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