1 #include "apphandler.hpp" 2 3 #include "app/channel.hpp" 4 #include "app/watchdog.hpp" 5 #include "ipmid.hpp" 6 #include "sys_info_param.hpp" 7 #include "transporthandler.hpp" 8 #include "types.hpp" 9 #include "utils.hpp" 10 11 #include <arpa/inet.h> 12 #include <host-ipmid/ipmid-api.h> 13 #include <limits.h> 14 #include <mapper.h> 15 #include <systemd/sd-bus.h> 16 #include <unistd.h> 17 18 #include <algorithm> 19 #include <array> 20 #include <cstddef> 21 #include <fstream> 22 #include <memory> 23 #include <nlohmann/json.hpp> 24 #include <phosphor-logging/elog-errors.hpp> 25 #include <phosphor-logging/log.hpp> 26 #include <string> 27 #include <tuple> 28 #include <vector> 29 #include <xyz/openbmc_project/Common/error.hpp> 30 #include <xyz/openbmc_project/Software/Activation/server.hpp> 31 #include <xyz/openbmc_project/Software/Version/server.hpp> 32 #include <xyz/openbmc_project/State/BMC/server.hpp> 33 34 #if __has_include(<filesystem>) 35 #include <filesystem> 36 #elif __has_include(<experimental/filesystem>) 37 #include <experimental/filesystem> 38 namespace std 39 { 40 // splice experimental::filesystem into std 41 namespace filesystem = std::experimental::filesystem; 42 } // namespace std 43 #else 44 #error filesystem not available 45 #endif 46 47 extern sd_bus* bus; 48 49 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; 50 constexpr auto bmc_state_property = "CurrentBMCState"; 51 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc"; 52 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; 53 constexpr auto bmc_guid_property = "UUID"; 54 constexpr auto bmc_guid_len = 16; 55 56 static constexpr auto redundancyIntf = 57 "xyz.openbmc_project.Software.RedundancyPriority"; 58 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; 59 static constexpr auto activationIntf = 60 "xyz.openbmc_project.Software.Activation"; 61 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 62 63 void register_netfn_app_functions() __attribute__((constructor)); 64 65 using namespace phosphor::logging; 66 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 67 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; 68 using Activation = 69 sdbusplus::xyz::openbmc_project::Software::server::Activation; 70 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC; 71 namespace fs = std::filesystem; 72 73 // Offset in get device id command. 74 typedef struct 75 { 76 uint8_t id; 77 uint8_t revision; 78 uint8_t fw[2]; 79 uint8_t ipmi_ver; 80 uint8_t addn_dev_support; 81 uint8_t manuf_id[3]; 82 uint8_t prod_id[2]; 83 uint8_t aux[4]; 84 } __attribute__((packed)) ipmi_device_id_t; 85 86 /** 87 * @brief Returns the Version info from primary s/w object 88 * 89 * Get the Version info from the active s/w object which is having high 90 * "Priority" value(a smaller number is a higher priority) and "Purpose" 91 * is "BMC" from the list of all s/w objects those are implementing 92 * RedundancyPriority interface from the given softwareRoot path. 93 * 94 * @return On success returns the Version info from primary s/w object. 95 * 96 */ 97 std::string getActiveSoftwareVersionInfo() 98 { 99 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 100 101 std::string revision{}; 102 auto objectTree = 103 ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, ""); 104 if (objectTree.empty()) 105 { 106 log<level::ERR>("No Obj has implemented the s/w redundancy interface", 107 entry("INTERFACE=%s", redundancyIntf)); 108 elog<InternalFailure>(); 109 } 110 111 auto objectFound = false; 112 for (auto& softObject : objectTree) 113 { 114 auto service = ipmi::getService(bus, redundancyIntf, softObject.first); 115 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot); 116 117 auto minPriority = 0xFF; 118 for (const auto& objIter : objValueTree) 119 { 120 try 121 { 122 auto& intfMap = objIter.second; 123 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 124 auto& versionProps = intfMap.at(versionIntf); 125 auto& activationProps = intfMap.at(activationIntf); 126 auto priority = 127 redundancyPriorityProps.at("Priority").get<uint8_t>(); 128 auto purpose = versionProps.at("Purpose").get<std::string>(); 129 auto activation = 130 activationProps.at("Activation").get<std::string>(); 131 auto version = versionProps.at("Version").get<std::string>(); 132 if ((Version::convertVersionPurposeFromString(purpose) == 133 Version::VersionPurpose::BMC) && 134 (Activation::convertActivationsFromString(activation) == 135 Activation::Activations::Active)) 136 { 137 if (priority < minPriority) 138 { 139 minPriority = priority; 140 objectFound = true; 141 revision = std::move(version); 142 } 143 } 144 } 145 catch (const std::exception& e) 146 { 147 log<level::ERR>(e.what()); 148 } 149 } 150 } 151 152 if (!objectFound) 153 { 154 log<level::ERR>("Could not found an BMC software Object"); 155 elog<InternalFailure>(); 156 } 157 158 return revision; 159 } 160 161 bool getCurrentBmcState() 162 { 163 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 164 165 // Get the Inventory object implementing the BMC interface 166 ipmi::DbusObjectInfo bmcObject = 167 ipmi::getDbusObject(bus, bmc_state_interface); 168 auto variant = 169 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, 170 bmc_state_interface, bmc_state_property); 171 172 return variant.is<std::string>() && 173 BMC::convertBMCStateFromString(variant.get<std::string>()) == 174 BMC::BMCState::Ready; 175 } 176 177 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 178 ipmi_request_t request, 179 ipmi_response_t response, 180 ipmi_data_len_t data_len, 181 ipmi_context_t context) 182 { 183 ipmi_ret_t rc = IPMI_CC_OK; 184 *data_len = 0; 185 186 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n"); 187 return rc; 188 } 189 190 typedef struct 191 { 192 char major; 193 char minor; 194 uint16_t d[2]; 195 } rev_t; 196 197 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */ 198 /* return -1 if not in those formats, this routine knows how to parse */ 199 /* version = v0.6-19-gf363f61-dirty */ 200 /* ^ ^ ^^ ^ */ 201 /* | | |----------|-- additional details */ 202 /* | |---------------- Minor */ 203 /* |------------------ Major */ 204 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */ 205 /* ^ ^ ^^ ^ */ 206 /* | | |--|---------- additional details */ 207 /* | |---------------- Minor */ 208 /* |------------------ Major */ 209 /* Additional details : If the option group exists it will force Auxiliary */ 210 /* Firmware Revision Information 4th byte to 1 indicating the build was */ 211 /* derived with additional edits */ 212 int convert_version(const char* p, rev_t* rev) 213 { 214 std::string s(p); 215 std::string token; 216 uint16_t commits; 217 218 auto location = s.find_first_of('v'); 219 if (location != std::string::npos) 220 { 221 s = s.substr(location + 1); 222 } 223 224 if (!s.empty()) 225 { 226 location = s.find_first_of("."); 227 if (location != std::string::npos) 228 { 229 rev->major = 230 static_cast<char>(std::stoi(s.substr(0, location), 0, 16)); 231 token = s.substr(location + 1); 232 } 233 234 if (!token.empty()) 235 { 236 location = token.find_first_of(".-"); 237 if (location != std::string::npos) 238 { 239 rev->minor = static_cast<char>( 240 std::stoi(token.substr(0, location), 0, 16)); 241 token = token.substr(location + 1); 242 } 243 } 244 245 // Capture the number of commits on top of the minor tag. 246 // I'm using BE format like the ipmi spec asked for 247 location = token.find_first_of(".-"); 248 if (!token.empty()) 249 { 250 commits = std::stoi(token.substr(0, location), 0, 16); 251 rev->d[0] = (commits >> 8) | (commits << 8); 252 253 // commit number we skip 254 location = token.find_first_of(".-"); 255 if (location != std::string::npos) 256 { 257 token = token.substr(location + 1); 258 } 259 } 260 else 261 { 262 rev->d[0] = 0; 263 } 264 265 if (location != std::string::npos) 266 { 267 token = token.substr(location + 1); 268 } 269 270 // Any value of the optional parameter forces it to 1 271 location = token.find_first_of(".-"); 272 if (location != std::string::npos) 273 { 274 token = token.substr(location + 1); 275 } 276 commits = (!token.empty()) ? 1 : 0; 277 278 // We do this operation to get this displayed in least significant bytes 279 // of ipmitool device id command. 280 rev->d[1] = (commits >> 8) | (commits << 8); 281 } 282 283 return 0; 284 } 285 286 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 287 ipmi_request_t request, 288 ipmi_response_t response, 289 ipmi_data_len_t data_len, 290 ipmi_context_t context) 291 { 292 ipmi_ret_t rc = IPMI_CC_OK; 293 int r = -1; 294 rev_t rev = {0}; 295 static ipmi_device_id_t dev_id{}; 296 static bool dev_id_initialized = false; 297 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 298 constexpr auto ipmiDevIdStateShift = 7; 299 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); 300 301 // Data length 302 *data_len = sizeof(dev_id); 303 304 if (!dev_id_initialized) 305 { 306 try 307 { 308 auto version = getActiveSoftwareVersionInfo(); 309 r = convert_version(version.c_str(), &rev); 310 } 311 catch (const std::exception& e) 312 { 313 log<level::ERR>(e.what()); 314 } 315 316 if (r >= 0) 317 { 318 // bit7 identifies if the device is available 319 // 0=normal operation 320 // 1=device firmware, SDR update, 321 // or self-initialization in progress. 322 // The availability may change in run time, so mask here 323 // and initialize later. 324 dev_id.fw[0] = rev.major & ipmiDevIdFw1Mask; 325 326 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 327 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 328 std::memcpy(&dev_id.aux, rev.d, 4); 329 } 330 331 // IPMI Spec version 2.0 332 dev_id.ipmi_ver = 2; 333 334 std::ifstream dev_id_file(filename); 335 if (dev_id_file.is_open()) 336 { 337 auto data = nlohmann::json::parse(dev_id_file, nullptr, false); 338 if (!data.is_discarded()) 339 { 340 dev_id.id = data.value("id", 0); 341 dev_id.revision = data.value("revision", 0); 342 dev_id.addn_dev_support = data.value("addn_dev_support", 0); 343 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16; 344 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8; 345 dev_id.manuf_id[0] = data.value("manuf_id", 0); 346 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8; 347 dev_id.prod_id[0] = data.value("prod_id", 0); 348 dev_id.aux[3] = data.value("aux", 0); 349 dev_id.aux[2] = data.value("aux", 0) >> 8; 350 dev_id.aux[1] = data.value("aux", 0) >> 16; 351 dev_id.aux[0] = data.value("aux", 0) >> 24; 352 353 // Don't read the file every time if successful 354 dev_id_initialized = true; 355 } 356 else 357 { 358 log<level::ERR>("Device ID JSON parser failure"); 359 rc = IPMI_CC_UNSPECIFIED_ERROR; 360 } 361 } 362 else 363 { 364 log<level::ERR>("Device ID file not found"); 365 rc = IPMI_CC_UNSPECIFIED_ERROR; 366 } 367 } 368 369 // Set availability to the actual current BMC state 370 dev_id.fw[0] &= ipmiDevIdFw1Mask; 371 if (!getCurrentBmcState()) 372 { 373 dev_id.fw[0] |= (1 << ipmiDevIdStateShift); 374 } 375 376 // Pack the actual response 377 std::memcpy(response, &dev_id, *data_len); 378 379 return rc; 380 } 381 382 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 383 ipmi_request_t request, 384 ipmi_response_t response, 385 ipmi_data_len_t data_len, 386 ipmi_context_t context) 387 { 388 ipmi_ret_t rc = IPMI_CC_OK; 389 390 // Byte 2: 391 // 55h - No error. 392 // 56h - Self Test function not implemented in this controller. 393 // 57h - Corrupted or inaccesssible data or devices. 394 // 58h - Fatal hardware error. 395 // FFh - reserved. 396 // all other: Device-specific 'internal failure'. 397 // Byte 3: 398 // For byte 2 = 55h, 56h, FFh: 00h 399 // For byte 2 = 58h, all other: Device-specific 400 // For byte 2 = 57h: self-test error bitfield. 401 // Note: returning 57h does not imply that all test were run. 402 // [7] 1b = Cannot access SEL device. 403 // [6] 1b = Cannot access SDR Repository. 404 // [5] 1b = Cannot access BMC FRU device. 405 // [4] 1b = IPMB signal lines do not respond. 406 // [3] 1b = SDR Repository empty. 407 // [2] 1b = Internal Use Area of BMC FRU corrupted. 408 // [1] 1b = controller update 'boot block' firmware corrupted. 409 // [0] 1b = controller operational firmware corrupted. 410 411 char selftestresults[2] = {0}; 412 413 *data_len = 2; 414 415 selftestresults[0] = 0x56; 416 selftestresults[1] = 0; 417 418 std::memcpy(response, selftestresults, *data_len); 419 420 return rc; 421 } 422 423 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 424 ipmi_request_t request, 425 ipmi_response_t response, 426 ipmi_data_len_t data_len, 427 ipmi_context_t context) 428 { 429 const char* objname = "/org/openbmc/control/chassis0"; 430 const char* iface = "org.freedesktop.DBus.Properties"; 431 const char* chassis_iface = "org.openbmc.control.Chassis"; 432 sd_bus_message* reply = NULL; 433 sd_bus_error error = SD_BUS_ERROR_NULL; 434 int r = 0; 435 char* uuid = NULL; 436 char* busname = NULL; 437 438 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 439 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 440 // order 441 // Ex: 0x2332fc2c40e66298e511f2782395a361 442 443 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec 444 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response 445 // Point resp end of array to save in reverse order 446 int resp_loc = resp_size - 1; 447 int i = 0; 448 char* tokptr = NULL; 449 char* id_octet = NULL; 450 size_t total_uuid_size = 0; 451 // 1 byte of resp is built from 2 chars of uuid. 452 constexpr size_t max_uuid_size = 2 * resp_size; 453 454 // Status code. 455 ipmi_ret_t rc = IPMI_CC_OK; 456 *data_len = 0; 457 458 // Call Get properties method with the interface and property name 459 r = mapper_get_service(bus, objname, &busname); 460 if (r < 0) 461 { 462 log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname), 463 entry("ERRNO=0x%X", -r)); 464 goto finish; 465 } 466 r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply, 467 "ss", chassis_iface, "uuid"); 468 if (r < 0) 469 { 470 log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r)); 471 rc = IPMI_CC_UNSPECIFIED_ERROR; 472 goto finish; 473 } 474 475 r = sd_bus_message_read(reply, "v", "s", &uuid); 476 if (r < 0 || uuid == NULL) 477 { 478 log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r)); 479 rc = IPMI_CC_RESPONSE_ERROR; 480 goto finish; 481 } 482 483 // Traverse the UUID 484 // Get the UUID octects separated by dash 485 id_octet = strtok_r(uuid, "-", &tokptr); 486 487 if (id_octet == NULL) 488 { 489 // Error 490 log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid)); 491 rc = IPMI_CC_RESPONSE_ERROR; 492 goto finish; 493 } 494 495 while (id_octet != NULL) 496 { 497 // Calculate the octet string size since it varies 498 // Divide it by 2 for the array size since 1 byte is built from 2 chars 499 int tmp_size = strlen(id_octet) / 2; 500 501 // Check if total UUID size has been exceeded 502 if ((total_uuid_size += strlen(id_octet)) > max_uuid_size) 503 { 504 // Error - UUID too long to store 505 log<level::ERR>("UUID too long", entry("UUID=%s", uuid)); 506 rc = IPMI_CC_RESPONSE_ERROR; 507 goto finish; 508 } 509 510 for (i = 0; i < tmp_size; i++) 511 { 512 // Holder of the 2 chars that will become a byte 513 char tmp_array[3] = {0}; 514 strncpy(tmp_array, id_octet, 2); // 2 chars at a time 515 516 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte 517 // Copy end to first 518 std::memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1); 519 resp_loc--; 520 id_octet += 2; // Finished with the 2 chars, advance 521 } 522 id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet 523 } 524 525 // Data length 526 *data_len = resp_size; 527 528 // Pack the actual response 529 std::memcpy(response, &resp_uuid, *data_len); 530 531 finish: 532 sd_bus_error_free(&error); 533 reply = sd_bus_message_unref(reply); 534 free(busname); 535 536 return rc; 537 } 538 539 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 540 ipmi_request_t request, 541 ipmi_response_t response, 542 ipmi_data_len_t data_len, 543 ipmi_context_t context) 544 { 545 546 // Status code. 547 ipmi_ret_t rc = IPMI_CC_OK; 548 549 // Per IPMI 2.0 spec, the input and output buffer size must be the max 550 // buffer size minus one byte to allocate space for the length byte. 551 uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A, 552 0x01}; 553 554 // Data length 555 *data_len = sizeof(str); 556 557 // Pack the actual response 558 std::memcpy(response, &str, *data_len); 559 560 return rc; 561 } 562 563 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 564 ipmi_request_t request, 565 ipmi_response_t response, 566 ipmi_data_len_t data_len, 567 ipmi_context_t context) 568 { 569 // Status code. 570 ipmi_ret_t rc = IPMI_CC_INVALID; 571 572 *data_len = strlen("THIS IS WILDCARD"); 573 574 // Now pack actual response 575 std::memcpy(response, "THIS IS WILDCARD", *data_len); 576 577 return rc; 578 } 579 580 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 581 ipmi_request_t request, 582 ipmi_response_t response, 583 ipmi_data_len_t data_len, 584 ipmi_context_t context) 585 586 { 587 ipmi_ret_t rc = IPMI_CC_OK; 588 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 589 590 try 591 { 592 // Get the Inventory object implementing BMC interface 593 ipmi::DbusObjectInfo bmcObject = 594 ipmi::getDbusObject(bus, bmc_interface); 595 596 // Read UUID property value from bmcObject 597 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 598 auto variant = 599 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, 600 bmc_guid_interface, bmc_guid_property); 601 std::string guidProp = variant.get<std::string>(); 602 603 // Erase "-" characters from the property value 604 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'), 605 guidProp.end()); 606 607 auto guidPropLen = guidProp.length(); 608 // Validate UUID data 609 // Divide by 2 as 1 byte is built from 2 chars 610 if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len)) 611 612 { 613 log<level::ERR>("Invalid UUID property value", 614 entry("UUID_LENGTH=%d", guidPropLen)); 615 return IPMI_CC_RESPONSE_ERROR; 616 } 617 618 // Convert data in RFC4122(MSB) format to LSB format 619 // Get 2 characters at a time as 1 byte is built from 2 chars and 620 // convert to hex byte 621 // TODO: Data printed for GUID command is not as per the 622 // GUID format defined in IPMI specification 2.0 section 20.8 623 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/ 624 uint8_t respGuid[bmc_guid_len]; 625 for (size_t i = 0, respLoc = (bmc_guid_len - 1); 626 i < guidPropLen && respLoc >= 0; i += 2, respLoc--) 627 { 628 auto value = static_cast<uint8_t>( 629 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16)); 630 respGuid[respLoc] = value; 631 } 632 633 *data_len = bmc_guid_len; 634 std::memcpy(response, &respGuid, bmc_guid_len); 635 } 636 catch (const InternalFailure& e) 637 { 638 log<level::ERR>("Failed in reading BMC UUID property", 639 entry("INTERFACE=%s", bmc_interface), 640 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface), 641 entry("PROPERTY=%s", bmc_guid_property)); 642 return IPMI_CC_UNSPECIFIED_ERROR; 643 } 644 return rc; 645 } 646 647 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore; 648 649 static std::string sysInfoReadSystemName() 650 { 651 // Use the BMC hostname as the "System Name." 652 char hostname[HOST_NAME_MAX + 1] = {}; 653 if (gethostname(hostname, HOST_NAME_MAX) != 0) 654 { 655 perror("System info parameter: system name"); 656 } 657 return hostname; 658 } 659 660 struct IpmiSysInfoResp 661 { 662 uint8_t paramRevision; 663 uint8_t setSelector; 664 union 665 { 666 struct 667 { 668 uint8_t encoding; 669 uint8_t stringLen; 670 uint8_t stringData0[14]; 671 } __attribute__((packed)); 672 uint8_t stringDataN[16]; 673 uint8_t byteData; 674 }; 675 } __attribute__((packed)); 676 677 /** 678 * Split a string into (up to) 16-byte chunks as expected in response for get 679 * system info parameter. 680 * 681 * @param[in] fullString: Input string to be split 682 * @param[in] chunkIndex: Index of the chunk to be written out 683 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if 684 * chunk_index = 0 and 16-byte capacity otherwise 685 * @return the number of bytes written into the output buffer, or -EINVAL for 686 * invalid arguments. 687 */ 688 static int splitStringParam(const std::string& fullString, int chunkIndex, 689 uint8_t* chunk) 690 { 691 constexpr int maxChunk = 255; 692 constexpr int smallChunk = 14; 693 constexpr int chunkSize = 16; 694 if (chunkIndex > maxChunk || chunk == nullptr) 695 { 696 return -EINVAL; 697 } 698 try 699 { 700 std::string output; 701 if (chunkIndex == 0) 702 { 703 // Output must have 14 byte capacity. 704 output = fullString.substr(0, smallChunk); 705 } 706 else 707 { 708 // Output must have 16 byte capacity. 709 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize); 710 } 711 712 std::memcpy(chunk, output.c_str(), output.length()); 713 return output.length(); 714 } 715 catch (const std::out_of_range& e) 716 { 717 // The position was beyond the end. 718 return -EINVAL; 719 } 720 } 721 722 /** 723 * Packs the Get Sys Info Request Item into the response. 724 * 725 * @param[in] paramString - the parameter. 726 * @param[in] setSelector - the selector 727 * @param[in,out] resp - the System info response. 728 * @return The number of bytes packed or failure from splitStringParam(). 729 */ 730 static int packGetSysInfoResp(const std::string& paramString, 731 uint8_t setSelector, IpmiSysInfoResp* resp) 732 { 733 uint8_t* dataBuffer = resp->stringDataN; 734 resp->setSelector = setSelector; 735 if (resp->setSelector == 0) // First chunk has only 14 bytes. 736 { 737 resp->encoding = 0; 738 resp->stringLen = paramString.length(); 739 dataBuffer = resp->stringData0; 740 } 741 return splitStringParam(paramString, resp->setSelector, dataBuffer); 742 } 743 744 ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 745 ipmi_request_t request, 746 ipmi_response_t response, 747 ipmi_data_len_t dataLen, 748 ipmi_context_t context) 749 { 750 IpmiSysInfoResp resp = {}; 751 size_t respLen = 0; 752 uint8_t* const reqData = static_cast<uint8_t*>(request); 753 std::string paramString; 754 bool found; 755 std::tuple<bool, std::string> ret; 756 constexpr int minRequestSize = 4; 757 constexpr int paramSelector = 1; 758 constexpr uint8_t revisionOnly = 0x80; 759 const uint8_t paramRequested = reqData[paramSelector]; 760 int rc; 761 762 if (*dataLen < minRequestSize) 763 { 764 return IPMI_CC_REQ_DATA_LEN_INVALID; 765 } 766 767 *dataLen = 0; // default to 0. 768 769 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6) 770 resp.paramRevision = 0x11; 771 if (reqData[0] & revisionOnly) // Get parameter revision only 772 { 773 respLen = 1; 774 goto writeResponse; 775 } 776 777 // The "Set In Progress" parameter can be used for rollback of parameter 778 // data and is not implemented. 779 if (paramRequested == 0) 780 { 781 resp.byteData = 0; 782 respLen = 2; 783 goto writeResponse; 784 } 785 786 if (sysInfoParamStore == nullptr) 787 { 788 sysInfoParamStore = std::make_unique<SysInfoParamStore>(); 789 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, 790 sysInfoReadSystemName); 791 } 792 793 // Parameters other than Set In Progress are assumed to be strings. 794 ret = sysInfoParamStore->lookup(paramRequested); 795 found = std::get<0>(ret); 796 paramString = std::get<1>(ret); 797 if (!found) 798 { 799 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 800 } 801 // TODO: Cache each parameter across multiple calls, until the whole string 802 // has been read out. Otherwise, it's possible for a parameter to change 803 // between requests for its chunks, returning chunks incoherent with each 804 // other. For now, the parameter store is simply required to have only 805 // idempotent callbacks. 806 rc = packGetSysInfoResp(paramString, reqData[2], &resp); 807 if (rc == -EINVAL) 808 { 809 return IPMI_CC_RESPONSE_ERROR; 810 } 811 812 respLen = sizeof(resp); // Write entire string data chunk in response. 813 814 writeResponse: 815 std::memcpy(response, &resp, sizeof(resp)); 816 *dataLen = respLen; 817 return IPMI_CC_OK; 818 } 819 820 void register_netfn_app_functions() 821 { 822 // <Get BT Interface Capabilities> 823 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, 824 ipmi_app_get_bt_capabilities, PRIVILEGE_USER); 825 826 // <Wildcard Command> 827 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL, 828 ipmi_app_wildcard_handler, PRIVILEGE_USER); 829 830 // <Reset Watchdog Timer> 831 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL, 832 ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR); 833 834 // <Set Watchdog Timer> 835 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL, 836 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR); 837 838 // <Get Watchdog Timer> 839 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL, 840 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR); 841 842 // <Get Device ID> 843 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL, 844 ipmi_app_get_device_id, PRIVILEGE_USER); 845 846 // <Get Self Test Results> 847 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL, 848 ipmi_app_get_self_test_results, PRIVILEGE_USER); 849 850 // <Get Device GUID> 851 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL, 852 ipmi_app_get_device_guid, PRIVILEGE_USER); 853 854 // <Set ACPI Power State> 855 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, 856 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN); 857 858 // <Get Channel Access> 859 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, 860 ipmi_get_channel_access, PRIVILEGE_USER); 861 862 // <Get Channel Info Command> 863 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, 864 ipmi_app_channel_info, PRIVILEGE_USER); 865 866 // <Get System GUID Command> 867 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL, 868 ipmi_app_get_sys_guid, PRIVILEGE_USER); 869 870 // <Get Channel Cipher Suites Command> 871 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL, 872 getChannelCipherSuites, PRIVILEGE_CALLBACK); 873 874 // <Set Channel Access Command> 875 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL, 876 ipmi_set_channel_access, PRIVILEGE_ADMIN); 877 878 // <Get System Info Command> 879 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL, 880 ipmi_app_get_system_info, PRIVILEGE_USER); 881 return; 882 } 883