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