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