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