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