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