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