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