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