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