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/types.hpp> 24 #include <ipmid/utils.hpp> 25 #include <memory> 26 #include <nlohmann/json.hpp> 27 #include <phosphor-logging/elog-errors.hpp> 28 #include <phosphor-logging/log.hpp> 29 #include <sdbusplus/message/types.hpp> 30 #include <string> 31 #include <sys_info_param.hpp> 32 #include <transporthandler.hpp> 33 #include <tuple> 34 #include <vector> 35 #include <xyz/openbmc_project/Common/error.hpp> 36 #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp> 37 #include <xyz/openbmc_project/Software/Activation/server.hpp> 38 #include <xyz/openbmc_project/Software/Version/server.hpp> 39 #include <xyz/openbmc_project/State/BMC/server.hpp> 40 41 extern sd_bus* bus; 42 43 constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; 44 constexpr auto bmc_state_property = "CurrentBMCState"; 45 46 static constexpr auto redundancyIntf = 47 "xyz.openbmc_project.Software.RedundancyPriority"; 48 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; 49 static constexpr auto activationIntf = 50 "xyz.openbmc_project.Software.Activation"; 51 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 52 53 void register_netfn_app_functions() __attribute__((constructor)); 54 55 using namespace phosphor::logging; 56 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 57 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; 58 using Activation = 59 sdbusplus::xyz::openbmc_project::Software::server::Activation; 60 using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC; 61 namespace fs = std::filesystem; 62 63 typedef struct 64 { 65 uint8_t busId; 66 uint8_t slaveAddr; 67 uint8_t slaveAddrMask; 68 std::vector<uint8_t> data; 69 std::vector<uint8_t> dataMask; 70 } i2cMasterWRWhitelist; 71 72 static std::vector<i2cMasterWRWhitelist>& getWRWhitelist() 73 { 74 static std::vector<i2cMasterWRWhitelist> wrWhitelist; 75 return wrWhitelist; 76 } 77 78 static constexpr const char* i2cMasterWRWhitelistFile = 79 "/usr/share/ipmi-providers/master_write_read_white_list.json"; 80 81 static constexpr uint8_t maxIPMIWriteReadSize = 144; 82 static constexpr const char* filtersStr = "filters"; 83 static constexpr const char* busIdStr = "busId"; 84 static constexpr const char* slaveAddrStr = "slaveAddr"; 85 static constexpr const char* slaveAddrMaskStr = "slaveAddrMask"; 86 static constexpr const char* cmdStr = "command"; 87 static constexpr const char* cmdMaskStr = "commandMask"; 88 static constexpr int base_16 = 16; 89 90 /** 91 * @brief Returns the Version info from primary s/w object 92 * 93 * Get the Version info from the active s/w object which is having high 94 * "Priority" value(a smaller number is a higher priority) and "Purpose" 95 * is "BMC" from the list of all s/w objects those are implementing 96 * RedundancyPriority interface from the given softwareRoot path. 97 * 98 * @return On success returns the Version info from primary s/w object. 99 * 100 */ 101 std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx) 102 { 103 std::string revision{}; 104 ipmi::ObjectTree objectTree; 105 try 106 { 107 objectTree = 108 ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf); 109 } 110 catch (sdbusplus::exception::SdBusError& e) 111 { 112 log<level::ERR>("Failed to fetch redundancy object from dbus", 113 entry("INTERFACE=%s", redundancyIntf), 114 entry("ERRMSG=%s", e.what())); 115 elog<InternalFailure>(); 116 } 117 118 auto objectFound = false; 119 for (auto& softObject : objectTree) 120 { 121 auto service = 122 ipmi::getService(*ctx->bus, redundancyIntf, softObject.first); 123 auto objValueTree = 124 ipmi::getManagedObjects(*ctx->bus, service, softwareRoot); 125 126 auto minPriority = 0xFF; 127 for (const auto& objIter : objValueTree) 128 { 129 try 130 { 131 auto& intfMap = objIter.second; 132 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 133 auto& versionProps = intfMap.at(versionIntf); 134 auto& activationProps = intfMap.at(activationIntf); 135 auto priority = 136 std::get<uint8_t>(redundancyPriorityProps.at("Priority")); 137 auto purpose = 138 std::get<std::string>(versionProps.at("Purpose")); 139 auto activation = 140 std::get<std::string>(activationProps.at("Activation")); 141 auto version = 142 std::get<std::string>(versionProps.at("Version")); 143 if ((Version::convertVersionPurposeFromString(purpose) == 144 Version::VersionPurpose::BMC) && 145 (Activation::convertActivationsFromString(activation) == 146 Activation::Activations::Active)) 147 { 148 if (priority < minPriority) 149 { 150 minPriority = priority; 151 objectFound = true; 152 revision = std::move(version); 153 } 154 } 155 } 156 catch (const std::exception& e) 157 { 158 log<level::ERR>(e.what()); 159 } 160 } 161 } 162 163 if (!objectFound) 164 { 165 log<level::ERR>("Could not found an BMC software Object"); 166 elog<InternalFailure>(); 167 } 168 169 return revision; 170 } 171 172 bool getCurrentBmcState() 173 { 174 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 175 176 // Get the Inventory object implementing the BMC interface 177 ipmi::DbusObjectInfo bmcObject = 178 ipmi::getDbusObject(bus, bmc_state_interface); 179 auto variant = 180 ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, 181 bmc_state_interface, bmc_state_property); 182 183 return std::holds_alternative<std::string>(variant) && 184 BMC::convertBMCStateFromString(std::get<std::string>(variant)) == 185 BMC::BMCState::Ready; 186 } 187 188 bool getCurrentBmcStateWithFallback(const bool fallbackAvailability) 189 { 190 try 191 { 192 return getCurrentBmcState(); 193 } 194 catch (...) 195 { 196 // Nothing provided the BMC interface, therefore return whatever was 197 // configured as the default. 198 return fallbackAvailability; 199 } 200 } 201 202 namespace acpi_state 203 { 204 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; 205 206 const static constexpr char* acpiObjPath = 207 "/xyz/openbmc_project/control/host0/acpi_power_state"; 208 const static constexpr char* acpiInterface = 209 "xyz.openbmc_project.Control.Power.ACPIPowerState"; 210 const static constexpr char* sysACPIProp = "SysACPIStatus"; 211 const static constexpr char* devACPIProp = "DevACPIStatus"; 212 213 enum class PowerStateType : uint8_t 214 { 215 sysPowerState = 0x00, 216 devPowerState = 0x01, 217 }; 218 219 // Defined in 20.6 of ipmi doc 220 enum class PowerState : uint8_t 221 { 222 s0G0D0 = 0x00, 223 s1D1 = 0x01, 224 s2D2 = 0x02, 225 s3D3 = 0x03, 226 s4 = 0x04, 227 s5G2 = 0x05, 228 s4S5 = 0x06, 229 g3 = 0x07, 230 sleep = 0x08, 231 g1Sleep = 0x09, 232 override = 0x0a, 233 legacyOn = 0x20, 234 legacyOff = 0x21, 235 unknown = 0x2a, 236 noChange = 0x7f, 237 }; 238 239 static constexpr uint8_t stateChanged = 0x80; 240 241 struct ACPIState 242 { 243 uint8_t sysACPIState; 244 uint8_t devACPIState; 245 } __attribute__((packed)); 246 247 std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = { 248 {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0}, 249 {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1}, 250 {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2}, 251 {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3}, 252 {ACPIPowerState::ACPI::S4, PowerState::s4}, 253 {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2}, 254 {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5}, 255 {ACPIPowerState::ACPI::G3, PowerState::g3}, 256 {ACPIPowerState::ACPI::SLEEP, PowerState::sleep}, 257 {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep}, 258 {ACPIPowerState::ACPI::OVERRIDE, PowerState::override}, 259 {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn}, 260 {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff}, 261 {ACPIPowerState::ACPI::Unknown, PowerState::unknown}}; 262 263 bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state) 264 { 265 if (type == acpi_state::PowerStateType::sysPowerState) 266 { 267 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) || 268 (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) || 269 (state == 270 static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) || 271 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || 272 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) 273 { 274 return true; 275 } 276 else 277 { 278 return false; 279 } 280 } 281 else if (type == acpi_state::PowerStateType::devPowerState) 282 { 283 if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) || 284 (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || 285 (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) 286 { 287 return true; 288 } 289 else 290 { 291 return false; 292 } 293 } 294 else 295 { 296 return false; 297 } 298 return false; 299 } 300 } // namespace acpi_state 301 302 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 303 ipmi_request_t request, 304 ipmi_response_t response, 305 ipmi_data_len_t data_len, 306 ipmi_context_t context) 307 { 308 auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown); 309 ipmi_ret_t rc = IPMI_CC_OK; 310 311 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 312 313 auto value = acpi_state::ACPIPowerState::ACPI::Unknown; 314 315 auto* req = reinterpret_cast<acpi_state::ACPIState*>(request); 316 317 if (*data_len != sizeof(acpi_state::ACPIState)) 318 { 319 log<level::ERR>("set_acpi invalid len"); 320 *data_len = 0; 321 return IPMI_CC_REQ_DATA_LEN_INVALID; 322 } 323 324 *data_len = 0; 325 326 if (req->sysACPIState & acpi_state::stateChanged) 327 { 328 // set system power state 329 s = req->sysACPIState & ~acpi_state::stateChanged; 330 331 if (!acpi_state::isValidACPIState( 332 acpi_state::PowerStateType::sysPowerState, s)) 333 { 334 log<level::ERR>("set_acpi_power sys invalid input", 335 entry("S=%x", s)); 336 return IPMI_CC_PARM_OUT_OF_RANGE; 337 } 338 339 // valid input 340 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) 341 { 342 log<level::DEBUG>("No change for system power state"); 343 } 344 else 345 { 346 auto found = std::find_if( 347 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), 348 [&s](const auto& iter) { 349 return (static_cast<uint8_t>(iter.second) == s); 350 }); 351 352 value = found->first; 353 354 try 355 { 356 auto acpiObject = 357 ipmi::getDbusObject(bus, acpi_state::acpiInterface); 358 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, 359 acpi_state::acpiInterface, 360 acpi_state::sysACPIProp, 361 convertForMessage(value)); 362 } 363 catch (const InternalFailure& e) 364 { 365 log<level::ERR>("Failed in set ACPI system property", 366 entry("EXCEPTION=%s", e.what())); 367 return IPMI_CC_UNSPECIFIED_ERROR; 368 } 369 } 370 } 371 else 372 { 373 log<level::DEBUG>("Do not change system power state"); 374 } 375 376 if (req->devACPIState & acpi_state::stateChanged) 377 { 378 // set device power state 379 s = req->devACPIState & ~acpi_state::stateChanged; 380 if (!acpi_state::isValidACPIState( 381 acpi_state::PowerStateType::devPowerState, s)) 382 { 383 log<level::ERR>("set_acpi_power dev invalid input", 384 entry("S=%x", s)); 385 return IPMI_CC_PARM_OUT_OF_RANGE; 386 } 387 388 // valid input 389 if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) 390 { 391 log<level::DEBUG>("No change for device power state"); 392 } 393 else 394 { 395 auto found = std::find_if( 396 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), 397 [&s](const auto& iter) { 398 return (static_cast<uint8_t>(iter.second) == s); 399 }); 400 401 value = found->first; 402 403 try 404 { 405 auto acpiObject = 406 ipmi::getDbusObject(bus, acpi_state::acpiInterface); 407 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, 408 acpi_state::acpiInterface, 409 acpi_state::devACPIProp, 410 convertForMessage(value)); 411 } 412 catch (const InternalFailure& e) 413 { 414 log<level::ERR>("Failed in set ACPI device property", 415 entry("EXCEPTION=%s", e.what())); 416 return IPMI_CC_UNSPECIFIED_ERROR; 417 } 418 } 419 } 420 else 421 { 422 log<level::DEBUG>("Do not change device power state"); 423 } 424 425 return rc; 426 } 427 428 /** 429 * @brief implements the get ACPI power state command 430 * 431 * @return IPMI completion code plus response data on success. 432 * - ACPI system power state 433 * - ACPI device power state 434 **/ 435 ipmi::RspType<uint8_t, // acpiSystemPowerState 436 uint8_t // acpiDevicePowerState 437 > 438 ipmiGetAcpiPowerState() 439 { 440 uint8_t sysAcpiState; 441 uint8_t devAcpiState; 442 443 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 444 445 try 446 { 447 auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface); 448 449 auto sysACPIVal = ipmi::getDbusProperty( 450 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, 451 acpi_state::sysACPIProp); 452 auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString( 453 std::get<std::string>(sysACPIVal)); 454 sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI)); 455 456 auto devACPIVal = ipmi::getDbusProperty( 457 bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, 458 acpi_state::devACPIProp); 459 auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString( 460 std::get<std::string>(devACPIVal)); 461 devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI)); 462 } 463 catch (const InternalFailure& e) 464 { 465 return ipmi::responseUnspecifiedError(); 466 } 467 468 return ipmi::responseSuccess(sysAcpiState, devAcpiState); 469 } 470 471 typedef struct 472 { 473 char major; 474 char minor; 475 uint16_t d[2]; 476 } Revision; 477 478 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */ 479 /* return -1 if not in those formats, this routine knows how to parse */ 480 /* version = v0.6-19-gf363f61-dirty */ 481 /* ^ ^ ^^ ^ */ 482 /* | | |----------|-- additional details */ 483 /* | |---------------- Minor */ 484 /* |------------------ Major */ 485 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */ 486 /* ^ ^ ^^ ^ */ 487 /* | | |--|---------- additional details */ 488 /* | |---------------- Minor */ 489 /* |------------------ Major */ 490 /* Additional details : If the option group exists it will force Auxiliary */ 491 /* Firmware Revision Information 4th byte to 1 indicating the build was */ 492 /* derived with additional edits */ 493 int convertVersion(std::string s, Revision& rev) 494 { 495 std::string token; 496 uint16_t commits; 497 498 auto location = s.find_first_of('v'); 499 if (location != std::string::npos) 500 { 501 s = s.substr(location + 1); 502 } 503 504 if (!s.empty()) 505 { 506 location = s.find_first_of("."); 507 if (location != std::string::npos) 508 { 509 rev.major = 510 static_cast<char>(std::stoi(s.substr(0, location), 0, 16)); 511 token = s.substr(location + 1); 512 } 513 514 if (!token.empty()) 515 { 516 location = token.find_first_of(".-"); 517 if (location != std::string::npos) 518 { 519 rev.minor = static_cast<char>( 520 std::stoi(token.substr(0, location), 0, 16)); 521 token = token.substr(location + 1); 522 } 523 } 524 525 // Capture the number of commits on top of the minor tag. 526 // I'm using BE format like the ipmi spec asked for 527 location = token.find_first_of(".-"); 528 if (!token.empty()) 529 { 530 commits = std::stoi(token.substr(0, location), 0, 16); 531 rev.d[0] = (commits >> 8) | (commits << 8); 532 533 // commit number we skip 534 location = token.find_first_of(".-"); 535 if (location != std::string::npos) 536 { 537 token = token.substr(location + 1); 538 } 539 } 540 else 541 { 542 rev.d[0] = 0; 543 } 544 545 if (location != std::string::npos) 546 { 547 token = token.substr(location + 1); 548 } 549 550 // Any value of the optional parameter forces it to 1 551 location = token.find_first_of(".-"); 552 if (location != std::string::npos) 553 { 554 token = token.substr(location + 1); 555 } 556 commits = (!token.empty()) ? 1 : 0; 557 558 // We do this operation to get this displayed in least significant bytes 559 // of ipmitool device id command. 560 rev.d[1] = (commits >> 8) | (commits << 8); 561 } 562 563 return 0; 564 } 565 566 /* @brief: Implement the Get Device ID IPMI command per the IPMI spec 567 * @param[in] ctx - shared_ptr to an IPMI context struct 568 * 569 * @returns IPMI completion code plus response data 570 * - Device ID (manufacturer defined) 571 * - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit] 572 * - FW revision major[7 bits] (binary encoded); available[1 bit] 573 * - FW Revision minor (BCD encoded) 574 * - IPMI version (0x02 for IPMI 2.0) 575 * - device support (bitfield of supported options) 576 * - MFG IANA ID (3 bytes) 577 * - product ID (2 bytes) 578 * - AUX info (4 bytes) 579 */ 580 ipmi::RspType<uint8_t, // Device ID 581 uint8_t, // Device Revision 582 uint8_t, // Firmware Revision Major 583 uint8_t, // Firmware Revision minor 584 uint8_t, // IPMI version 585 uint8_t, // Additional device support 586 uint24_t, // MFG ID 587 uint16_t, // Product ID 588 uint32_t // AUX info 589 > 590 ipmiAppGetDeviceId(ipmi::Context::ptr ctx) 591 { 592 int r = -1; 593 Revision rev = {0}; 594 static struct 595 { 596 uint8_t id; 597 uint8_t revision; 598 uint8_t fw[2]; 599 uint8_t ipmiVer; 600 uint8_t addnDevSupport; 601 uint24_t manufId; 602 uint16_t prodId; 603 uint32_t aux; 604 } devId; 605 static bool dev_id_initialized = false; 606 static bool defaultActivationSetting = true; 607 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 608 constexpr auto ipmiDevIdStateShift = 7; 609 constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); 610 611 if (!dev_id_initialized) 612 { 613 try 614 { 615 auto version = getActiveSoftwareVersionInfo(ctx); 616 r = convertVersion(version, rev); 617 } 618 catch (const std::exception& e) 619 { 620 log<level::ERR>(e.what()); 621 } 622 623 if (r >= 0) 624 { 625 // bit7 identifies if the device is available 626 // 0=normal operation 627 // 1=device firmware, SDR update, 628 // or self-initialization in progress. 629 // The availability may change in run time, so mask here 630 // and initialize later. 631 devId.fw[0] = rev.major & ipmiDevIdFw1Mask; 632 633 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 634 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 635 std::memcpy(&devId.aux, rev.d, 4); 636 } 637 638 // IPMI Spec version 2.0 639 devId.ipmiVer = 2; 640 641 std::ifstream devIdFile(filename); 642 if (devIdFile.is_open()) 643 { 644 auto data = nlohmann::json::parse(devIdFile, nullptr, false); 645 if (!data.is_discarded()) 646 { 647 devId.id = data.value("id", 0); 648 devId.revision = data.value("revision", 0); 649 devId.addnDevSupport = data.value("addn_dev_support", 0); 650 devId.manufId = data.value("manuf_id", 0); 651 devId.prodId = data.value("prod_id", 0); 652 devId.aux = data.value("aux", 0); 653 654 // Set the availablitity of the BMC. 655 defaultActivationSetting = data.value("availability", true); 656 657 // Don't read the file every time if successful 658 dev_id_initialized = true; 659 } 660 else 661 { 662 log<level::ERR>("Device ID JSON parser failure"); 663 return ipmi::responseUnspecifiedError(); 664 } 665 } 666 else 667 { 668 log<level::ERR>("Device ID file not found"); 669 return ipmi::responseUnspecifiedError(); 670 } 671 } 672 673 // Set availability to the actual current BMC state 674 devId.fw[0] &= ipmiDevIdFw1Mask; 675 if (!getCurrentBmcStateWithFallback(defaultActivationSetting)) 676 { 677 devId.fw[0] |= (1 << ipmiDevIdStateShift); 678 } 679 680 return ipmi::responseSuccess( 681 devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer, 682 devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux); 683 } 684 685 auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t> 686 { 687 // Byte 2: 688 // 55h - No error. 689 // 56h - Self Test function not implemented in this controller. 690 // 57h - Corrupted or inaccesssible data or devices. 691 // 58h - Fatal hardware error. 692 // FFh - reserved. 693 // all other: Device-specific 'internal failure'. 694 // Byte 3: 695 // For byte 2 = 55h, 56h, FFh: 00h 696 // For byte 2 = 58h, all other: Device-specific 697 // For byte 2 = 57h: self-test error bitfield. 698 // Note: returning 57h does not imply that all test were run. 699 // [7] 1b = Cannot access SEL device. 700 // [6] 1b = Cannot access SDR Repository. 701 // [5] 1b = Cannot access BMC FRU device. 702 // [4] 1b = IPMB signal lines do not respond. 703 // [3] 1b = SDR Repository empty. 704 // [2] 1b = Internal Use Area of BMC FRU corrupted. 705 // [1] 1b = controller update 'boot block' firmware corrupted. 706 // [0] 1b = controller operational firmware corrupted. 707 constexpr uint8_t notImplemented = 0x56; 708 constexpr uint8_t zero = 0; 709 return ipmi::responseSuccess(notImplemented, zero); 710 } 711 712 static constexpr size_t uuidBinaryLength = 16; 713 static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122) 714 { 715 using Argument = xyz::openbmc_project::Common::InvalidArgument; 716 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 717 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 718 // order 719 // Ex: 0x2332fc2c40e66298e511f2782395a361 720 constexpr size_t uuidHexLength = (2 * uuidBinaryLength); 721 constexpr size_t uuidRfc4122Length = (uuidHexLength + 4); 722 std::array<uint8_t, uuidBinaryLength> uuid; 723 if (rfc4122.size() == uuidRfc4122Length) 724 { 725 rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'), 726 rfc4122.end()); 727 } 728 if (rfc4122.size() != uuidHexLength) 729 { 730 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 731 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 732 } 733 for (size_t ind = 0; ind < uuidHexLength; ind += 2) 734 { 735 char v[3]; 736 v[0] = rfc4122[ind]; 737 v[1] = rfc4122[ind + 1]; 738 v[2] = 0; 739 size_t err; 740 long b; 741 try 742 { 743 b = std::stoul(v, &err, 16); 744 } 745 catch (std::exception& e) 746 { 747 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 748 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 749 } 750 // check that exactly two ascii bytes were converted 751 if (err != 2) 752 { 753 elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), 754 Argument::ARGUMENT_VALUE(rfc4122.c_str())); 755 } 756 uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b); 757 } 758 return uuid; 759 } 760 761 auto ipmiAppGetDeviceGuid() 762 -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>> 763 { 764 // return a fixed GUID based on /etc/machine-id 765 // This should match the /redfish/v1/Managers/bmc's UUID data 766 767 // machine specific application ID (for BMC ID) 768 // generated by systemd-id128 -p new as per man page 769 static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE( 770 e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78); 771 772 sd_id128_t bmcUuid; 773 // create the UUID from /etc/machine-id via the systemd API 774 sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid); 775 776 char bmcUuidCstr[SD_ID128_STRING_MAX]; 777 std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr); 778 779 std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid); 780 return ipmi::responseSuccess(uuid); 781 } 782 783 auto ipmiAppGetBtCapabilities() 784 -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t> 785 { 786 // Per IPMI 2.0 spec, the input and output buffer size must be the max 787 // buffer size minus one byte to allocate space for the length byte. 788 constexpr uint8_t nrOutstanding = 0x01; 789 constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1; 790 constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1; 791 constexpr uint8_t transactionTime = 0x0A; 792 constexpr uint8_t nrRetries = 0x01; 793 794 return ipmi::responseSuccess(nrOutstanding, inputBufferSize, 795 outputBufferSize, transactionTime, nrRetries); 796 } 797 798 auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>> 799 { 800 static constexpr auto bmcInterface = 801 "xyz.openbmc_project.Inventory.Item.Bmc"; 802 static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID"; 803 static constexpr auto uuidProperty = "UUID"; 804 805 ipmi::Value propValue; 806 try 807 { 808 // Get the Inventory object implementing BMC interface 809 auto busPtr = getSdBus(); 810 auto objectInfo = ipmi::getDbusObject(*busPtr, bmcInterface); 811 812 // Read UUID property value from bmcObject 813 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 814 propValue = 815 ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first, 816 uuidInterface, uuidProperty); 817 } 818 catch (const InternalFailure& e) 819 { 820 log<level::ERR>("Failed in reading BMC UUID property", 821 entry("INTERFACE=%s", uuidInterface), 822 entry("PROPERTY=%s", uuidProperty)); 823 return ipmi::responseUnspecifiedError(); 824 } 825 std::array<uint8_t, 16> uuid; 826 std::string rfc4122Uuid = std::get<std::string>(propValue); 827 try 828 { 829 // convert to IPMI format 830 uuid = rfc4122ToIpmi(rfc4122Uuid); 831 } 832 catch (const InvalidArgument& e) 833 { 834 log<level::ERR>("Failed in parsing BMC UUID property", 835 entry("INTERFACE=%s", uuidInterface), 836 entry("PROPERTY=%s", uuidProperty), 837 entry("VALUE=%s", rfc4122Uuid.c_str())); 838 return ipmi::responseUnspecifiedError(); 839 } 840 return ipmi::responseSuccess(uuid); 841 } 842 843 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore; 844 845 static std::string sysInfoReadSystemName() 846 { 847 // Use the BMC hostname as the "System Name." 848 char hostname[HOST_NAME_MAX + 1] = {}; 849 if (gethostname(hostname, HOST_NAME_MAX) != 0) 850 { 851 perror("System info parameter: system name"); 852 } 853 return hostname; 854 } 855 856 struct IpmiSysInfoResp 857 { 858 uint8_t paramRevision; 859 uint8_t setSelector; 860 union 861 { 862 struct 863 { 864 uint8_t encoding; 865 uint8_t stringLen; 866 uint8_t stringData0[14]; 867 } __attribute__((packed)); 868 uint8_t stringDataN[16]; 869 uint8_t byteData; 870 }; 871 } __attribute__((packed)); 872 873 /** 874 * Split a string into (up to) 16-byte chunks as expected in response for get 875 * system info parameter. 876 * 877 * @param[in] fullString: Input string to be split 878 * @param[in] chunkIndex: Index of the chunk to be written out 879 * @param[in,out] chunk: Output data buffer; must have 14 byte capacity if 880 * chunk_index = 0 and 16-byte capacity otherwise 881 * @return the number of bytes written into the output buffer, or -EINVAL for 882 * invalid arguments. 883 */ 884 static int splitStringParam(const std::string& fullString, int chunkIndex, 885 uint8_t* chunk) 886 { 887 constexpr int maxChunk = 255; 888 constexpr int smallChunk = 14; 889 constexpr int chunkSize = 16; 890 if (chunkIndex > maxChunk || chunk == nullptr) 891 { 892 return -EINVAL; 893 } 894 try 895 { 896 std::string output; 897 if (chunkIndex == 0) 898 { 899 // Output must have 14 byte capacity. 900 output = fullString.substr(0, smallChunk); 901 } 902 else 903 { 904 // Output must have 16 byte capacity. 905 output = fullString.substr((chunkIndex * chunkSize) - 2, chunkSize); 906 } 907 908 std::memcpy(chunk, output.c_str(), output.length()); 909 return output.length(); 910 } 911 catch (const std::out_of_range& e) 912 { 913 // The position was beyond the end. 914 return -EINVAL; 915 } 916 } 917 918 /** 919 * Packs the Get Sys Info Request Item into the response. 920 * 921 * @param[in] paramString - the parameter. 922 * @param[in] setSelector - the selector 923 * @param[in,out] resp - the System info response. 924 * @return The number of bytes packed or failure from splitStringParam(). 925 */ 926 static int packGetSysInfoResp(const std::string& paramString, 927 uint8_t setSelector, IpmiSysInfoResp* resp) 928 { 929 uint8_t* dataBuffer = resp->stringDataN; 930 resp->setSelector = setSelector; 931 if (resp->setSelector == 0) // First chunk has only 14 bytes. 932 { 933 resp->encoding = 0; 934 resp->stringLen = paramString.length(); 935 dataBuffer = resp->stringData0; 936 } 937 return splitStringParam(paramString, resp->setSelector, dataBuffer); 938 } 939 940 ipmi_ret_t ipmi_app_get_system_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 941 ipmi_request_t request, 942 ipmi_response_t response, 943 ipmi_data_len_t dataLen, 944 ipmi_context_t context) 945 { 946 IpmiSysInfoResp resp = {}; 947 size_t respLen = 0; 948 uint8_t* const reqData = static_cast<uint8_t*>(request); 949 std::string paramString; 950 bool found; 951 std::tuple<bool, std::string> ret; 952 constexpr int minRequestSize = 4; 953 constexpr int paramSelector = 1; 954 constexpr uint8_t revisionOnly = 0x80; 955 const uint8_t paramRequested = reqData[paramSelector]; 956 int rc; 957 958 if (*dataLen < minRequestSize) 959 { 960 return IPMI_CC_REQ_DATA_LEN_INVALID; 961 } 962 963 *dataLen = 0; // default to 0. 964 965 // Parameters revision as of IPMI spec v2.0 rev. 1.1 (Feb 11, 2014 E6) 966 resp.paramRevision = 0x11; 967 if (reqData[0] & revisionOnly) // Get parameter revision only 968 { 969 respLen = 1; 970 goto writeResponse; 971 } 972 973 // The "Set In Progress" parameter can be used for rollback of parameter 974 // data and is not implemented. 975 if (paramRequested == 0) 976 { 977 resp.byteData = 0; 978 respLen = 2; 979 goto writeResponse; 980 } 981 982 if (sysInfoParamStore == nullptr) 983 { 984 sysInfoParamStore = std::make_unique<SysInfoParamStore>(); 985 sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, 986 sysInfoReadSystemName); 987 } 988 989 // Parameters other than Set In Progress are assumed to be strings. 990 ret = sysInfoParamStore->lookup(paramRequested); 991 found = std::get<0>(ret); 992 paramString = std::get<1>(ret); 993 if (!found) 994 { 995 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; 996 } 997 // TODO: Cache each parameter across multiple calls, until the whole string 998 // has been read out. Otherwise, it's possible for a parameter to change 999 // between requests for its chunks, returning chunks incoherent with each 1000 // other. For now, the parameter store is simply required to have only 1001 // idempotent callbacks. 1002 rc = packGetSysInfoResp(paramString, reqData[2], &resp); 1003 if (rc == -EINVAL) 1004 { 1005 return IPMI_CC_RESPONSE_ERROR; 1006 } 1007 1008 respLen = sizeof(resp); // Write entire string data chunk in response. 1009 1010 writeResponse: 1011 std::memcpy(response, &resp, sizeof(resp)); 1012 *dataLen = respLen; 1013 return IPMI_CC_OK; 1014 } 1015 1016 inline std::vector<uint8_t> convertStringToData(const std::string& command) 1017 { 1018 std::istringstream iss(command); 1019 std::string token; 1020 std::vector<uint8_t> dataValue; 1021 while (std::getline(iss, token, ' ')) 1022 { 1023 dataValue.emplace_back( 1024 static_cast<uint8_t>(std::stoul(token, nullptr, base_16))); 1025 } 1026 return dataValue; 1027 } 1028 1029 static bool populateI2CMasterWRWhitelist() 1030 { 1031 nlohmann::json data = nullptr; 1032 std::ifstream jsonFile(i2cMasterWRWhitelistFile); 1033 1034 if (!jsonFile.good()) 1035 { 1036 log<level::WARNING>("i2c white list file not found!", 1037 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); 1038 return false; 1039 } 1040 1041 try 1042 { 1043 data = nlohmann::json::parse(jsonFile, nullptr, false); 1044 } 1045 catch (nlohmann::json::parse_error& e) 1046 { 1047 log<level::ERR>("Corrupted i2c white list config file", 1048 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile), 1049 entry("MSG: %s", e.what())); 1050 return false; 1051 } 1052 1053 try 1054 { 1055 // Example JSON Structure format 1056 // "filters": [ 1057 // { 1058 // "Description": "Allow full read - ignore first byte write value 1059 // for 0x40 to 0x4F", 1060 // "busId": "0x01", 1061 // "slaveAddr": "0x40", 1062 // "slaveAddrMask": "0x0F", 1063 // "command": "0x00", 1064 // "commandMask": "0xFF" 1065 // }, 1066 // { 1067 // "Description": "Allow full read - first byte match 0x05 and 1068 // ignore second byte", 1069 // "busId": "0x01", 1070 // "slaveAddr": "0x57", 1071 // "slaveAddrMask": "0x00", 1072 // "command": "0x05 0x00", 1073 // "commandMask": "0x00 0xFF" 1074 // },] 1075 1076 nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); 1077 std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist(); 1078 for (const auto& it : filters.items()) 1079 { 1080 nlohmann::json filter = it.value(); 1081 if (filter.is_null()) 1082 { 1083 log<level::ERR>( 1084 "Corrupted I2C master write read whitelist config file", 1085 entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); 1086 return false; 1087 } 1088 const std::vector<uint8_t>& writeData = 1089 convertStringToData(filter[cmdStr].get<std::string>()); 1090 const std::vector<uint8_t>& writeDataMask = 1091 convertStringToData(filter[cmdMaskStr].get<std::string>()); 1092 if (writeDataMask.size() != writeData.size()) 1093 { 1094 log<level::ERR>("I2C master write read whitelist filter " 1095 "mismatch for command & mask size"); 1096 return false; 1097 } 1098 whitelist.push_back( 1099 {static_cast<uint8_t>(std::stoul( 1100 filter[busIdStr].get<std::string>(), nullptr, base_16)), 1101 static_cast<uint8_t>( 1102 std::stoul(filter[slaveAddrStr].get<std::string>(), 1103 nullptr, base_16)), 1104 static_cast<uint8_t>( 1105 std::stoul(filter[slaveAddrMaskStr].get<std::string>(), 1106 nullptr, base_16)), 1107 writeData, writeDataMask}); 1108 } 1109 if (whitelist.size() != filters.size()) 1110 { 1111 log<level::ERR>( 1112 "I2C master write read whitelist filter size mismatch"); 1113 return false; 1114 } 1115 } 1116 catch (std::exception& e) 1117 { 1118 log<level::ERR>("I2C master write read whitelist unexpected exception", 1119 entry("ERROR=%s", e.what())); 1120 return false; 1121 } 1122 return true; 1123 } 1124 1125 static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data, 1126 const std::vector<uint8_t>& dataMask, 1127 const std::vector<uint8_t>& writeData) 1128 { 1129 std::vector<uint8_t> processedDataBuf(data.size()); 1130 std::vector<uint8_t> processedReqBuf(dataMask.size()); 1131 std::transform(writeData.begin(), writeData.end(), dataMask.begin(), 1132 processedReqBuf.begin(), std::bit_or<uint8_t>()); 1133 std::transform(data.begin(), data.end(), dataMask.begin(), 1134 processedDataBuf.begin(), std::bit_or<uint8_t>()); 1135 1136 return (processedDataBuf == processedReqBuf); 1137 } 1138 1139 static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr, 1140 std::vector<uint8_t>& writeData) 1141 { 1142 std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist(); 1143 for (const auto& wlEntry : whiteList) 1144 { 1145 if ((busId == wlEntry.busId) && 1146 ((slaveAddr | wlEntry.slaveAddrMask) == 1147 (wlEntry.slaveAddr | wlEntry.slaveAddrMask))) 1148 { 1149 const std::vector<uint8_t>& dataMask = wlEntry.dataMask; 1150 // Skip as no-match, if requested write data is more than the 1151 // write data mask size 1152 if (writeData.size() > dataMask.size()) 1153 { 1154 continue; 1155 } 1156 if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData)) 1157 { 1158 return true; 1159 } 1160 } 1161 } 1162 return false; 1163 } 1164 1165 /** @brief implements master write read IPMI command which can be used for 1166 * low-level I2C/SMBus write, read or write-read access 1167 * @param isPrivateBus -to indicate private bus usage 1168 * @param busId - bus id 1169 * @param channelNum - channel number 1170 * @param reserved - skip 1 bit 1171 * @param slaveAddr - slave address 1172 * @param read count - number of bytes to be read 1173 * @param writeData - data to be written 1174 * 1175 * @returns IPMI completion code plus response data 1176 * - readData - i2c response data 1177 */ 1178 ipmi::RspType<std::vector<uint8_t>> 1179 ipmiMasterWriteRead(bool isPrivateBus, uint3_t busId, uint4_t channelNum, 1180 bool reserved, uint7_t slaveAddr, uint8_t readCount, 1181 std::vector<uint8_t> writeData) 1182 { 1183 i2c_rdwr_ioctl_data msgReadWrite = {0}; 1184 i2c_msg i2cmsg[2] = {0}; 1185 1186 if (readCount > maxIPMIWriteReadSize) 1187 { 1188 log<level::ERR>("Master write read command: Read count exceeds limit"); 1189 return ipmi::responseParmOutOfRange(); 1190 } 1191 const size_t writeCount = writeData.size(); 1192 if (!readCount && !writeCount) 1193 { 1194 log<level::ERR>("Master write read command: Read & write count are 0"); 1195 return ipmi::responseInvalidFieldRequest(); 1196 } 1197 if (!isCmdWhitelisted(static_cast<uint8_t>(busId), 1198 static_cast<uint8_t>(slaveAddr), writeData)) 1199 { 1200 log<level::ERR>("Master write read request blocked!", 1201 entry("BUS=%d", static_cast<uint8_t>(busId)), 1202 entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr))); 1203 return ipmi::responseInvalidFieldRequest(); 1204 } 1205 std::vector<uint8_t> readBuf(readCount); 1206 std::string i2cBus = 1207 "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId)); 1208 1209 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 1210 if (i2cDev < 0) 1211 { 1212 log<level::ERR>("Failed to open i2c bus", 1213 entry("BUS=%s", i2cBus.c_str())); 1214 return ipmi::responseInvalidFieldRequest(); 1215 } 1216 1217 int msgCount = 0; 1218 if (writeCount) 1219 { 1220 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr); 1221 i2cmsg[msgCount].flags = 0x00; 1222 i2cmsg[msgCount].len = writeCount; 1223 i2cmsg[msgCount].buf = writeData.data(); 1224 msgCount++; 1225 } 1226 if (readCount) 1227 { 1228 i2cmsg[msgCount].addr = static_cast<uint8_t>(slaveAddr); 1229 i2cmsg[msgCount].flags = I2C_M_RD; 1230 i2cmsg[msgCount].len = readCount; 1231 i2cmsg[msgCount].buf = readBuf.data(); 1232 msgCount++; 1233 } 1234 1235 msgReadWrite.msgs = i2cmsg; 1236 msgReadWrite.nmsgs = msgCount; 1237 1238 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); 1239 ::close(i2cDev); 1240 1241 if (ret < 0) 1242 { 1243 log<level::ERR>("Master write read: Failed", entry("RET=%d", ret)); 1244 return ipmi::responseUnspecifiedError(); 1245 } 1246 if (readCount) 1247 { 1248 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); 1249 } 1250 return ipmi::responseSuccess(readBuf); 1251 } 1252 1253 void register_netfn_app_functions() 1254 { 1255 // <Get Device ID> 1256 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1257 ipmi::app::cmdGetDeviceId, ipmi::Privilege::User, 1258 ipmiAppGetDeviceId); 1259 1260 // <Get BT Interface Capabilities> 1261 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1262 ipmi::app::cmdGetBtIfaceCapabilities, 1263 ipmi::Privilege::User, ipmiAppGetBtCapabilities); 1264 1265 // <Reset Watchdog Timer> 1266 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1267 ipmi::app::cmdResetWatchdogTimer, 1268 ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer); 1269 1270 // <Set Watchdog Timer> 1271 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL, 1272 ipmi_app_watchdog_set, PRIVILEGE_OPERATOR); 1273 1274 // <Get Watchdog Timer> 1275 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL, 1276 ipmi_app_watchdog_get, PRIVILEGE_OPERATOR); 1277 1278 // <Get Self Test Results> 1279 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1280 ipmi::app::cmdGetSelfTestResults, 1281 ipmi::Privilege::User, ipmiAppGetSelfTestResults); 1282 1283 // <Get Device GUID> 1284 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1285 ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User, 1286 ipmiAppGetDeviceGuid); 1287 1288 // <Set ACPI Power State> 1289 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, 1290 ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN); 1291 1292 // <Get ACPI Power State> 1293 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1294 ipmi::app::cmdGetAcpiPowerState, 1295 ipmi::Privilege::Admin, ipmiGetAcpiPowerState); 1296 1297 // Note: For security reason, this command will be registered only when 1298 // there are proper I2C Master write read whitelist 1299 if (populateI2CMasterWRWhitelist()) 1300 { 1301 // Note: For security reasons, registering master write read as admin 1302 // privilege command, even though IPMI 2.0 specification allows it as 1303 // operator privilege. 1304 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1305 ipmi::app::cmdMasterWriteRead, 1306 ipmi::Privilege::Admin, ipmiMasterWriteRead); 1307 } 1308 1309 // <Get System GUID Command> 1310 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, 1311 ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, 1312 ipmiAppGetSystemGuid); 1313 1314 // <Get Channel Cipher Suites Command> 1315 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL, 1316 getChannelCipherSuites, PRIVILEGE_CALLBACK); 1317 1318 // <Get System Info Command> 1319 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYSTEM_INFO, NULL, 1320 ipmi_app_get_system_info, PRIVILEGE_USER); 1321 return; 1322 } 1323