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