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