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