1 #include "config.h" 2 3 #include <arpa/inet.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <linux/i2c-dev.h> 7 #include <linux/i2c.h> 8 #include <sys/ioctl.h> 9 #include <sys/stat.h> 10 #include <sys/types.h> 11 #include <systemd/sd-bus.h> 12 #include <unistd.h> 13 14 #include <app/channel.hpp> 15 #include <app/watchdog.hpp> 16 #include <apphandler.hpp> 17 #include <ipmid/api.hpp> 18 #include <ipmid/sessiondef.hpp> 19 #include <ipmid/sessionhelper.hpp> 20 #include <ipmid/types.hpp> 21 #include <ipmid/utils.hpp> 22 #include <nlohmann/json.hpp> 23 #include <phosphor-logging/elog-errors.hpp> 24 #include <phosphor-logging/lg2.hpp> 25 #include <sdbusplus/message/types.hpp> 26 #include <sys_info_param.hpp> 27 #include <xyz/openbmc_project/Common/error.hpp> 28 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp> 29 #include <xyz/openbmc_project/Software/Activation/server.hpp> 30 #include <xyz/openbmc_project/Software/Version/server.hpp> 31 #include <xyz/openbmc_project/State/BMC/server.hpp> 32 33 #include <algorithm> 34 #include <array> 35 #include <charconv> 36 #include <cstddef> 37 #include <cstdint> 38 #include <filesystem> 39 #include <fstream> 40 #include <memory> 41 #include <regex> 42 #include <string> 43 #include <string_view> 44 #include <tuple> 45 #include <vector> 46 47 extern sd_bus* bus; 48 49 constexpr auto versionPurposeHostEnd = ".Host"; 50 51 static constexpr auto redundancyIntf = 52 "xyz.openbmc_project.Software.RedundancyPriority"; 53 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; 54 static constexpr auto activationIntf = 55 "xyz.openbmc_project.Software.Activation"; 56 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 57 58 void registerNetFnAppFunctions() __attribute__((constructor)); 59 60 using namespace phosphor::logging; 61 using namespace sdbusplus::error::xyz::openbmc_project::common; 62 using Version = sdbusplus::server::xyz::openbmc_project::software::Version; 63 using Activation = 64 sdbusplus::server::xyz::openbmc_project::software::Activation; 65 using BMCState = sdbusplus::server::xyz::openbmc_project::state::BMC; 66 namespace fs = std::filesystem; 67 68 #ifdef ENABLE_I2C_WHITELIST_CHECK 69 typedef struct 70 { 71 uint8_t busId; 72 uint8_t targetAddr; 73 uint8_t targetAddrMask; 74 std::vector<uint8_t> data; 75 std::vector<uint8_t> dataMask; 76 } i2cControllerWRAllowlist; 77 78 static std::vector<i2cControllerWRAllowlist>& getWRAllowlist() 79 { 80 static std::vector<i2cControllerWRAllowlist> wrAllowlist; 81 return wrAllowlist; 82 } 83 84 static constexpr const char* i2cControllerWRAllowlistFile = 85 "/usr/share/ipmi-providers/master_write_read_white_list.json"; 86 87 static constexpr const char* filtersStr = "filters"; 88 static constexpr const char* busIdStr = "busId"; 89 static constexpr const char* targetAddrStr = "slaveAddr"; 90 static constexpr const char* targetAddrMaskStr = "slaveAddrMask"; 91 static constexpr const char* cmdStr = "command"; 92 static constexpr const char* cmdMaskStr = "commandMask"; 93 static constexpr int base_16 = 16; 94 #endif // ENABLE_I2C_WHITELIST_CHECK 95 static constexpr uint8_t oemCmdStart = 192; 96 static constexpr uint8_t invalidParamSelectorStart = 8; 97 static constexpr uint8_t invalidParamSelectorEnd = 191; 98 99 /** 100 * @brief Returns the Version info from primary s/w object 101 * 102 * Get the Version info from the active s/w object which is having high 103 * "Priority" value(a smaller number is a higher priority) and "Purpose" 104 * is "BMC" from the list of all s/w objects those are implementing 105 * RedundancyPriority interface from the given softwareRoot path. 106 * 107 * @return On success returns the Version info from primary s/w object. 108 * 109 */ 110 std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx) 111 { 112 std::string revision{}; 113 ipmi::ObjectTree objectTree; 114 try 115 { 116 objectTree = 117 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf); 118 } 119 catch (const sdbusplus::exception_t& e) 120 { 121 lg2::error("Failed to fetch redundancy object from dbus, " 122 "interface: {INTERFACE}, error: {ERROR}", 123 "INTERFACE", redundancyIntf, "ERROR", e); 124 elog<InternalFailure>(); 125 } 126 127 auto objectFound = false; 128 for (auto& softObject : objectTree) 129 { 130 auto service = 131 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first); 132 auto objValueTree = 133 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot); 134 135 auto minPriority = 0xFF; 136 for (const auto& objIter : objValueTree) 137 { 138 try 139 { 140 auto& intfMap = objIter.second; 141 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 142 auto& versionProps = intfMap.at(versionIntf); 143 auto& activationProps = intfMap.at(activationIntf); 144 auto priority = 145 std::get<uint8_t>(redundancyPriorityProps.at("Priority")); 146 auto purpose = 147 std::get<std::string>(versionProps.at("Purpose")); 148 auto activation = 149 std::get<std::string>(activationProps.at("Activation")); 150 auto version = 151 std::get<std::string>(versionProps.at("Version")); 152 if ((Version::convertVersionPurposeFromString(purpose) == 153 Version::VersionPurpose::BMC) && 154 (Activation::convertActivationsFromString(activation) == 155 Activation::Activations::Active)) 156 { 157 if (priority < minPriority) 158 { 159 minPriority = priority; 160 objectFound = true; 161 revision = std::move(version); 162 } 163 } 164 } 165 catch (const std::exception& e) 166 { 167 lg2::error("error message: {ERROR}", "ERROR", e); 168 } 169 } 170 } 171 172 if (!objectFound) 173 { 174 lg2::error("Could not found an BMC software Object"); 175 elog<InternalFailure>(); 176 } 177 178 return revision; 179 } 180 181 bool getCurrentBmcState() 182 { 183 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 184 185 // Get the Inventory object implementing the BMC interface 186 ipmi::DbusObjectInfo bmcObject = 187 ipmi::getDbusObject(bus, BMCState::interface); 188 auto variant = ipmi::getDbusProperty( 189 bus, bmcObject.second, bmcObject.first, BMCState::interface, 190 BMCState::property_names::current_bmc_state); 191 192 return std::holds_alternative<std::string>(variant) && 193 BMCState::convertBMCStateFromString( 194 std::get<std::string>(variant)) == BMCState::BMCState::Ready; 195 } 196 197 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability) 198 { 199 try 200 { 201 return getCurrentBmcState(); 202 } 203 catch (...) 204 { 205 // Nothing provided the BMC interface, therefore return whatever was 206 // configured as the default. 207 return fallbackAvailability; 208 } 209 } 210 211 namespace acpi_state 212 { 213 using namespace sdbusplus::server::xyz::openbmc_project::control::power; 214 215 const static constexpr char* acpiInterface = 216 "xyz.openbmc_project.Control.Power.ACPIPowerState"; 217 const static constexpr char* sysACPIProp = "SysACPIStatus"; 218 const static constexpr char* devACPIProp = "DevACPIStatus"; 219 220 enum class PowerStateType : uint8_t 221 { 222 sysPowerState = 0x00, 223 devPowerState = 0x01, 224 }; 225 226 // Defined in 20.6 of ipmi doc 227 enum class PowerState : uint8_t 228 { 229 s0G0D0 = 0x00, 230 s1D1 = 0x01, 231 s2D2 = 0x02, 232 s3D3 = 0x03, 233 s4 = 0x04, 234 s5G2 = 0x05, 235 s4S5 = 0x06, 236 g3 = 0x07, 237 sleep = 0x08, 238 g1Sleep = 0x09, 239 override = 0x0a, 240 legacyOn = 0x20, 241 legacyOff = 0x21, 242 unknown = 0x2a, 243 noChange = 0x7f, 244 }; 245 246 static constexpr uint8_t stateChanged = 0x80; 247 248 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = { 249 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0}, 250 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1}, 251 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2}, 252 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3}, 253 {ACPIPowerState::ACPI::S4, PowerState::s4}, 254 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2}, 255 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5}, 256 {ACPIPowerState::ACPI::G3, PowerState::g3}, 257 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep}, 258 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep}, 259 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override}, 260 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn}, 261 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff}, 262 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}}; 263 264 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state) 265 { 266 if (type == acpi_state::PowerStateType::sysPowerState) 267 { 268 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) || 269 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) || 270 (state == 271 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) || 272 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || 273 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) 274 { 275 return true; 276 } 277 else 278 { 279 return false; 280 } 281 } 282 else if (type == acpi_state::PowerStateType::devPowerState) 283 { 284 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) || 285 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || 286 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) 287 { 288 return true; 289 } 290 else 291 { 292 return false; 293 } 294 } 295 else 296 { 297 return false; 298 } 299 return false; 300 } 301 } // namespace acpi_state 302 303 /** @brief implements Set ACPI Power State command 304 * @param sysAcpiState - ACPI system power state to set 305 * @param devAcpiState - ACPI device power state to set 306 * 307 * @return IPMI completion code on success 308 **/ 309 ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState, 310 uint8_t devAcpiState) 311 { 312 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown); 313 314 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 315 316 auto value = acpi_state::ACPIPowerState::ACPI::Unknown; 317 318 if (sysAcpiState & acpi_state::stateChanged) 319 { 320 // set system power state 321 s = sysAcpiState & ~acpi_state::stateChanged; 322 323 if (!acpi_state::isValidACPIState( 324 acpi_state::PowerStateType::sysPowerState, s)) 325 { 326 lg2::error("set_acpi_power sys invalid input, S: {S}", "S", s); 327 return ipmi::responseParmOutOfRange(); 328 } 329 330 // valid input 331 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) 332 { 333 lg2::debug("No change for system power state"); 334 } 335 else 336 { 337 auto found = std::find_if( 338 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), 339 [&s](const auto& iter) { 340 return (static_cast<uint8_t>(iter.second) == s); 341 }); 342 343 value = found->first; 344 345 try 346 { 347 auto acpiObject = 348 ipmi::getDbusObject(bus, acpi_state::acpiInterface); 349 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, 350 acpi_state::acpiInterface, 351 acpi_state::sysACPIProp, 352 convertForMessage(value)); 353 } 354 catch (const InternalFailure& e) 355 { 356 lg2::error("Failed in set ACPI system property: {ERROR}", 357 "ERROR", e); 358 return ipmi::responseUnspecifiedError(); 359 } 360 } 361 } 362 else 363 { 364 lg2::debug("Do not change system power state"); 365 } 366 367 if (devAcpiState & acpi_state::stateChanged) 368 { 369 // set device power state 370 s = devAcpiState & ~acpi_state::stateChanged; 371 if (!acpi_state::isValidACPIState( 372 acpi_state::PowerStateType::devPowerState, s)) 373 { 374 lg2::error("set_acpi_power dev invalid input, S: {S}", "S", s); 375 return ipmi::responseParmOutOfRange(); 376 } 377 378 // valid input 379 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) 380 { 381 lg2::debug("No change for device power state"); 382 } 383 else 384 { 385 auto found = std::find_if( 386 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), 387 [&s](const auto& iter) { 388 return (static_cast<uint8_t>(iter.second) == s); 389 }); 390 391 value = found->first; 392 393 try 394 { 395 auto acpiObject = 396 ipmi::getDbusObject(bus, acpi_state::acpiInterface); 397 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, 398 acpi_state::acpiInterface, 399 acpi_state::devACPIProp, 400 convertForMessage(value)); 401 } 402 catch (const InternalFailure& e) 403 { 404 lg2::error("Failed in set ACPI device property: {ERROR}", 405 "ERROR", e); 406 return ipmi::responseUnspecifiedError(); 407 } 408 } 409 } 410 else 411 { 412 lg2::debug("Do not change device power state"); 413 } 414 return ipmi::responseSuccess(); 415 } 416 417 /** 418 * @brief implements the get ACPI power state command 419 * 420 * @return IPMI completion code plus response data on success. 421 * - ACPI system power state 422 * - ACPI device power state 423 **/ 424 ipmi::RspType<uint8_t, // acpiSystemPowerState 425 uint8_t // acpiDevicePowerState 426 > 427 ipmiGetAcpiPowerState() 428 { 429 uint8_t sysAcpiState; 430 uint8_t devAcpiState; 431 432 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 433 434 try 435 { 436 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface); 437 438 auto sysACPIVal = ipmi::getDbusProperty( 439 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, 440 acpi_state::sysACPIProp); 441 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString( 442 std::get<std::string>(sysACPIVal)); 443 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI)); 444 445 auto devACPIVal = ipmi::getDbusProperty( 446 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, 447 acpi_state::devACPIProp); 448 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString( 449 std::get<std::string>(devACPIVal)); 450 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI)); 451 } 452 catch (const InternalFailure& e) 453 { 454 return ipmi::responseUnspecifiedError(); 455 } 456 457 return ipmi::responseSuccess(sysAcpiState, devAcpiState); 458 } 459 460 typedef struct 461 { 462 char major; 463 char minor; 464 uint8_t aux[4]; 465 } Revision; 466 467 /* Use regular expression searching matched pattern X.Y, and convert it to */ 468 /* Major (X) and Minor (Y) version. */ 469 /* Example: */ 470 /* version = 2.14.0-dev */ 471 /* ^ ^ */ 472 /* | |---------------- Minor */ 473 /* |------------------ Major */ 474 /* */ 475 /* Default regex string only tries to match Major and Minor version. */ 476 /* */ 477 /* To match more firmware version info, platforms need to define it own */ 478 /* regex string to match more strings, and assign correct mapping index in */ 479 /* matches array. */ 480 /* */ 481 /* matches[0]: matched index for major ver */ 482 /* matches[1]: matched index for minor ver */ 483 /* matches[2]: matched index for aux[0] (set 0 to skip) */ 484 /* matches[3]: matched index for aux[1] (set 0 to skip) */ 485 /* matches[4]: matched index for aux[2] (set 0 to skip) */ 486 /* matches[5]: matched index for aux[3] (set 0 to skip) */ 487 /* Example: */ 488 /* regex = "([\d]+).([\d]+).([\d]+)-dev-([\d]+)-g([0-9a-fA-F]{2}) */ 489 /* ([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})" */ 490 /* matches = {1,2,5,6,7,8} */ 491 /* version = 2.14.0-dev-750-g37a7c5ad1-dirty */ 492 /* ^ ^ ^ ^ ^ ^ ^ ^ */ 493 /* | | | | | | | | */ 494 /* | | | | | | | |-- Aux byte 3 (0xAD), index 8 */ 495 /* | | | | | | |---- Aux byte 2 (0xC5), index 7 */ 496 /* | | | | | |------ Aux byte 1 (0xA7), index 6 */ 497 /* | | | | |-------- Aux byte 0 (0x37), index 5 */ 498 /* | | | |------------- Not used, index 4 */ 499 /* | | |------------------- Not used, index 3 */ 500 /* | |---------------------- Minor (14), index 2 */ 501 /* |------------------------ Major (2), index 1 */ 502 int convertVersion(std::string s, Revision& rev) 503 { 504 static const std::vector<size_t> matches = { 505 MAJOR_MATCH_INDEX, MINOR_MATCH_INDEX, AUX_0_MATCH_INDEX, 506 AUX_1_MATCH_INDEX, AUX_2_MATCH_INDEX, AUX_3_MATCH_INDEX}; 507 std::regex fw_regex(FW_VER_REGEX); 508 std::smatch m; 509 Revision r = {0}; 510 size_t val; 511 512 if (std::regex_search(s, m, fw_regex)) 513 { 514 if (m.size() < *std::max_element(matches.begin(), matches.end())) 515 { // max index higher than match count 516 return -1; 517 } 518 519 // convert major 520 { 521 std::string str = m[matches[0]].str(); 522 const auto& [ptr, ec] = 523 std::from_chars(str.data(), str.data() + str.size(), val); 524 if (ec != std::errc() || ptr != str.data() + str.size()) 525 { // failed to convert major string 526 return -1; 527 } 528 529 if (val >= 2000) 530 { // For the platforms use year as major version, it would expect to 531 // have major version between 0 - 99. If the major version is 532 // greater than or equal to 2000, it is treated as a year and 533 // converted to 0 - 99. 534 r.major = val % 100; 535 } 536 else 537 { 538 r.major = val & 0x7F; 539 } 540 } 541 542 // convert minor 543 { 544 std::string str = m[matches[1]].str(); 545 const auto& [ptr, ec] = 546 std::from_chars(str.data(), str.data() + str.size(), val); 547 if (ec != std::errc() || ptr != str.data() + str.size()) 548 { // failed to convert minor string 549 return -1; 550 } 551 r.minor = val & 0xFF; 552 } 553 554 // convert aux bytes 555 { 556 size_t i; 557 for (i = 0; i < 4; i++) 558 { 559 if (matches[i + 2] == 0) 560 { 561 continue; 562 } 563 564 std::string str = m[matches[i + 2]].str(); 565 const char* cstr = str.c_str(); 566 auto [ptr, 567 ec] = std::from_chars(cstr, cstr + str.size(), val, 16); 568 if (ec != std::errc() || ptr != cstr + str.size()) 569 { // failed to convert aux byte string 570 break; 571 } 572 573 r.aux[i] = val & 0xFF; 574 } 575 576 if (i != 4) 577 { // something wrong durign converting aux bytes 578 return -1; 579 } 580 } 581 582 // all matched 583 rev = r; 584 return 0; 585 } 586 587 return -1; 588 } 589 590 /* @brief: Implement the Get Device ID IPMI command per the IPMI spec 591 * @param[in] ctx - shared_ptr to an IPMI context struct 592 * 593 * @returns IPMI completion code plus response data 594 * - Device ID (manufacturer defined) 595 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit] 596 * - FW revision major[7 bits] (binary encoded); available[1 bit] 597 * - FW Revision minor (BCD encoded) 598 * - IPMI version (0x02 for IPMI 2.0) 599 * - device support (bitfield of supported options) 600 * - MFG IANA ID (3 bytes) 601 * - product ID (2 bytes) 602 * - AUX info (4 bytes) 603 */ 604 ipmi::RspType<uint8_t, // Device ID 605 uint8_t, // Device Revision 606 uint8_t, // Firmware Revision Major 607 uint8_t, // Firmware Revision minor 608 uint8_t, // IPMI version 609 uint8_t, // Additional device support 610 uint24_t, // MFG ID 611 uint16_t, // Product ID 612 uint32_t // AUX info 613 > 614 ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx) 615 { 616 static struct 617 { 618 uint8_t id; 619 uint8_t revision; 620 uint8_t fw[2]; 621 uint8_t ipmiVer; 622 uint8_t addnDevSupport; 623 uint24_t manufId; 624 uint16_t prodId; 625 uint32_t aux; 626 } devId; 627 static bool dev_id_initialized = false; 628 static bool defaultActivationSetting = true; 629 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 630 constexpr auto ipmiDevIdStateShift = 7; 631 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); 632 633 #ifdef GET_DBUS_ACTIVE_SOFTWARE 634 static bool haveBMCVersion = false; 635 if (!haveBMCVersion || !dev_id_initialized) 636 { 637 int r = -1; 638 Revision rev = {0, 0, {0, 0, 0, 0}}; 639 try 640 { 641 auto version = getActiveSoftwareVersionInfo(ctx); 642 r = convertVersion(version, rev); 643 } 644 catch (const std::exception& e) 645 { 646 lg2::error("error message: {ERROR}", "ERROR", e); 647 } 648 649 if (r >= 0) 650 { 651 // bit7 identifies if the device is available 652 // 0=normal operation 653 // 1=device firmware, SDR update, 654 // or self-initialization in progress. 655 // The availability may change in run time, so mask here 656 // and initialize later. 657 devId.fw[0] = rev.major & ipmiDevIdFw1Mask; 658 659 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 660 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 661 std::memcpy(&devId.aux, rev.aux, sizeof(rev.aux)); 662 haveBMCVersion = true; 663 } 664 } 665 #endif 666 if (!dev_id_initialized) 667 { 668 // IPMI Spec version 2.0 669 devId.ipmiVer = 2; 670 671 std::ifstream devIdFile(filename); 672 if (devIdFile.is_open()) 673 { 674 auto data = nlohmann::json::parse(devIdFile, nullptr, false); 675 if (!data.is_discarded()) 676 { 677 devId.id = data.value("id", 0); 678 devId.revision = data.value("revision", 0); 679 devId.addnDevSupport = data.value("addn_dev_support", 0); 680 devId.manufId = data.value("manuf_id", 0); 681 devId.prodId = data.value("prod_id", 0); 682 #ifdef GET_DBUS_ACTIVE_SOFTWARE 683 if (!(AUX_0_MATCH_INDEX || AUX_1_MATCH_INDEX || 684 AUX_2_MATCH_INDEX || AUX_3_MATCH_INDEX)) 685 #endif 686 { 687 devId.aux = data.value("aux", 0); 688 } 689 690 if (data.contains("firmware_revision")) 691 { 692 const auto& firmwareRevision = data.at("firmware_revision"); 693 if (firmwareRevision.contains("major")) 694 { 695 firmwareRevision.at("major").get_to(devId.fw[0]); 696 } 697 if (firmwareRevision.contains("minor")) 698 { 699 firmwareRevision.at("minor").get_to(devId.fw[1]); 700 } 701 } 702 703 // Set the availablitity of the BMC. 704 defaultActivationSetting = data.value("availability", true); 705 706 // Don't read the file every time if successful 707 dev_id_initialized = true; 708 } 709 else 710 { 711 lg2::error("Device ID JSON parser failure"); 712 return ipmi::responseUnspecifiedError(); 713 } 714 } 715 else 716 { 717 lg2::error("Device ID file not found"); 718 return ipmi::responseUnspecifiedError(); 719 } 720 } 721 722 // Set availability to the actual current BMC state 723 devId.fw[0] &= ipmiDevIdFw1Mask; 724 if (!getCurrentBmcStateWithFallback(defaultActivationSetting)) 725 { 726 devId.fw[0] |= (1 << ipmiDevIdStateShift); 727 } 728 729 return ipmi::responseSuccess( 730 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer, 731 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux); 732 } 733 734 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t> 735 { 736 // Byte 2: 737 // 55h - No error. 738 // 56h - Self Test function not implemented in this controller. 739 // 57h - Corrupted or inaccesssible data or devices. 740 // 58h - Fatal hardware error. 741 // FFh - reserved. 742 // all other: Device-specific 'internal failure'. 743 // Byte 3: 744 // For byte 2 = 55h, 56h, FFh: 00h 745 // For byte 2 = 58h, all other: Device-specific 746 // For byte 2 = 57h: self-test error bitfield. 747 // Note: returning 57h does not imply that all test were run. 748 // [7] 1b = Cannot access SEL device. 749 // [6] 1b = Cannot access SDR Repository. 750 // [5] 1b = Cannot access BMC FRU device. 751 // [4] 1b = IPMB signal lines do not respond. 752 // [3] 1b = SDR Repository empty. 753 // [2] 1b = Internal Use Area of BMC FRU corrupted. 754 // [1] 1b = controller update 'boot block' firmware corrupted. 755 // [0] 1b = controller operational firmware corrupted. 756 constexpr uint8_t notImplemented = 0x56; 757 constexpr uint8_t zero = 0; 758 return ipmi::responseSuccess(notImplemented, zero); 759 } 760 761 static constexpr size_t uuidBinaryLength = 16; 762 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122) 763 { 764 using Argument = xyz::openbmc_project::common::InvalidArgument; 765 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 766 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 767 // order 768 // Ex: 0x2332fc2c40e66298e511f2782395a361 769 constexpr size_t uuidHexLength = (2 * uuidBinaryLength); 770 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4); 771 std::array<uint8_t, uuidBinaryLength> uuid; 772 if (rfc4122.size() == uuidRfc4122Length) 773 { 774 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'), 775 rfc4122.end()); 776 } 777 if (rfc4122.size() != uuidHexLength) 778 { 779 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 780 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 781 } 782 for (size_t ind = 0; ind < uuidHexLength; ind += 2) 783 { 784 char v[3]; 785 v[0] = rfc4122[ind]; 786 v[1] = rfc4122[ind + 1]; 787 v[2] = 0; 788 size_t err; 789 long b; 790 try 791 { 792 b = std::stoul(v, &err, 16); 793 } 794 catch (const std::exception& e) 795 { 796 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 797 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 798 } 799 // check that exactly two ascii bytes were converted 800 if (err != 2) 801 { 802 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 803 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 804 } 805 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b); 806 } 807 return uuid; 808 } 809 810 auto ipmiAppGetDeviceGuid() 811 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>> 812 { 813 // return a fixed GUID based on /etc/machine-id 814 // This should match the /redfish/v1/Managers/bmc's UUID data 815 816 // machine specific application ID (for BMC ID) 817 // generated by systemd-id128 -p new as per man page 818 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE( 819 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78); 820 821 sd_id128_t bmcUuid; 822 // create the UUID from /etc/machine-id via the systemd API 823 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid); 824 825 char bmcUuidCstr[SD_ID128_STRING_MAX]; 826 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr); 827 828 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid); 829 return ipmi::responseSuccess(uuid); 830 } 831 832 auto ipmiAppGetBtCapabilities() 833 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t> 834 { 835 // Per IPMI 2.0 spec, the input and output buffer size must be the max 836 // buffer size minus one byte to allocate space for the length byte. 837 constexpr uint8_t nrOutstanding = 0x01; 838 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1; 839 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1; 840 constexpr uint8_t transactionTime = 0x0A; 841 constexpr uint8_t nrRetries = 0x01; 842 843 return ipmi::responseSuccess(nrOutstanding, inputBufferSize, 844 outputBufferSize, transactionTime, nrRetries); 845 } 846 847 auto ipmiAppGetSystemGuid(ipmi::Context::ptr& ctx) 848 -> ipmi::RspType<std::array<uint8_t, 16>> 849 { 850 static constexpr auto uuidService = "xyz.openbmc_project.Settings"; 851 static constexpr auto uuidObject = "/xyz/openbmc_project/Common/UUID"; 852 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID"; 853 static constexpr auto uuidProperty = "UUID"; 854 855 // Read UUID property value from settings object 856 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 857 std::string rfc4122Uuid{}; 858 boost::system::error_code ec = ipmi::getDbusProperty( 859 ctx, uuidService, uuidObject, uuidInterface, uuidProperty, rfc4122Uuid); 860 if (ec.value()) 861 { 862 lg2::error("Failed to read System UUID property, " 863 "interface: {INTERFACE}, property: {PROPERTY}, " 864 "error: {ERROR}", 865 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty, 866 "ERROR", ec.message()); 867 return ipmi::responseUnspecifiedError(); 868 } 869 std::array<uint8_t, 16> uuid; 870 try 871 { 872 // convert to IPMI format 873 uuid = rfc4122ToIpmi(rfc4122Uuid); 874 } 875 catch (const InvalidArgument& e) 876 { 877 lg2::error("Failed in parsing BMC UUID property, " 878 "interface: {INTERFACE}, property: {PROPERTY}, " 879 "value: {VALUE}, error: {ERROR}", 880 "INTERFACE", uuidInterface, "PROPERTY", uuidProperty, 881 "VALUE", rfc4122Uuid, "ERROR", e); 882 return ipmi::responseUnspecifiedError(); 883 } 884 return ipmi::responseSuccess(uuid); 885 } 886 887 /** 888 * @brief set the session state as teardown 889 * 890 * This function is to set the session state to tear down in progress if the 891 * state is active. 892 * 893 * @param[in] busp - Dbus obj 894 * @param[in] service - service name 895 * @param[in] obj - object path 896 * 897 * @return success completion code if it sets the session state to 898 * tearDownInProgress else return the corresponding error completion code. 899 **/ 900 uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp, 901 const std::string& service, const std::string& obj) 902 { 903 try 904 { 905 uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty( 906 *busp, service, obj, session::sessionIntf, "State")); 907 908 if (sessionState == static_cast<uint8_t>(session::State::active)) 909 { 910 ipmi::setDbusProperty( 911 *busp, service, obj, session::sessionIntf, "State", 912 static_cast<uint8_t>(session::State::tearDownInProgress)); 913 return ipmi::ccSuccess; 914 } 915 } 916 catch (const std::exception& e) 917 { 918 lg2::error("Failed in getting session state property, " 919 "service: {SERVICE}, object path: {OBJECT_PATH}, " 920 "interface: {INTERFACE}, error: {ERROR}", 921 "SERVICE", service, "OBJECT_PATH", obj, "INTERFACE", 922 session::sessionIntf, "ERROR", e); 923 return ipmi::ccUnspecifiedError; 924 } 925 926 return ipmi::ccInvalidFieldRequest; 927 } 928 929 ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId, 930 std::optional<uint8_t> requestSessionHandle) 931 { 932 auto busp = getSdBus(); 933 uint8_t reqSessionHandle = 934 requestSessionHandle.value_or(session::defaultSessionHandle); 935 936 if (reqSessionId == session::sessionZero && 937 reqSessionHandle == session::defaultSessionHandle) 938 { 939 return ipmi::response(session::ccInvalidSessionId); 940 } 941 942 if (reqSessionId == session::sessionZero && 943 reqSessionHandle == session::invalidSessionHandle) 944 { 945 return ipmi::response(session::ccInvalidSessionHandle); 946 } 947 948 if (reqSessionId != session::sessionZero && 949 reqSessionHandle != session::defaultSessionHandle) 950 { 951 return ipmi::response(ipmi::ccInvalidFieldRequest); 952 } 953 954 try 955 { 956 ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects( 957 *busp, session::sessionManagerRootPath, session::sessionIntf); 958 959 for (auto& objectTreeItr : objectTree) 960 { 961 const std::string obj = objectTreeItr.first; 962 963 if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle)) 964 { 965 auto& serviceMap = objectTreeItr.second; 966 967 // Session id and session handle are unique for each session. 968 // Session id and handler are retrived from the object path and 969 // object path will be unique for each session. Checking if 970 // multiple objects exist with same object path under multiple 971 // services. 972 if (serviceMap.size() != 1) 973 { 974 return ipmi::responseUnspecifiedError(); 975 } 976 977 auto itr = serviceMap.begin(); 978 const std::string service = itr->first; 979 return ipmi::response(setSessionState(busp, service, obj)); 980 } 981 } 982 } 983 catch (const sdbusplus::exception_t& e) 984 { 985 lg2::error("Failed to fetch object from dbus, " 986 "interface: {INTERFACE}, error: {ERROR}", 987 "INTERFACE", session::sessionIntf, "ERROR", e); 988 return ipmi::responseUnspecifiedError(); 989 } 990 991 return ipmi::responseInvalidFieldRequest(); 992 } 993 994 uint8_t getTotalSessionCount() 995 { 996 uint8_t count = 0, ch = 0; 997 998 while (ch < ipmi::maxIpmiChannels && 999 count < session::maxNetworkInstanceSupported) 1000 { 1001 ipmi::ChannelInfo chInfo{}; 1002 ipmi::getChannelInfo(ch, chInfo); 1003 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) == 1004 ipmi::EChannelMediumType::lan8032) 1005 { 1006 count++; 1007 } 1008 ch++; 1009 } 1010 return count * session::maxSessionCountPerChannel; 1011 } 1012 1013 /** 1014 * @brief get session info request data. 1015 * 1016 * This function validates the request data and retrive request session id, 1017 * session handle. 1018 * 1019 * @param[in] ctx - context of current session. 1020 * @param[in] sessionIndex - request session index 1021 * @param[in] payload - input payload 1022 * @param[in] reqSessionId - unpacked session Id will be asigned 1023 * @param[in] reqSessionHandle - unpacked session handle will be asigned 1024 * 1025 * @return success completion code if request data is valid 1026 * else return the correcponding error completion code. 1027 **/ 1028 uint8_t getSessionInfoRequestData( 1029 const ipmi::Context::ptr ctx, const uint8_t sessionIndex, 1030 ipmi::message::Payload& payload, uint32_t& reqSessionId, 1031 uint8_t& reqSessionHandle) 1032 { 1033 if ((sessionIndex > session::maxSessionCountPerChannel) && 1034 (sessionIndex < session::searchSessionByHandle)) 1035 { 1036 return ipmi::ccInvalidFieldRequest; 1037 } 1038 1039 switch (sessionIndex) 1040 { 1041 case session::searchCurrentSession: 1042 1043 ipmi::ChannelInfo chInfo; 1044 ipmi::getChannelInfo(ctx->channel, chInfo); 1045 1046 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) != 1047 ipmi::EChannelMediumType::lan8032) 1048 { 1049 return ipmi::ccInvalidFieldRequest; 1050 } 1051 1052 if (!payload.fullyUnpacked()) 1053 { 1054 return ipmi::ccReqDataLenInvalid; 1055 } 1056 // Check if current sessionId is 0, sessionId 0 is reserved. 1057 if (ctx->sessionId == session::sessionZero) 1058 { 1059 return session::ccInvalidSessionId; 1060 } 1061 reqSessionId = ctx->sessionId; 1062 break; 1063 1064 case session::searchSessionByHandle: 1065 1066 if ((payload.unpack(reqSessionHandle)) || 1067 (!payload.fullyUnpacked())) 1068 { 1069 return ipmi::ccReqDataLenInvalid; 1070 } 1071 1072 if ((reqSessionHandle == session::sessionZero) || 1073 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) > 1074 session::maxSessionCountPerChannel)) 1075 { 1076 return session::ccInvalidSessionHandle; 1077 } 1078 break; 1079 1080 case session::searchSessionById: 1081 1082 if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked())) 1083 { 1084 return ipmi::ccReqDataLenInvalid; 1085 } 1086 1087 if (reqSessionId == session::sessionZero) 1088 { 1089 return session::ccInvalidSessionId; 1090 } 1091 break; 1092 1093 default: 1094 if (!payload.fullyUnpacked()) 1095 { 1096 return ipmi::ccReqDataLenInvalid; 1097 } 1098 break; 1099 } 1100 return ipmi::ccSuccess; 1101 } 1102 1103 uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service, 1104 const std::string& objPath, uint8_t& sessionState) 1105 { 1106 boost::system::error_code ec = ipmi::getDbusProperty( 1107 ctx, service, objPath, session::sessionIntf, "State", sessionState); 1108 if (ec) 1109 { 1110 lg2::error("Failed to fetch state property, service: {SERVICE}, " 1111 "object path: {OBJECTPATH}, interface: {INTERFACE}, " 1112 "error: {ERROR}", 1113 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE", 1114 session::sessionIntf, "ERROR", ec.message()); 1115 return ipmi::ccUnspecifiedError; 1116 } 1117 return ipmi::ccSuccess; 1118 } 1119 1120 static constexpr uint8_t macAddrLen = 6; 1121 /** Alias SessionDetails - contain the optional information about an 1122 * RMCP+ session. 1123 * 1124 * @param userID - uint6_t session user ID (0-63) 1125 * @param reserved - uint2_t reserved 1126 * @param privilege - uint4_t session privilege (0-5) 1127 * @param reserved - uint4_t reserved 1128 * @param channel - uint4_t session channel number 1129 * @param protocol - uint4_t session protocol 1130 * @param remoteIP - uint32_t remote IP address 1131 * @param macAddr - std::array<uint8_t, 6> mac address 1132 * @param port - uint16_t remote port 1133 */ 1134 using SessionDetails = 1135 std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t, 1136 std::array<uint8_t, macAddrLen>, uint16_t>; 1137 1138 /** @brief get session details for a given session 1139 * 1140 * @param[in] ctx - ipmi::Context pointer for accessing D-Bus 1141 * @param[in] service - D-Bus service name to fetch details from 1142 * @param[in] objPath - D-Bus object path for session 1143 * @param[out] sessionHandle - return session handle for session 1144 * @param[out] sessionState - return session state for session 1145 * @param[out] details - return a SessionDetails tuple containing other 1146 * session info 1147 * @return - ipmi::Cc success or error code 1148 */ 1149 ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service, 1150 const std::string& objPath, uint8_t& sessionHandle, 1151 uint8_t& sessionState, SessionDetails& details) 1152 { 1153 ipmi::PropertyMap sessionProps; 1154 boost::system::error_code ec = ipmi::getAllDbusProperties( 1155 ctx, service, objPath, session::sessionIntf, sessionProps); 1156 1157 if (ec) 1158 { 1159 lg2::error("Failed to fetch state property, service: {SERVICE}, " 1160 "object path: {OBJECTPATH}, interface: {INTERFACE}, " 1161 "error: {ERROR}", 1162 "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE", 1163 session::sessionIntf, "ERROR", ec.message()); 1164 return ipmi::ccUnspecifiedError; 1165 } 1166 1167 sessionState = ipmi::mappedVariant<uint8_t>( 1168 sessionProps, "State", static_cast<uint8_t>(session::State::inactive)); 1169 if (sessionState == static_cast<uint8_t>(session::State::active)) 1170 { 1171 sessionHandle = 1172 ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0); 1173 std::get<0>(details) = 1174 ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff); 1175 // std::get<1>(details) = 0; // (default constructed to 0) 1176 std::get<2>(details) = 1177 ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0); 1178 // std::get<3>(details) = 0; // (default constructed to 0) 1179 std::get<4>(details) = 1180 ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff); 1181 constexpr uint4_t rmcpPlusProtocol = 1; 1182 std::get<5>(details) = rmcpPlusProtocol; 1183 std::get<6>(details) = 1184 ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0); 1185 // std::get<7>(details) = {{0}}; // default constructed to all 0 1186 std::get<8>(details) = 1187 ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0); 1188 } 1189 1190 return ipmi::ccSuccess; 1191 } 1192 1193 ipmi::RspType<uint8_t, // session handle, 1194 uint8_t, // total session count 1195 uint8_t, // active session count 1196 std::optional<SessionDetails>> 1197 ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex, 1198 ipmi::message::Payload& payload) 1199 { 1200 uint32_t reqSessionId = 0; 1201 uint8_t reqSessionHandle = session::defaultSessionHandle; 1202 // initializing state to 0xff as 0 represents state as inactive. 1203 uint8_t state = 0xFF; 1204 1205 uint8_t completionCode = getSessionInfoRequestData( 1206 ctx, sessionIndex, payload, reqSessionId, reqSessionHandle); 1207 1208 if (completionCode) 1209 { 1210 return ipmi::response(completionCode); 1211 } 1212 ipmi::ObjectTree objectTree; 1213 boost::system::error_code ec = ipmi::getAllDbusObjects( 1214 ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree); 1215 if (ec) 1216 { 1217 lg2::error("Failed to fetch object from dbus, " 1218 "interface: {INTERFACE}, error: {ERROR}", 1219 "INTERFACE", session::sessionIntf, "ERROR", ec.message()); 1220 return ipmi::responseUnspecifiedError(); 1221 } 1222 1223 uint8_t totalSessionCount = getTotalSessionCount(); 1224 uint8_t activeSessionCount = 0; 1225 uint8_t sessionHandle = session::defaultSessionHandle; 1226 uint8_t activeSessionHandle = 0; 1227 std::optional<SessionDetails> maybeDetails; 1228 uint8_t index = 0; 1229 for (auto& objectTreeItr : objectTree) 1230 { 1231 uint32_t sessionId = 0; 1232 std::string objectPath = objectTreeItr.first; 1233 1234 if (!parseCloseSessionInputPayload(objectPath, sessionId, 1235 sessionHandle)) 1236 { 1237 continue; 1238 } 1239 index++; 1240 auto& serviceMap = objectTreeItr.second; 1241 auto itr = serviceMap.begin(); 1242 1243 if (serviceMap.size() != 1) 1244 { 1245 return ipmi::responseUnspecifiedError(); 1246 } 1247 1248 std::string service = itr->first; 1249 uint8_t sessionState = 0; 1250 completionCode = 1251 getSessionState(ctx, service, objectPath, sessionState); 1252 if (completionCode) 1253 { 1254 return ipmi::response(completionCode); 1255 } 1256 1257 if (sessionState == static_cast<uint8_t>(session::State::active)) 1258 { 1259 activeSessionCount++; 1260 } 1261 1262 if (index == sessionIndex || reqSessionId == sessionId || 1263 reqSessionHandle == sessionHandle) 1264 { 1265 SessionDetails details{}; 1266 completionCode = getSessionDetails(ctx, service, objectPath, 1267 sessionHandle, state, details); 1268 1269 if (completionCode) 1270 { 1271 return ipmi::response(completionCode); 1272 } 1273 activeSessionHandle = sessionHandle; 1274 maybeDetails = std::move(details); 1275 } 1276 } 1277 1278 if (state == static_cast<uint8_t>(session::State::active) || 1279 state == static_cast<uint8_t>(session::State::tearDownInProgress)) 1280 { 1281 return ipmi::responseSuccess(activeSessionHandle, totalSessionCount, 1282 activeSessionCount, maybeDetails); 1283 } 1284 1285 return ipmi::responseInvalidFieldRequest(); 1286 } 1287 1288 std::optional<std::string> getSysFWVersion(ipmi::Context::ptr& ctx) 1289 { 1290 /* 1291 * The System Firmware version is detected via following steps: 1292 * - Get all of object paths that include 1293 * "xyz.openbmc_project.Software.Version" interface. 1294 * - Get the Purpose property of above object paths. 1295 * - If the Purpose is Host then get the Version property. 1296 */ 1297 ipmi::ObjectTree objectTree; 1298 boost::system::error_code ec = 1299 ipmi::getAllDbusObjects(ctx, softwareRoot, versionIntf, objectTree); 1300 if (ec.value()) 1301 { 1302 return std::nullopt; 1303 } 1304 1305 for (const auto& [objPath, serviceMap] : objectTree) 1306 { 1307 for (const auto& [service, intfs] : serviceMap) 1308 { 1309 ipmi::PropertyMap props; 1310 ec = ipmi::getAllDbusProperties(ctx, service, objPath, versionIntf, 1311 props); 1312 if (ec.value()) 1313 { 1314 continue; 1315 } 1316 1317 std::string purposeProp = std::string( 1318 ipmi::mappedVariant<std::string>(props, "Purpose", "")); 1319 1320 if (!purposeProp.ends_with(versionPurposeHostEnd)) 1321 { 1322 continue; 1323 } 1324 1325 std::string sysFWVersion = std::string( 1326 ipmi::mappedVariant<std::string>(props, "Version", "")); 1327 1328 if (sysFWVersion.empty()) 1329 { 1330 return std::nullopt; 1331 } 1332 1333 return sysFWVersion; 1334 } 1335 } 1336 1337 return std::nullopt; 1338 } 1339 1340 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore; 1341 1342 static std::string sysInfoReadSystemName() 1343 { 1344 // Use the BMC hostname as the "System Name." 1345 char hostname[HOST_NAME_MAX + 1] = {}; 1346 if (gethostname(hostname, HOST_NAME_MAX) != 0) 1347 { 1348 perror("System info parameter: system name"); 1349 } 1350 return hostname; 1351 } 1352 1353 static constexpr uint8_t paramRevision = 0x11; 1354 static constexpr size_t configParameterLength = 16; 1355 1356 static constexpr size_t smallChunkSize = 14; 1357 static constexpr size_t fullChunkSize = 16; 1358 static constexpr uint8_t progressMask = 0x3; 1359 static constexpr uint8_t maxValidEncodingData = 0x02; 1360 1361 static constexpr uint8_t setComplete = 0x0; 1362 static constexpr uint8_t setInProgress = 0x1; 1363 static uint8_t transferStatus = setComplete; 1364 1365 static constexpr uint8_t configDataOverhead = 2; 1366 1367 namespace ipmi 1368 { 1369 constexpr Cc ccParmNotSupported = 0x80; 1370 constexpr Cc ccSetInProgressActive = 0x81; 1371 1372 static inline auto responseParmNotSupported() 1373 { 1374 return response(ccParmNotSupported); 1375 } 1376 static inline auto responseSetInProgressActive() 1377 { 1378 return response(ccSetInProgressActive); 1379 } 1380 } // namespace ipmi 1381 1382 ipmi::RspType<uint8_t, // Parameter revision 1383 std::optional<uint8_t>, // data1 / setSelector / ProgressStatus 1384 std::optional<std::vector<uint8_t>>> // data2-17 1385 ipmiAppGetSystemInfo(ipmi::Context::ptr ctx, uint7_t reserved, 1386 bool getRevision, uint8_t paramSelector, 1387 uint8_t setSelector, uint8_t BlockSelector) 1388 { 1389 if (reserved || (paramSelector >= invalidParamSelectorStart && 1390 paramSelector <= invalidParamSelectorEnd)) 1391 { 1392 return ipmi::responseInvalidFieldRequest(); 1393 } 1394 if (paramSelector >= oemCmdStart) 1395 { 1396 return ipmi::responseParmNotSupported(); 1397 } 1398 if (getRevision) 1399 { 1400 return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt); 1401 } 1402 1403 if (paramSelector == 0) 1404 { 1405 return ipmi::responseSuccess(paramRevision, transferStatus, 1406 std::nullopt); 1407 } 1408 1409 if (BlockSelector != 0) // 00h if parameter does not require a block number 1410 { 1411 return ipmi::responseParmNotSupported(); 1412 } 1413 1414 if (sysInfoParamStore == nullptr) 1415 { 1416 sysInfoParamStore = std::make_unique<SysInfoParamStore>(); 1417 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, 1418 sysInfoReadSystemName); 1419 } 1420 1421 if (paramSelector == IPMI_SYSINFO_SYSTEM_FW_VERSION) 1422 { 1423 // If the system firmware version is not cached, get it from the D-Bus. 1424 const auto& [found, paramString] = 1425 sysInfoParamStore->lookup(IPMI_SYSINFO_SYSTEM_FW_VERSION); 1426 1427 if (!found || paramString.empty()) 1428 { 1429 auto fwVersion = getSysFWVersion(ctx); 1430 if (fwVersion == std::nullopt) 1431 { 1432 return ipmi::responseUnspecifiedError(); 1433 } 1434 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_FW_VERSION, 1435 *fwVersion); 1436 } 1437 } 1438 1439 // Parameters other than Set In Progress are assumed to be strings. 1440 std::tuple<bool, std::string> ret = 1441 sysInfoParamStore->lookup(paramSelector); 1442 bool found = std::get<0>(ret); 1443 if (!found) 1444 { 1445 return ipmi::responseSensorInvalid(); 1446 } 1447 std::string& paramString = std::get<1>(ret); 1448 std::vector<uint8_t> configData; 1449 size_t count = 0; 1450 if (setSelector == 0) 1451 { // First chunk has only 14 bytes. 1452 configData.emplace_back(0); // encoding 1453 configData.emplace_back(paramString.length()); // string length 1454 count = std::min(paramString.length(), smallChunkSize); 1455 configData.resize(count + configDataOverhead); 1456 std::copy_n(paramString.begin(), count, 1457 configData.begin() + configDataOverhead); // 14 bytes chunk 1458 1459 // Append zero's to remaining bytes 1460 if (configData.size() < configParameterLength) 1461 { 1462 std::fill_n(std::back_inserter(configData), 1463 configParameterLength - configData.size(), 0x00); 1464 } 1465 } 1466 else 1467 { 1468 size_t offset = (setSelector * fullChunkSize) - configDataOverhead; 1469 if (offset >= paramString.length()) 1470 { 1471 return ipmi::responseParmOutOfRange(); 1472 } 1473 count = std::min(paramString.length() - offset, fullChunkSize); 1474 configData.resize(count); 1475 std::copy_n(paramString.begin() + offset, count, 1476 configData.begin()); // 16 bytes chunk 1477 } 1478 return ipmi::responseSuccess(paramRevision, setSelector, configData); 1479 } 1480 1481 ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1, 1482 std::vector<uint8_t> configData) 1483 { 1484 if (paramSelector >= invalidParamSelectorStart && 1485 paramSelector <= invalidParamSelectorEnd) 1486 { 1487 return ipmi::responseInvalidFieldRequest(); 1488 } 1489 if (paramSelector >= oemCmdStart) 1490 { 1491 return ipmi::responseParmNotSupported(); 1492 } 1493 1494 if (paramSelector == 0) 1495 { 1496 // attempt to set the 'set in progress' value (in parameter #0) 1497 // when not in the set complete state. 1498 if ((transferStatus != setComplete) && (data1 == setInProgress)) 1499 { 1500 return ipmi::responseSetInProgressActive(); 1501 } 1502 // only following 2 states are supported 1503 if (data1 > setInProgress) 1504 { 1505 lg2::error("illegal SetInProgress status"); 1506 return ipmi::responseInvalidFieldRequest(); 1507 } 1508 1509 transferStatus = data1 & progressMask; 1510 return ipmi::responseSuccess(); 1511 } 1512 1513 if (configData.size() > configParameterLength) 1514 { 1515 return ipmi::responseInvalidFieldRequest(); 1516 } 1517 1518 // Append zero's to remaining bytes 1519 if (configData.size() < configParameterLength) 1520 { 1521 fill_n(back_inserter(configData), 1522 (configParameterLength - configData.size()), 0x00); 1523 } 1524 1525 if (!sysInfoParamStore) 1526 { 1527 sysInfoParamStore = std::make_unique<SysInfoParamStore>(); 1528 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, 1529 sysInfoReadSystemName); 1530 } 1531 1532 // lookup 1533 std::tuple<bool, std::string> ret = 1534 sysInfoParamStore->lookup(paramSelector); 1535 bool found = std::get<0>(ret); 1536 std::string& paramString = std::get<1>(ret); 1537 if (!found) 1538 { 1539 // parameter does not exist. Init new 1540 paramString = ""; 1541 } 1542 1543 uint8_t setSelector = data1; 1544 size_t count = 0; 1545 if (setSelector == 0) // First chunk has only 14 bytes. 1546 { 1547 uint8_t encoding = configData.at(0); 1548 if (encoding > maxValidEncodingData) 1549 { 1550 return ipmi::responseInvalidFieldRequest(); 1551 } 1552 1553 size_t stringLen = configData.at(1); // string length 1554 count = std::min(stringLen, smallChunkSize); 1555 count = std::min(count, configData.size()); 1556 paramString.resize(stringLen); // reserve space 1557 std::copy_n(configData.begin() + configDataOverhead, count, 1558 paramString.begin()); 1559 } 1560 else 1561 { 1562 size_t offset = (setSelector * fullChunkSize) - configDataOverhead; 1563 if (offset >= paramString.length()) 1564 { 1565 return ipmi::responseParmOutOfRange(); 1566 } 1567 count = std::min(paramString.length() - offset, configData.size()); 1568 std::copy_n(configData.begin(), count, paramString.begin() + offset); 1569 } 1570 sysInfoParamStore->update(paramSelector, paramString); 1571 return ipmi::responseSuccess(); 1572 } 1573 1574 #ifdef ENABLE_I2C_WHITELIST_CHECK 1575 inline std::vector<uint8_t> convertStringToData(const std::string& command) 1576 { 1577 std::istringstream iss(command); 1578 std::string token; 1579 std::vector<uint8_t> dataValue; 1580 while (std::getline(iss, token, ' ')) 1581 { 1582 dataValue.emplace_back( 1583 static_cast<uint8_t>(std::stoul(token, nullptr, base_16))); 1584 } 1585 return dataValue; 1586 } 1587 1588 static bool populateI2CControllerWRAllowlist() 1589 { 1590 nlohmann::json data = nullptr; 1591 std::ifstream jsonFile(i2cControllerWRAllowlistFile); 1592 1593 if (!jsonFile.good()) 1594 { 1595 lg2::warning("i2c allow list file not found! file name: {FILE_NAME}", 1596 "FILE_NAME", i2cControllerWRAllowlistFile); 1597 return false; 1598 } 1599 1600 try 1601 { 1602 data = nlohmann::json::parse(jsonFile, nullptr, false); 1603 } 1604 catch (const nlohmann::json::parse_error& e) 1605 { 1606 lg2::error("Corrupted i2c allow list config file, " 1607 "file name: {FILE_NAME}, error: {ERROR}", 1608 "FILE_NAME", i2cControllerWRAllowlistFile, "ERROR", e); 1609 return false; 1610 } 1611 1612 try 1613 { 1614 // Example JSON Structure format 1615 // "filters": [ 1616 // { 1617 // "Description": "Allow full read - ignore first byte write value 1618 // for 0x40 to 0x4F", 1619 // "busId": "0x01", 1620 // "slaveAddr": "0x40", 1621 // "slaveAddrMask": "0x0F", 1622 // "command": "0x00", 1623 // "commandMask": "0xFF" 1624 // }, 1625 // { 1626 // "Description": "Allow full read - first byte match 0x05 and 1627 // ignore second byte", 1628 // "busId": "0x01", 1629 // "slaveAddr": "0x57", 1630 // "slaveAddrMask": "0x00", 1631 // "command": "0x05 0x00", 1632 // "commandMask": "0x00 0xFF" 1633 // },] 1634 1635 nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); 1636 std::vector<i2cControllerWRAllowlist>& allowlist = getWRAllowlist(); 1637 for (const auto& it : filters.items()) 1638 { 1639 nlohmann::json filter = it.value(); 1640 if (filter.is_null()) 1641 { 1642 lg2::error( 1643 "Corrupted I2C controller write read allowlist config file, " 1644 "file name: {FILE_NAME}", 1645 "FILE_NAME", i2cControllerWRAllowlistFile); 1646 return false; 1647 } 1648 const std::vector<uint8_t>& writeData = 1649 convertStringToData(filter[cmdStr].get<std::string>()); 1650 const std::vector<uint8_t>& writeDataMask = 1651 convertStringToData(filter[cmdMaskStr].get<std::string>()); 1652 if (writeDataMask.size() != writeData.size()) 1653 { 1654 lg2::error("I2C controller write read allowlist filter " 1655 "mismatch for command & mask size"); 1656 return false; 1657 } 1658 allowlist.push_back( 1659 {static_cast<uint8_t>(std::stoul( 1660 filter[busIdStr].get<std::string>(), nullptr, base_16)), 1661 static_cast<uint8_t>( 1662 std::stoul(filter[targetAddrStr].get<std::string>(), 1663 nullptr, base_16)), 1664 static_cast<uint8_t>( 1665 std::stoul(filter[targetAddrMaskStr].get<std::string>(), 1666 nullptr, base_16)), 1667 writeData, writeDataMask}); 1668 } 1669 if (allowlist.size() != filters.size()) 1670 { 1671 lg2::error( 1672 "I2C controller write read allowlist filter size mismatch"); 1673 return false; 1674 } 1675 } 1676 catch (const std::exception& e) 1677 { 1678 lg2::error("I2C controller write read allowlist " 1679 "unexpected exception: {ERROR}", 1680 "ERROR", e); 1681 return false; 1682 } 1683 return true; 1684 } 1685 1686 static inline bool isWriteDataAllowlisted(const std::vector<uint8_t>& data, 1687 const std::vector<uint8_t>& dataMask, 1688 const std::vector<uint8_t>& writeData) 1689 { 1690 std::vector<uint8_t> processedDataBuf(data.size()); 1691 std::vector<uint8_t> processedReqBuf(dataMask.size()); 1692 std::transform(writeData.begin(), writeData.end(), dataMask.begin(), 1693 processedReqBuf.begin(), std::bit_or<uint8_t>()); 1694 std::transform(data.begin(), data.end(), dataMask.begin(), 1695 processedDataBuf.begin(), std::bit_or<uint8_t>()); 1696 1697 return (processedDataBuf == processedReqBuf); 1698 } 1699 1700 static bool isCmdAllowlisted(uint8_t busId, uint8_t targetAddr, 1701 std::vector<uint8_t>& writeData) 1702 { 1703 std::vector<i2cControllerWRAllowlist>& allowList = getWRAllowlist(); 1704 for (const auto& wlEntry : allowList) 1705 { 1706 if ((busId == wlEntry.busId) && 1707 ((targetAddr | wlEntry.targetAddrMask) == 1708 (wlEntry.targetAddr | wlEntry.targetAddrMask))) 1709 { 1710 const std::vector<uint8_t>& dataMask = wlEntry.dataMask; 1711 // Skip as no-match, if requested write data is more than the 1712 // write data mask size 1713 if (writeData.size() > dataMask.size()) 1714 { 1715 continue; 1716 } 1717 if (isWriteDataAllowlisted(wlEntry.data, dataMask, writeData)) 1718 { 1719 return true; 1720 } 1721 } 1722 } 1723 return false; 1724 } 1725 #else 1726 static bool populateI2CControllerWRAllowlist() 1727 { 1728 lg2::info("I2C_WHITELIST_CHECK is disabled, do not populate allowlist"); 1729 return true; 1730 } 1731 #endif // ENABLE_I2C_WHITELIST_CHECK 1732 1733 /** @brief implements controller write read IPMI command which can be used for 1734 * low-level I2C/SMBus write, read or write-read access 1735 * @param isPrivateBus -to indicate private bus usage 1736 * @param busId - bus id 1737 * @param channelNum - channel number 1738 * @param reserved - skip 1 bit 1739 * @param targetAddr - target address 1740 * @param read count - number of bytes to be read 1741 * @param writeData - data to be written 1742 * 1743 * @returns IPMI completion code plus response data 1744 * - readData - i2c response data 1745 */ 1746 ipmi::RspType<std::vector<uint8_t>> ipmiControllerWriteRead( 1747 [[maybe_unused]] bool isPrivateBus, uint3_t busId, 1748 [[maybe_unused]] uint4_t channelNum, bool reserved, uint7_t targetAddr, 1749 uint8_t readCount, std::vector<uint8_t> writeData) 1750 { 1751 if (reserved) 1752 { 1753 return ipmi::responseInvalidFieldRequest(); 1754 } 1755 const size_t writeCount = writeData.size(); 1756 if (!readCount && !writeCount) 1757 { 1758 lg2::error("Controller write read command: Read & write count are 0"); 1759 return ipmi::responseInvalidFieldRequest(); 1760 } 1761 #ifdef ENABLE_I2C_WHITELIST_CHECK 1762 if (!isCmdAllowlisted(static_cast<uint8_t>(busId), 1763 static_cast<uint8_t>(targetAddr), writeData)) 1764 { 1765 lg2::error("Controller write read request blocked!, " 1766 "bus: {BUS}, addr: {ADDR}", 1767 "BUS", static_cast<uint8_t>(busId), "ADDR", lg2::hex, 1768 static_cast<uint8_t>(targetAddr)); 1769 } 1770 #endif // ENABLE_I2C_WHITELIST_CHECK 1771 std::vector<uint8_t> readBuf(readCount); 1772 std::string i2cBus = 1773 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId)); 1774 1775 ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(targetAddr), 1776 writeData, readBuf); 1777 if (ret != ipmi::ccSuccess) 1778 { 1779 return ipmi::response(ret); 1780 } 1781 return ipmi::responseSuccess(readBuf); 1782 } 1783 1784 void registerNetFnAppFunctions() 1785 { 1786 // <Get Device ID> 1787 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1788 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User, 1789 ipmiAppGetDeviceId); 1790 1791 // <Get BT Interface Capabilities> 1792 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1793 ipmi::app::cmdGetBtIfaceCapabilities, 1794 ipmi::Privilege::User, ipmiAppGetBtCapabilities); 1795 1796 // <Reset Watchdog Timer> 1797 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1798 ipmi::app::cmdResetWatchdogTimer, 1799 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer); 1800 1801 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1802 ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User, 1803 ipmiAppGetSessionInfo); 1804 1805 // <Set Watchdog Timer> 1806 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1807 ipmi::app::cmdSetWatchdogTimer, 1808 ipmi::Privilege::Operator, ipmiSetWatchdogTimer); 1809 1810 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1811 ipmi::app::cmdCloseSession, ipmi::Privilege::Callback, 1812 ipmiAppCloseSession); 1813 1814 // <Get Watchdog Timer> 1815 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1816 ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User, 1817 ipmiGetWatchdogTimer); 1818 1819 // <Get Self Test Results> 1820 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1821 ipmi::app::cmdGetSelfTestResults, 1822 ipmi::Privilege::User, ipmiAppGetSelfTestResults); 1823 1824 // <Get Device GUID> 1825 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1826 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User, 1827 ipmiAppGetDeviceGuid); 1828 1829 // <Set ACPI Power State> 1830 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1831 ipmi::app::cmdSetAcpiPowerState, 1832 ipmi::Privilege::Admin, ipmiSetAcpiPowerState); 1833 // <Get ACPI Power State> 1834 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1835 ipmi::app::cmdGetAcpiPowerState, 1836 ipmi::Privilege::User, ipmiGetAcpiPowerState); 1837 1838 // Note: For security reason, this command will be registered only when 1839 // there are proper I2C Controller write read allowlist 1840 if (populateI2CControllerWRAllowlist()) 1841 { 1842 // Note: For security reasons, registering controller write read as 1843 // admin privilege command, even though IPMI 2.0 specification allows it 1844 // as operator privilege. 1845 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1846 ipmi::app::cmdMasterWriteRead, 1847 ipmi::Privilege::Admin, ipmiControllerWriteRead); 1848 } 1849 1850 // <Get System GUID Command> 1851 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1852 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, 1853 ipmiAppGetSystemGuid); 1854 1855 // <Get Channel Cipher Suites Command> 1856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1857 ipmi::app::cmdGetChannelCipherSuites, 1858 ipmi::Privilege::None, getChannelCipherSuites); 1859 1860 // <Get System Info Command> 1861 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1862 ipmi::app::cmdGetSystemInfoParameters, 1863 ipmi::Privilege::User, ipmiAppGetSystemInfo); 1864 // <Set System Info Command> 1865 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1866 ipmi::app::cmdSetSystemInfoParameters, 1867 ipmi::Privilege::Admin, ipmiAppSetSystemInfo); 1868 return; 1869 } 1870