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