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