1 #include "apphandler.h" 2 3 #include <arpa/inet.h> 4 #include <mapper.h> 5 #include <stdint.h> 6 #include <stdio.h> 7 #include <systemd/sd-bus.h> 8 9 #include "host-ipmid/ipmid-api.h" 10 11 #if __has_include(<filesystem>) 12 #include <filesystem> 13 #elif __has_include(<experimental/filesystem>) 14 #include <experimental/filesystem> 15 namespace std 16 { 17 // splice experimental::filesystem into std 18 namespace filesystem = std::experimental::filesystem; 19 } // namespace std 20 #else 21 #error filesystem not available 22 #endif 23 24 #include "app/channel.hpp" 25 #include "app/watchdog.hpp" 26 #include "ipmid.hpp" 27 #include "nlohmann/json.hpp" 28 #include "transporthandler.hpp" 29 #include "types.hpp" 30 #include "utils.hpp" 31 32 #include <array> 33 #include <cstddef> 34 #include <fstream> 35 #include <phosphor-logging/elog-errors.hpp> 36 #include <phosphor-logging/log.hpp> 37 #include <string> 38 #include <vector> 39 #include <xyz/openbmc_project/Common/error.hpp> 40 #include <xyz/openbmc_project/Software/Activation/server.hpp> 41 #include <xyz/openbmc_project/Software/Version/server.hpp> 42 43 extern sd_bus* bus; 44 45 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc"; 46 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; 47 constexpr auto bmc_guid_property = "UUID"; 48 constexpr auto bmc_guid_len = 16; 49 50 static constexpr auto redundancyIntf = 51 "xyz.openbmc_project.Software.RedundancyPriority"; 52 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; 53 static constexpr auto activationIntf = 54 "xyz.openbmc_project.Software.Activation"; 55 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 56 57 void register_netfn_app_functions() __attribute__((constructor)); 58 59 using namespace phosphor::logging; 60 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 61 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; 62 using Activation = 63 sdbusplus::xyz::openbmc_project::Software::server::Activation; 64 namespace fs = std::filesystem; 65 66 // Offset in get device id command. 67 typedef struct 68 { 69 uint8_t id; 70 uint8_t revision; 71 uint8_t fw[2]; 72 uint8_t ipmi_ver; 73 uint8_t addn_dev_support; 74 uint8_t manuf_id[3]; 75 uint8_t prod_id[2]; 76 uint8_t aux[4]; 77 } __attribute__((packed)) ipmi_device_id_t; 78 79 /** 80 * @brief Returns the Version info from primary s/w object 81 * 82 * Get the Version info from the active s/w object which is having high 83 * "Priority" value(a smaller number is a higher priority) and "Purpose" 84 * is "BMC" from the list of all s/w objects those are implementing 85 * RedundancyPriority interface from the given softwareRoot path. 86 * 87 * @return On success returns the Version info from primary s/w object. 88 * 89 */ 90 std::string getActiveSoftwareVersionInfo() 91 { 92 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 93 94 std::string revision{}; 95 auto objectTree = 96 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, ""); 97 if (objectTree.empty()) 98 { 99 log<level::ERR>("No Obj has implemented the s/w redundancy interface", 100 entry("INTERFACE=%s", redundancyIntf)); 101 elog<InternalFailure>(); 102 } 103 104 auto objectFound = false; 105 for (auto& softObject : objectTree) 106 { 107 auto service = ipmi::getService(bus, redundancyIntf, softObject.first); 108 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot); 109 110 auto minPriority = 0xFF; 111 for (const auto& objIter : objValueTree) 112 { 113 try 114 { 115 auto& intfMap = objIter.second; 116 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 117 auto& versionProps = intfMap.at(versionIntf); 118 auto& activationProps = intfMap.at(activationIntf); 119 auto priority = 120 redundancyPriorityProps.at("Priority").get<uint8_t>(); 121 auto purpose = versionProps.at("Purpose").get<std::string>(); 122 auto activation = 123 activationProps.at("Activation").get<std::string>(); 124 auto version = versionProps.at("Version").get<std::string>(); 125 if ((Version::convertVersionPurposeFromString(purpose) == 126 Version::VersionPurpose::BMC) && 127 (Activation::convertActivationsFromString(activation) == 128 Activation::Activations::Active)) 129 { 130 if (priority < minPriority) 131 { 132 minPriority = priority; 133 objectFound = true; 134 revision = std::move(version); 135 } 136 } 137 } 138 catch (const std::exception& e) 139 { 140 log<level::ERR>(e.what()); 141 } 142 } 143 } 144 145 if (!objectFound) 146 { 147 log<level::ERR>("Could not found an BMC software Object"); 148 elog<InternalFailure>(); 149 } 150 151 return revision; 152 } 153 154 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 155 ipmi_request_t request, 156 ipmi_response_t response, 157 ipmi_data_len_t data_len, 158 ipmi_context_t context) 159 { 160 ipmi_ret_t rc = IPMI_CC_OK; 161 *data_len = 0; 162 163 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n"); 164 return rc; 165 } 166 167 typedef struct 168 { 169 char major; 170 char minor; 171 uint16_t d[2]; 172 } rev_t; 173 174 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */ 175 /* return -1 if not in those formats, this routine knows how to parse */ 176 /* version = v0.6-19-gf363f61-dirty */ 177 /* ^ ^ ^^ ^ */ 178 /* | | |----------|-- additional details */ 179 /* | |---------------- Minor */ 180 /* |------------------ Major */ 181 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */ 182 /* ^ ^ ^^ ^ */ 183 /* | | |--|---------- additional details */ 184 /* | |---------------- Minor */ 185 /* |------------------ Major */ 186 /* Additional details : If the option group exists it will force Auxiliary */ 187 /* Firmware Revision Information 4th byte to 1 indicating the build was */ 188 /* derived with additional edits */ 189 int convert_version(const char* p, rev_t* rev) 190 { 191 std::string s(p); 192 std::string token; 193 uint16_t commits; 194 195 auto location = s.find_first_of('v'); 196 if (location != std::string::npos) 197 { 198 s = s.substr(location + 1); 199 } 200 201 if (!s.empty()) 202 { 203 location = s.find_first_of("."); 204 if (location != std::string::npos) 205 { 206 rev->major = 207 static_cast<char>(std::stoi(s.substr(0, location), 0, 16)); 208 token = s.substr(location + 1); 209 } 210 211 if (!token.empty()) 212 { 213 location = token.find_first_of(".-"); 214 if (location != std::string::npos) 215 { 216 rev->minor = static_cast<char>( 217 std::stoi(token.substr(0, location), 0, 16)); 218 token = token.substr(location + 1); 219 } 220 } 221 222 // Capture the number of commits on top of the minor tag. 223 // I'm using BE format like the ipmi spec asked for 224 location = token.find_first_of(".-"); 225 if (!token.empty()) 226 { 227 commits = std::stoi(token.substr(0, location), 0, 16); 228 rev->d[0] = (commits >> 8) | (commits << 8); 229 230 // commit number we skip 231 location = token.find_first_of(".-"); 232 if (location != std::string::npos) 233 { 234 token = token.substr(location + 1); 235 } 236 } 237 else 238 { 239 rev->d[0] = 0; 240 } 241 242 if (location != std::string::npos) 243 { 244 token = token.substr(location + 1); 245 } 246 247 // Any value of the optional parameter forces it to 1 248 location = token.find_first_of(".-"); 249 if (location != std::string::npos) 250 { 251 token = token.substr(location + 1); 252 } 253 commits = (!token.empty()) ? 1 : 0; 254 255 // We do this operation to get this displayed in least significant bytes 256 // of ipmitool device id command. 257 rev->d[1] = (commits >> 8) | (commits << 8); 258 } 259 260 return 0; 261 } 262 263 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 264 ipmi_request_t request, 265 ipmi_response_t response, 266 ipmi_data_len_t data_len, 267 ipmi_context_t context) 268 { 269 ipmi_ret_t rc = IPMI_CC_OK; 270 int r = -1; 271 rev_t rev = {0}; 272 static ipmi_device_id_t dev_id{}; 273 static bool dev_id_initialized = false; 274 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 275 276 // Data length 277 *data_len = sizeof(dev_id); 278 279 if (!dev_id_initialized) 280 { 281 try 282 { 283 auto version = getActiveSoftwareVersionInfo(); 284 r = convert_version(version.c_str(), &rev); 285 } 286 catch (const std::exception& e) 287 { 288 log<level::ERR>(e.what()); 289 } 290 291 if (r >= 0) 292 { 293 // bit7 identifies if the device is available 294 // 0=normal operation 295 // 1=device firmware, SDR update, 296 // or self-initialization in progress. 297 // our SDR is normal working condition, so mask: 298 dev_id.fw[0] = 0x7F & rev.major; 299 300 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 301 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 302 memcpy(&dev_id.aux, rev.d, 4); 303 } 304 305 // IPMI Spec version 2.0 306 dev_id.ipmi_ver = 2; 307 308 std::ifstream dev_id_file(filename); 309 if (dev_id_file.is_open()) 310 { 311 auto data = nlohmann::json::parse(dev_id_file, nullptr, false); 312 if (!data.is_discarded()) 313 { 314 dev_id.id = data.value("id", 0); 315 dev_id.revision = data.value("revision", 0); 316 dev_id.addn_dev_support = data.value("addn_dev_support", 0); 317 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16; 318 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8; 319 dev_id.manuf_id[0] = data.value("manuf_id", 0); 320 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8; 321 dev_id.prod_id[0] = data.value("prod_id", 0); 322 dev_id.aux[3] = data.value("aux", 0); 323 dev_id.aux[2] = data.value("aux", 0) >> 8; 324 dev_id.aux[1] = data.value("aux", 0) >> 16; 325 dev_id.aux[0] = data.value("aux", 0) >> 24; 326 327 // Don't read the file every time if successful 328 dev_id_initialized = true; 329 } 330 else 331 { 332 log<level::ERR>("Device ID JSON parser failure"); 333 rc = IPMI_CC_UNSPECIFIED_ERROR; 334 } 335 } 336 else 337 { 338 log<level::ERR>("Device ID file not found"); 339 rc = IPMI_CC_UNSPECIFIED_ERROR; 340 } 341 } 342 343 // Pack the actual response 344 memcpy(response, &dev_id, *data_len); 345 346 return rc; 347 } 348 349 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 350 ipmi_request_t request, 351 ipmi_response_t response, 352 ipmi_data_len_t data_len, 353 ipmi_context_t context) 354 { 355 ipmi_ret_t rc = IPMI_CC_OK; 356 357 // Byte 2: 358 // 55h - No error. 359 // 56h - Self Test function not implemented in this controller. 360 // 57h - Corrupted or inaccesssible data or devices. 361 // 58h - Fatal hardware error. 362 // FFh - reserved. 363 // all other: Device-specific 'internal failure'. 364 // Byte 3: 365 // For byte 2 = 55h, 56h, FFh: 00h 366 // For byte 2 = 58h, all other: Device-specific 367 // For byte 2 = 57h: self-test error bitfield. 368 // Note: returning 57h does not imply that all test were run. 369 // [7] 1b = Cannot access SEL device. 370 // [6] 1b = Cannot access SDR Repository. 371 // [5] 1b = Cannot access BMC FRU device. 372 // [4] 1b = IPMB signal lines do not respond. 373 // [3] 1b = SDR Repository empty. 374 // [2] 1b = Internal Use Area of BMC FRU corrupted. 375 // [1] 1b = controller update 'boot block' firmware corrupted. 376 // [0] 1b = controller operational firmware corrupted. 377 378 char selftestresults[2] = {0}; 379 380 *data_len = 2; 381 382 selftestresults[0] = 0x56; 383 selftestresults[1] = 0; 384 385 memcpy(response, selftestresults, *data_len); 386 387 return rc; 388 } 389 390 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 391 ipmi_request_t request, 392 ipmi_response_t response, 393 ipmi_data_len_t data_len, 394 ipmi_context_t context) 395 { 396 const char* objname = "/org/openbmc/control/chassis0"; 397 const char* iface = "org.freedesktop.DBus.Properties"; 398 const char* chassis_iface = "org.openbmc.control.Chassis"; 399 sd_bus_message* reply = NULL; 400 sd_bus_error error = SD_BUS_ERROR_NULL; 401 int r = 0; 402 char* uuid = NULL; 403 char* busname = NULL; 404 405 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 406 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 407 // order 408 // Ex: 0x2332fc2c40e66298e511f2782395a361 409 410 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec 411 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response 412 // Point resp end of array to save in reverse order 413 int resp_loc = resp_size - 1; 414 int i = 0; 415 char* tokptr = NULL; 416 char* id_octet = NULL; 417 418 // Status code. 419 ipmi_ret_t rc = IPMI_CC_OK; 420 *data_len = 0; 421 422 // Call Get properties method with the interface and property name 423 r = mapper_get_service(bus, objname, &busname); 424 if (r < 0) 425 { 426 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname), 427 entry("ERRNO=0x%X", -r)); 428 goto finish; 429 } 430 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply, 431 "ss", chassis_iface, "uuid"); 432 if (r < 0) 433 { 434 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r)); 435 rc = IPMI_CC_UNSPECIFIED_ERROR; 436 goto finish; 437 } 438 439 r = sd_bus_message_read(reply, "v", "s", &uuid); 440 if (r < 0 || uuid == NULL) 441 { 442 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r)); 443 rc = IPMI_CC_RESPONSE_ERROR; 444 goto finish; 445 } 446 447 // Traverse the UUID 448 // Get the UUID octects separated by dash 449 id_octet = strtok_r(uuid, "-", &tokptr); 450 451 if (id_octet == NULL) 452 { 453 // Error 454 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid)); 455 rc = IPMI_CC_RESPONSE_ERROR; 456 goto finish; 457 } 458 459 while (id_octet != NULL) 460 { 461 // Calculate the octet string size since it varies 462 // Divide it by 2 for the array size since 1 byte is built from 2 chars 463 int tmp_size = strlen(id_octet) / 2; 464 465 for (i = 0; i < tmp_size; i++) 466 { 467 // Holder of the 2 chars that will become a byte 468 char tmp_array[3] = {0}; 469 strncpy(tmp_array, id_octet, 2); // 2 chars at a time 470 471 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte 472 // Copy end to first 473 memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1); 474 resp_loc--; 475 id_octet += 2; // Finished with the 2 chars, advance 476 } 477 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet 478 } 479 480 // Data length 481 *data_len = resp_size; 482 483 // Pack the actual response 484 memcpy(response, &resp_uuid, *data_len); 485 486 finish: 487 sd_bus_error_free(&error); 488 reply = sd_bus_message_unref(reply); 489 free(busname); 490 491 return rc; 492 } 493 494 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 495 ipmi_request_t request, 496 ipmi_response_t response, 497 ipmi_data_len_t data_len, 498 ipmi_context_t context) 499 { 500 501 // Status code. 502 ipmi_ret_t rc = IPMI_CC_OK; 503 504 // Per IPMI 2.0 spec, the input and output buffer size must be the max 505 // buffer size minus one byte to allocate space for the length byte. 506 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A, 507 0x01}; 508 509 // Data length 510 *data_len = sizeof(str); 511 512 // Pack the actual response 513 memcpy(response, &str, *data_len); 514 515 return rc; 516 } 517 518 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 519 ipmi_request_t request, 520 ipmi_response_t response, 521 ipmi_data_len_t data_len, 522 ipmi_context_t context) 523 { 524 // Status code. 525 ipmi_ret_t rc = IPMI_CC_INVALID; 526 527 *data_len = strlen("THIS IS WILDCARD"); 528 529 // Now pack actual response 530 memcpy(response, "THIS IS WILDCARD", *data_len); 531 532 return rc; 533 } 534 535 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 536 ipmi_request_t request, 537 ipmi_response_t response, 538 ipmi_data_len_t data_len, 539 ipmi_context_t context) 540 541 { 542 ipmi_ret_t rc = IPMI_CC_OK; 543 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 544 545 try 546 { 547 // Get the Inventory object implementing BMC interface 548 ipmi::DbusObjectInfo bmcObject = 549 ipmi::getDbusObject(bus, bmc_interface); 550 551 // Read UUID property value from bmcObject 552 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 553 auto variant = 554 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, 555 bmc_guid_interface, bmc_guid_property); 556 std::string guidProp = variant.get<std::string>(); 557 558 // Erase "-" characters from the property value 559 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'), 560 guidProp.end()); 561 562 auto guidPropLen = guidProp.length(); 563 // Validate UUID data 564 // Divide by 2 as 1 byte is built from 2 chars 565 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len)) 566 567 { 568 log<level::ERR>("Invalid UUID property value", 569 entry("UUID_LENGTH=%d", guidPropLen)); 570 return IPMI_CC_RESPONSE_ERROR; 571 } 572 573 // Convert data in RFC4122(MSB) format to LSB format 574 // Get 2 characters at a time as 1 byte is built from 2 chars and 575 // convert to hex byte 576 // TODO: Data printed for GUID command is not as per the 577 // GUID format defined in IPMI specification 2.0 section 20.8 578 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/ 579 uint8_t respGuid[bmc_guid_len]; 580 for (size_t i = 0, respLoc = (bmc_guid_len - 1); 581 i < guidPropLen && respLoc >= 0; i += 2, respLoc--) 582 { 583 auto value = static_cast<uint8_t>( 584 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16)); 585 respGuid[respLoc] = value; 586 } 587 588 *data_len = bmc_guid_len; 589 memcpy(response, &respGuid, bmc_guid_len); 590 } 591 catch (const InternalFailure& e) 592 { 593 log<level::ERR>("Failed in reading BMC UUID property", 594 entry("INTERFACE=%s", bmc_interface), 595 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface), 596 entry("PROPERTY=%s", bmc_guid_property)); 597 return IPMI_CC_UNSPECIFIED_ERROR; 598 } 599 return rc; 600 } 601 602 void register_netfn_app_functions() 603 { 604 // <Get BT Interface Capabilities> 605 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, 606 ipmi_app_get_bt_capabilities, PRIVILEGE_USER); 607 608 // <Wildcard Command> 609 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL, 610 ipmi_app_wildcard_handler, PRIVILEGE_USER); 611 612 // <Reset Watchdog Timer> 613 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL, 614 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR); 615 616 // <Set Watchdog Timer> 617 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL, 618 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR); 619 620 // <Get Watchdog Timer> 621 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL, 622 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR); 623 624 // <Get Device ID> 625 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL, 626 ipmi_app_get_device_id, PRIVILEGE_USER); 627 628 // <Get Self Test Results> 629 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL, 630 ipmi_app_get_self_test_results, PRIVILEGE_USER); 631 632 // <Get Device GUID> 633 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL, 634 ipmi_app_get_device_guid, PRIVILEGE_USER); 635 636 // <Set ACPI Power State> 637 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, 638 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN); 639 640 // <Get Channel Access> 641 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, 642 ipmi_get_channel_access, PRIVILEGE_USER); 643 644 // <Get Channel Info Command> 645 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, 646 ipmi_app_channel_info, PRIVILEGE_USER); 647 648 // <Get System GUID Command> 649 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, 650 ipmi_app_get_sys_guid, PRIVILEGE_USER); 651 652 // <Get Channel Cipher Suites Command> 653 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL, 654 getChannelCipherSuites, PRIVILEGE_CALLBACK); 655 return; 656 } 657