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