1 #include "config.h" 2 3 #include "chassishandler.hpp" 4 5 #include <arpa/inet.h> 6 #include <endian.h> 7 #include <limits.h> 8 #include <mapper.h> 9 #include <netinet/in.h> 10 11 #include <array> 12 #include <chrono> 13 #include <cstring> 14 #include <filesystem> 15 #include <fstream> 16 #include <future> 17 #include <ipmid/api.hpp> 18 #include <ipmid/types.hpp> 19 #include <ipmid/utils.hpp> 20 #include <map> 21 #include <phosphor-logging/elog-errors.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/bus.hpp> 24 #include <sdbusplus/message/types.hpp> 25 #include <sdbusplus/server/object.hpp> 26 #include <sdbusplus/timer.hpp> 27 #include <settings.hpp> 28 #include <sstream> 29 #include <string> 30 #include <xyz/openbmc_project/Common/error.hpp> 31 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> 32 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> 33 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> 34 #include <xyz/openbmc_project/State/Host/server.hpp> 35 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> 36 37 // Defines 38 #define SET_PARM_VERSION 0x01 39 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 40 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80 41 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 42 43 std::unique_ptr<phosphor::Timer> identifyTimer 44 __attribute__((init_priority(101))); 45 46 static ChassisIDState chassisIDState = ChassisIDState::reserved; 47 48 constexpr size_t SIZE_MAC = 18; 49 constexpr size_t SIZE_BOOT_OPTION = (uint8_t) 50 BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot 51 // option parametrs 52 constexpr size_t SIZE_PREFIX = 7; 53 constexpr size_t MAX_PREFIX_VALUE = 32; 54 constexpr size_t SIZE_COOKIE = 4; 55 constexpr size_t SIZE_VERSION = 2; 56 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15; 57 58 // PetiBoot-Specific 59 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62, 60 0x21, 0x00, 0x01, 0x06}; 61 62 static constexpr size_t COOKIE_OFFSET = 1; 63 static constexpr size_t VERSION_OFFSET = 5; 64 static constexpr size_t ADDR_SIZE_OFFSET = 8; 65 static constexpr size_t MAC_OFFSET = 9; 66 static constexpr size_t ADDRTYPE_OFFSET = 16; 67 static constexpr size_t IPADDR_OFFSET = 17; 68 69 static constexpr size_t encIdentifyObjectsSize = 1; 70 static constexpr size_t chassisIdentifyReqLength = 2; 71 static constexpr size_t identifyIntervalPos = 0; 72 static constexpr size_t forceIdentifyPos = 1; 73 74 void register_netfn_chassis_functions() __attribute__((constructor)); 75 76 // Host settings in dbus 77 // Service name should be referenced by connection name got via object mapper 78 const char* settings_object_name = "/org/openbmc/settings/host0"; 79 const char* settings_intf_name = "org.freedesktop.DBus.Properties"; 80 const char* identify_led_object_name = 81 "/xyz/openbmc_project/led/groups/enclosure_identify"; 82 83 constexpr auto SETTINGS_ROOT = "/"; 84 constexpr auto SETTINGS_MATCH = "host0"; 85 86 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; 87 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; 88 89 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state"; 90 static constexpr auto chassisPOHStateIntf = 91 "xyz.openbmc_project.State.PowerOnHours"; 92 static constexpr auto pOHCounterProperty = "POHCounter"; 93 static constexpr auto match = "chassis0"; 94 const static constexpr char chassisCapIntf[] = 95 "xyz.openbmc_project.Control.ChassisCapabilities"; 96 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags"; 97 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress"; 98 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress"; 99 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress"; 100 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; 101 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; 102 static constexpr uint8_t chassisCapFlagMask = 0x0f; 103 static constexpr uint8_t chassisCapAddrMask = 0xfe; 104 static constexpr const char* powerButtonIntf = 105 "xyz.openbmc_project.Chassis.Buttons.Power"; 106 static constexpr const char* powerButtonPath = 107 "/xyz/openbmc_project/Chassis/Buttons/Power0"; 108 static constexpr const char* resetButtonIntf = 109 "xyz.openbmc_project.Chassis.Buttons.Reset"; 110 static constexpr const char* resetButtonPath = 111 "/xyz/openbmc_project/Chassis/Buttons/Reset0"; 112 113 typedef struct 114 { 115 uint8_t cap_flags; 116 uint8_t fru_info_dev_addr; 117 uint8_t sdr_dev_addr; 118 uint8_t sel_dev_addr; 119 uint8_t system_management_dev_addr; 120 uint8_t bridge_dev_addr; 121 } __attribute__((packed)) ipmi_chassis_cap_t; 122 123 typedef struct 124 { 125 uint8_t cur_power_state; 126 uint8_t last_power_event; 127 uint8_t misc_power_state; 128 uint8_t front_panel_button_cap_status; 129 } __attribute__((packed)) ipmi_get_chassis_status_t; 130 131 // Phosphor Host State manager 132 namespace State = sdbusplus::xyz::openbmc_project::State::server; 133 134 namespace fs = std::filesystem; 135 136 using namespace phosphor::logging; 137 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 138 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 139 140 namespace chassis 141 { 142 namespace internal 143 { 144 145 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; 146 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source"; 147 constexpr auto powerRestoreIntf = 148 "xyz.openbmc_project.Control.Power.RestorePolicy"; 149 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 150 151 namespace cache 152 { 153 154 std::unique_ptr<settings::Objects> objectsPtr = nullptr; 155 156 settings::Objects& getObjects() 157 { 158 if (objectsPtr == nullptr) 159 { 160 objectsPtr = std::make_unique<settings::Objects>( 161 dbus, std::vector<std::string>{bootModeIntf, bootSourceIntf, 162 powerRestoreIntf}); 163 } 164 return *objectsPtr; 165 } 166 167 } // namespace cache 168 } // namespace internal 169 } // namespace chassis 170 171 namespace poh 172 { 173 174 constexpr auto minutesPerCount = 60; 175 176 } // namespace poh 177 178 struct get_sys_boot_options_t 179 { 180 uint8_t parameter; 181 uint8_t set; 182 uint8_t block; 183 } __attribute__((packed)); 184 185 struct get_sys_boot_options_response_t 186 { 187 uint8_t version; 188 uint8_t parm; 189 uint8_t data[SIZE_BOOT_OPTION]; 190 } __attribute__((packed)); 191 192 struct set_sys_boot_options_t 193 { 194 uint8_t parameter; 195 uint8_t data[SIZE_BOOT_OPTION]; 196 } __attribute__((packed)); 197 198 int getHostNetworkData(get_sys_boot_options_response_t* respptr) 199 { 200 ipmi::PropertyMap properties; 201 int rc = 0; 202 uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE; 203 204 try 205 { 206 // TODO There may be cases where an interface is implemented by multiple 207 // objects,to handle such cases we are interested on that object 208 // which are on interested busname. 209 // Currenlty mapper doesn't give the readable busname(gives busid) 210 // so we can't match with bus name so giving some object specific info 211 // as SETTINGS_MATCH. 212 // Later SETTINGS_MATCH will be replaced with busname. 213 214 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 215 216 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 217 SETTINGS_ROOT, SETTINGS_MATCH); 218 219 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 220 SETTINGS_ROOT, SETTINGS_MATCH); 221 222 properties = ipmi::getAllDbusProperties( 223 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE); 224 auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second, 225 macObjectInfo.first, MAC_INTERFACE, 226 "MACAddress"); 227 228 auto ipAddress = std::get<std::string>(properties["Address"]); 229 230 auto gateway = std::get<std::string>(properties["Gateway"]); 231 232 auto prefix = std::get<uint8_t>(properties["PrefixLength"]); 233 234 uint8_t isStatic = 235 (std::get<std::string>(properties["Origin"]) == 236 "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 237 ? 1 238 : 0; 239 240 auto MACAddress = std::get<std::string>(variant); 241 242 // it is expected here that we should get the valid data 243 // but we may also get the default values. 244 // Validation of the data is done by settings. 245 // 246 // if mac address is default mac address then 247 // don't send blank override. 248 if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS)) 249 { 250 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 251 rc = -1; 252 return rc; 253 } 254 // if addr is static then ipaddress,gateway,prefix 255 // should not be default one,don't send blank override. 256 if (isStatic) 257 { 258 if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) || 259 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix)) 260 { 261 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 262 rc = -1; 263 return rc; 264 } 265 } 266 267 sscanf( 268 MACAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, 269 (respptr->data + MAC_OFFSET), (respptr->data + MAC_OFFSET + 1), 270 (respptr->data + MAC_OFFSET + 2), (respptr->data + MAC_OFFSET + 3), 271 (respptr->data + MAC_OFFSET + 4), (respptr->data + MAC_OFFSET + 5)); 272 273 respptr->data[MAC_OFFSET + 6] = 0x00; 274 275 std::memcpy(respptr->data + ADDRTYPE_OFFSET, &isStatic, 276 sizeof(isStatic)); 277 278 uint8_t addressFamily = (std::get<std::string>(properties["Type"]) == 279 "xyz.openbmc_project.Network.IP.Protocol.IPv4") 280 ? AF_INET 281 : AF_INET6; 282 283 addrSize = (addressFamily == AF_INET) 284 ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE 285 : ipmi::network::IPV6_ADDRESS_SIZE_BYTE; 286 287 // ipaddress and gateway would be in IPv4 format 288 inet_pton(addressFamily, ipAddress.c_str(), 289 (respptr->data + IPADDR_OFFSET)); 290 291 uint8_t prefixOffset = IPADDR_OFFSET + addrSize; 292 293 std::memcpy(respptr->data + prefixOffset, &prefix, sizeof(prefix)); 294 295 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); 296 297 inet_pton(addressFamily, gateway.c_str(), 298 (respptr->data + gatewayOffset)); 299 } 300 catch (InternalFailure& e) 301 { 302 commit<InternalFailure>(); 303 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 304 rc = -1; 305 return rc; 306 } 307 308 // PetiBoot-Specific 309 // If success then copy the first 9 bytes to the data 310 std::memcpy(respptr->data, net_conf_initial_bytes, 311 sizeof(net_conf_initial_bytes)); 312 313 std::memcpy(respptr->data + ADDR_SIZE_OFFSET, &addrSize, sizeof(addrSize)); 314 315 #ifdef _IPMI_DEBUG_ 316 std::printf("\n===Printing the IPMI Formatted Data========\n"); 317 318 for (uint8_t pos = 0; pos < index; pos++) 319 { 320 std::printf("%02x ", respptr->data[pos]); 321 } 322 #endif 323 324 return rc; 325 } 326 327 /** @brief convert IPv4 and IPv6 addresses from binary to text form. 328 * @param[in] family - IPv4/Ipv6 329 * @param[in] data - req data pointer. 330 * @param[in] offset - offset in the data. 331 * @param[in] addrSize - size of the data which needs to be read from offset. 332 * @returns address in text form. 333 */ 334 335 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset, 336 uint8_t addrSize) 337 { 338 char ipAddr[INET6_ADDRSTRLEN] = {}; 339 340 switch (family) 341 { 342 case AF_INET: 343 { 344 struct sockaddr_in addr4 345 { 346 }; 347 std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize); 348 349 inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN); 350 351 break; 352 } 353 case AF_INET6: 354 { 355 struct sockaddr_in6 addr6 356 { 357 }; 358 std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize); 359 360 inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN); 361 362 break; 363 } 364 default: 365 { 366 return {}; 367 } 368 } 369 370 return ipAddr; 371 } 372 373 int setHostNetworkData(set_sys_boot_options_t* reqptr) 374 { 375 using namespace std::string_literals; 376 std::string host_network_config; 377 char mac[]{"00:00:00:00:00:00"}; 378 std::string ipAddress, gateway; 379 char addrOrigin{0}; 380 uint8_t addrSize{0}; 381 std::string addressOrigin = 382 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 383 std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; 384 uint8_t prefix{0}; 385 uint32_t zeroCookie = 0; 386 uint8_t family = AF_INET; 387 388 // cookie starts from second byte 389 // version starts from sixth byte 390 391 try 392 { 393 do 394 { 395 // cookie == 0x21 0x70 0x62 0x21 396 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), 397 (net_conf_initial_bytes + COOKIE_OFFSET), 398 SIZE_COOKIE) != 0) 399 { 400 // cookie == 0 401 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), &zeroCookie, 402 SIZE_COOKIE) == 0) 403 { 404 // need to zero out the network settings. 405 break; 406 } 407 408 log<level::ERR>("Invalid Cookie"); 409 elog<InternalFailure>(); 410 } 411 412 // vesion == 0x00 0x01 413 if (memcmp(&(reqptr->data[VERSION_OFFSET]), 414 (net_conf_initial_bytes + VERSION_OFFSET), 415 SIZE_VERSION) != 0) 416 { 417 418 log<level::ERR>("Invalid Version"); 419 elog<InternalFailure>(); 420 } 421 422 std::snprintf( 423 mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT, 424 reqptr->data[MAC_OFFSET], reqptr->data[MAC_OFFSET + 1], 425 reqptr->data[MAC_OFFSET + 2], reqptr->data[MAC_OFFSET + 3], 426 reqptr->data[MAC_OFFSET + 4], reqptr->data[MAC_OFFSET + 5]); 427 428 std::memcpy(&addrOrigin, &(reqptr->data[ADDRTYPE_OFFSET]), 429 sizeof(decltype(addrOrigin))); 430 431 if (addrOrigin) 432 { 433 addressOrigin = 434 "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 435 } 436 437 // Get the address size 438 std::memcpy(&addrSize, &reqptr->data[ADDR_SIZE_OFFSET], 439 sizeof(addrSize)); 440 441 uint8_t prefixOffset = IPADDR_OFFSET + addrSize; 442 443 std::memcpy(&prefix, &(reqptr->data[prefixOffset]), 444 sizeof(decltype(prefix))); 445 446 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); 447 448 if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE) 449 { 450 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; 451 family = AF_INET6; 452 } 453 454 ipAddress = 455 getAddrStr(family, reqptr->data, IPADDR_OFFSET, addrSize); 456 457 gateway = getAddrStr(family, reqptr->data, gatewayOffset, addrSize); 458 459 } while (0); 460 461 // Cookie == 0 or it is a valid cookie 462 host_network_config += "ipaddress="s + ipAddress + ",prefix="s + 463 std::to_string(prefix) + ",gateway="s + gateway + 464 ",mac="s + mac + ",addressOrigin="s + 465 addressOrigin; 466 467 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 468 469 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 470 SETTINGS_ROOT, SETTINGS_MATCH); 471 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 472 SETTINGS_ROOT, SETTINGS_MATCH); 473 // set the dbus property 474 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 475 IP_INTERFACE, "Address", std::string(ipAddress)); 476 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 477 IP_INTERFACE, "PrefixLength", prefix); 478 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 479 IP_INTERFACE, "Origin", addressOrigin); 480 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 481 IP_INTERFACE, "Gateway", std::string(gateway)); 482 ipmi::setDbusProperty( 483 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type", 484 std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4")); 485 ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first, 486 MAC_INTERFACE, "MACAddress", std::string(mac)); 487 488 log<level::DEBUG>( 489 "Network configuration changed", 490 entry("NETWORKCONFIG=%s", host_network_config.c_str())); 491 } 492 catch (InternalFailure& e) 493 { 494 commit<InternalFailure>(); 495 return -1; 496 } 497 498 return 0; 499 } 500 501 uint32_t getPOHCounter() 502 { 503 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 504 505 auto chassisStateObj = 506 ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match); 507 508 auto service = 509 ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first); 510 511 auto propValue = 512 ipmi::getDbusProperty(bus, service, chassisStateObj.first, 513 chassisPOHStateIntf, pOHCounterProperty); 514 515 return std::get<uint32_t>(propValue); 516 } 517 518 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 519 ipmi_request_t request, 520 ipmi_response_t response, 521 ipmi_data_len_t data_len, 522 ipmi_context_t context) 523 { 524 // Status code. 525 ipmi_ret_t rc = IPMI_CC_INVALID; 526 *data_len = 0; 527 return rc; 528 } 529 530 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 531 ipmi_request_t request, 532 ipmi_response_t response, 533 ipmi_data_len_t data_len, 534 ipmi_context_t context) 535 { 536 // sd_bus error 537 ipmi_ret_t rc = IPMI_CC_OK; 538 539 ipmi_chassis_cap_t chassis_cap{}; 540 541 if (*data_len != 0) 542 { 543 return IPMI_CC_REQ_DATA_LEN_INVALID; 544 } 545 546 *data_len = sizeof(ipmi_chassis_cap_t); 547 548 try 549 { 550 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 551 552 ipmi::DbusObjectInfo chassisCapObject = 553 ipmi::getDbusObject(bus, chassisCapIntf); 554 555 // capabilities flags 556 // [7..4] - reserved 557 // [3] – 1b = provides power interlock (IPM 1.5) 558 // [2] – 1b = provides Diagnostic Interrupt (FP NMI) 559 // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis 560 // has capabilities 561 // to lock out external power control and reset button or 562 // front panel interfaces and/or detect tampering with those 563 // interfaces). 564 // [0] -1b = Chassis provides intrusion (physical security) sensor. 565 // set to default value 0x0. 566 ipmi::Value variant = ipmi::getDbusProperty( 567 bus, chassisCapObject.second, chassisCapObject.first, 568 chassisCapIntf, chassisCapFlagsProp); 569 chassis_cap.cap_flags = std::get<uint8_t>(variant); 570 571 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 572 chassisCapObject.first, chassisCapIntf, 573 chassisFRUDevAddrProp); 574 // Chassis FRU info Device Address. 575 chassis_cap.fru_info_dev_addr = std::get<uint8_t>(variant); 576 577 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 578 chassisCapObject.first, chassisCapIntf, 579 chassisSDRDevAddrProp); 580 // Chassis SDR Device Address. 581 chassis_cap.sdr_dev_addr = std::get<uint8_t>(variant); 582 583 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 584 chassisCapObject.first, chassisCapIntf, 585 chassisSELDevAddrProp); 586 // Chassis SEL Device Address. 587 chassis_cap.sel_dev_addr = std::get<uint8_t>(variant); 588 589 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 590 chassisCapObject.first, chassisCapIntf, 591 chassisSMDevAddrProp); 592 // Chassis System Management Device Address. 593 chassis_cap.system_management_dev_addr = std::get<uint8_t>(variant); 594 595 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 596 chassisCapObject.first, chassisCapIntf, 597 chassisBridgeDevAddrProp); 598 // Chassis Bridge Device Address. 599 chassis_cap.bridge_dev_addr = std::get<uint8_t>(variant); 600 uint8_t* respP = reinterpret_cast<uint8_t*>(response); 601 uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap); 602 std::copy(chassisP, chassisP + *data_len, respP); 603 } 604 catch (std::exception& e) 605 { 606 log<level::ERR>(e.what()); 607 rc = IPMI_CC_UNSPECIFIED_ERROR; 608 *data_len = 0; 609 return rc; 610 } 611 612 return rc; 613 } 614 615 /** @brief implements set chassis capalibities command 616 * @param intrusion - chassis intrusion 617 * @param fpLockout - frontpannel lockout 618 * @param reserved1 - skip one bit 619 * @param fruDeviceAddr - chassis FRU info Device Address 620 * @param sdrDeviceAddr - chassis SDR device address 621 * @param selDeviceAddr - chassis SEL device address 622 * @param smDeviceAddr - chassis system management device address 623 * @param bridgeDeviceAddr - chassis bridge device address 624 * 625 * @returns IPMI completion code 626 */ 627 ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout, 628 uint6_t reserved1, 629 630 uint8_t fruDeviceAddr, 631 632 uint8_t sdrDeviceAddr, 633 634 uint8_t selDeviceAddr, 635 636 uint8_t smDeviceAddr, 637 638 uint8_t bridgeDeviceAddr) 639 { 640 641 // check input data 642 if (reserved1 != 0) 643 { 644 log<level::ERR>("Unsupported request parameter"); 645 return ipmi::responseInvalidFieldRequest(); 646 } 647 648 if ((fruDeviceAddr & ~chassisCapAddrMask) != 0) 649 { 650 log<level::ERR>("Unsupported request parameter(FRU Addr)", 651 entry("REQ=0x%x", fruDeviceAddr)); 652 return ipmi::responseInvalidFieldRequest(); 653 } 654 if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0) 655 { 656 log<level::ERR>("Unsupported request parameter(SDR Addr)", 657 entry("REQ=0x%x", sdrDeviceAddr)); 658 return ipmi::responseInvalidFieldRequest(); 659 } 660 661 if ((selDeviceAddr & ~chassisCapAddrMask) != 0) 662 { 663 log<level::ERR>("Unsupported request parameter(SEL Addr)", 664 entry("REQ=0x%x", selDeviceAddr)); 665 return ipmi::responseInvalidFieldRequest(); 666 } 667 668 if ((smDeviceAddr & ~chassisCapAddrMask) != 0) 669 { 670 log<level::ERR>("Unsupported request parameter(SM Addr)", 671 entry("REQ=0x%x", smDeviceAddr)); 672 return ipmi::responseInvalidFieldRequest(); 673 } 674 675 if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0) 676 { 677 log<level::ERR>("Unsupported request parameter(Bridge Addr)", 678 entry("REQ=0x%x", bridgeDeviceAddr)); 679 return ipmi::responseInvalidFieldRequest(); 680 } 681 682 uint8_t capFlags = (static_cast<uint8_t>(intrusion)) | 683 ((static_cast<uint8_t>(fpLockout)) << 1); 684 try 685 { 686 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 687 ipmi::DbusObjectInfo chassisCapObject = 688 ipmi::getDbusObject(bus, chassisCapIntf); 689 690 ipmi::setDbusProperty(bus, chassisCapObject.second, 691 chassisCapObject.first, chassisCapIntf, 692 chassisCapFlagsProp, capFlags); 693 694 ipmi::setDbusProperty(bus, chassisCapObject.second, 695 chassisCapObject.first, chassisCapIntf, 696 chassisFRUDevAddrProp, fruDeviceAddr); 697 698 ipmi::setDbusProperty(bus, chassisCapObject.second, 699 chassisCapObject.first, chassisCapIntf, 700 chassisSDRDevAddrProp, sdrDeviceAddr); 701 702 ipmi::setDbusProperty(bus, chassisCapObject.second, 703 chassisCapObject.first, chassisCapIntf, 704 chassisSELDevAddrProp, selDeviceAddr); 705 706 ipmi::setDbusProperty(bus, chassisCapObject.second, 707 chassisCapObject.first, chassisCapIntf, 708 chassisSMDevAddrProp, smDeviceAddr); 709 710 ipmi::setDbusProperty(bus, chassisCapObject.second, 711 chassisCapObject.first, chassisCapIntf, 712 chassisBridgeDevAddrProp, bridgeDeviceAddr); 713 } 714 catch (std::exception& e) 715 { 716 log<level::ERR>(e.what()); 717 return ipmi::responseUnspecifiedError(); 718 } 719 return ipmi::responseSuccess(); 720 } 721 722 //------------------------------------------ 723 // Calls into Host State Manager Dbus object 724 //------------------------------------------ 725 int initiate_state_transition(State::Host::Transition transition) 726 { 727 // OpenBMC Host State Manager dbus framework 728 constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0"; 729 constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host"; 730 constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; 731 constexpr auto PROPERTY = "RequestedHostTransition"; 732 733 // sd_bus error 734 int rc = 0; 735 char* busname = NULL; 736 737 // SD Bus error report mechanism. 738 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 739 740 // Gets a hook onto either a SYSTEM or SESSION bus 741 sd_bus* bus_type = ipmid_get_sd_bus_connection(); 742 rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname); 743 if (rc < 0) 744 { 745 log<level::ERR>( 746 "Failed to get bus name", 747 entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT)); 748 return rc; 749 } 750 751 // Convert to string equivalent of the passed in transition enum. 752 auto request = State::convertForMessage(transition); 753 754 rc = sd_bus_call_method(bus_type, // On the system bus 755 busname, // Service to contact 756 HOST_STATE_MANAGER_ROOT, // Object path 757 DBUS_PROPERTY_IFACE, // Interface name 758 "Set", // Method to be called 759 &bus_error, // object to return error 760 nullptr, // Response buffer if any 761 "ssv", // Takes 3 arguments 762 HOST_STATE_MANAGER_IFACE, PROPERTY, "s", 763 request.c_str()); 764 if (rc < 0) 765 { 766 log<level::ERR>("Failed to initiate transition", 767 entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str())); 768 } 769 else 770 { 771 log<level::INFO>("Transition request initiated successfully"); 772 } 773 774 sd_bus_error_free(&bus_error); 775 free(busname); 776 777 return rc; 778 } 779 780 //------------------------------------------ 781 // Set Enabled property to inform NMI source 782 // handling to trigger a NMI_OUT BSOD. 783 //------------------------------------------ 784 int setNmiProperty(const bool value) 785 { 786 constexpr const char* nmiSourceObjPath = 787 "/xyz/openbmc_project/Chassis/Control/NMISource"; 788 constexpr const char* nmiSourceIntf = 789 "xyz.openbmc_project.Chassis.Control.NMISource"; 790 std::string bmcSourceSignal = "xyz.openbmc_project.Chassis.Control." 791 "NMISource.BMCSourceSignal.ChassisCmd"; 792 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 793 794 try 795 { 796 auto service = ipmi::getService(*busp, nmiSourceIntf, nmiSourceObjPath); 797 ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf, 798 "BMCSource", bmcSourceSignal); 799 ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf, 800 "Enabled", value); 801 } 802 catch (std::exception& e) 803 { 804 log<level::ERR>("Failed to trigger NMI_OUT", 805 entry("EXCEPTION=%s", e.what())); 806 return -1; 807 } 808 809 return 0; 810 } 811 812 namespace power_policy 813 { 814 815 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; 816 using IpmiValue = uint8_t; 817 using DbusValue = RestorePolicy::Policy; 818 819 const std::map<DbusValue, IpmiValue> dbusToIpmi = { 820 {RestorePolicy::Policy::AlwaysOff, 0x00}, 821 {RestorePolicy::Policy::Restore, 0x01}, 822 {RestorePolicy::Policy::AlwaysOn, 0x02}}; 823 824 static constexpr uint8_t noChange = 0x03; 825 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; 826 827 /* helper function for Get Chassis Status Command 828 */ 829 std::optional<uint2_t> getPowerRestorePolicy() 830 { 831 uint2_t restorePolicy = 0; 832 using namespace chassis::internal; 833 834 settings::Objects& objects = cache::getObjects(); 835 836 try 837 { 838 const auto& powerRestoreSetting = 839 objects.map.at(powerRestoreIntf).front(); 840 ipmi::Value result = ipmi::getDbusProperty( 841 *getSdBus(), 842 objects.service(powerRestoreSetting, powerRestoreIntf).c_str(), 843 powerRestoreSetting.c_str(), powerRestoreIntf, 844 "PowerRestorePolicy"); 845 auto powerRestore = RestorePolicy::convertPolicyFromString( 846 std::get<std::string>(result)); 847 restorePolicy = dbusToIpmi.at(powerRestore); 848 } 849 catch (const std::exception& e) 850 { 851 log<level::ERR>( 852 "Failed to fetch pgood property", entry("ERROR=%s", e.what()), 853 entry("PATH=%s", objects.map.at(powerRestoreIntf).front().c_str()), 854 entry("INTERFACE=%s", powerRestoreIntf)); 855 cache::objectsPtr.reset(); 856 return std::nullopt; 857 } 858 return std::make_optional(restorePolicy); 859 } 860 861 /* 862 * getPowerStatus 863 * helper function for Get Chassis Status Command 864 * return - optional value for pgood (no value on error) 865 */ 866 std::optional<bool> getPowerStatus() 867 { 868 bool powerGood = false; 869 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 870 try 871 { 872 constexpr const char* chassisStatePath = 873 "/xyz/openbmc_project/state/chassis0"; 874 constexpr const char* chassisStateIntf = 875 "xyz.openbmc_project.State.Chassis"; 876 auto service = 877 ipmi::getService(*busp, chassisStateIntf, chassisStatePath); 878 879 ipmi::Value powerState = 880 ipmi::getDbusProperty(*busp, service, chassisStatePath, 881 chassisStateIntf, "CurrentPowerState"); 882 powerGood = std::get<std::string>(powerState) == 883 "xyz.openbmc_project.State.Chassis.PowerState.On"; 884 } 885 catch (const std::exception& e) 886 { 887 try 888 { 889 // FIXME: some legacy modules use the older path; try that next 890 constexpr const char* legacyPwrCtrlObj = 891 "/org/openbmc/control/power0"; 892 constexpr const char* legacyPwrCtrlIntf = 893 "org.openbmc.control.Power"; 894 auto service = 895 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj); 896 897 ipmi::Value variant = ipmi::getDbusProperty( 898 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood"); 899 powerGood = static_cast<bool>(std::get<int>(variant)); 900 } 901 catch (const std::exception& e) 902 { 903 log<level::ERR>("Failed to fetch pgood property", 904 entry("ERROR=%s", e.what())); 905 return std::nullopt; 906 } 907 } 908 return std::make_optional(powerGood); 909 } 910 911 /* 912 * getACFailStatus 913 * helper function for Get Chassis Status Command 914 * return - bool value for ACFail (false on error) 915 */ 916 bool getACFailStatus() 917 { 918 constexpr const char* powerControlObj = 919 "/xyz/openbmc_project/Chassis/Control/Power0"; 920 constexpr const char* powerControlIntf = 921 "xyz.openbmc_project.Chassis.Control.Power"; 922 bool acFail = false; 923 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 924 try 925 { 926 auto service = 927 ipmi::getService(*bus, powerControlIntf, powerControlObj); 928 929 ipmi::Value variant = ipmi::getDbusProperty( 930 *bus, service, powerControlObj, powerControlIntf, "PFail"); 931 acFail = std::get<bool>(variant); 932 } 933 catch (const std::exception& e) 934 { 935 log<level::ERR>("Failed to fetch PFail property", 936 entry("ERROR=%s", e.what()), 937 entry("PATH=%s", powerControlObj), 938 entry("INTERFACE=%s", powerControlIntf)); 939 } 940 return acFail; 941 } 942 } // namespace power_policy 943 944 static std::optional<bool> getButtonEnabled(const std::string& buttonPath, 945 const std::string& buttonIntf) 946 { 947 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 948 bool buttonDisabled = false; 949 try 950 { 951 auto service = ipmi::getService(*busp, buttonIntf, buttonPath); 952 ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath, 953 buttonIntf, "Enabled"); 954 buttonDisabled = !std::get<bool>(enabled); 955 } 956 catch (sdbusplus::exception::SdBusError& e) 957 { 958 log<level::ERR>("Fail to get button Enabled property", 959 entry("PATH=%s", buttonPath.c_str()), 960 entry("ERROR=%s", e.what())); 961 return std::nullopt; 962 } 963 return std::make_optional(buttonDisabled); 964 } 965 966 //---------------------------------------------------------------------- 967 // Get Chassis Status commands 968 //---------------------------------------------------------------------- 969 ipmi::RspType<bool, // Power is on 970 bool, // Power overload 971 bool, // Interlock 972 bool, // power fault 973 bool, // power control fault 974 uint2_t, // power restore policy 975 bool, // reserved 976 977 bool, // AC failed 978 bool, // last power down caused by a Power overload 979 bool, // last power down caused by a power interlock 980 bool, // last power down caused by power fault 981 bool, // last ‘Power is on’ state was entered via IPMI command 982 uint3_t, // reserved 983 984 bool, // Chassis intrusion active 985 bool, // Front Panel Lockout active 986 bool, // Drive Fault 987 bool, // Cooling/fan fault detected 988 uint2_t, // Chassis Identify State 989 bool, // Chassis Identify command and state info supported 990 bool, // reserved 991 992 bool, // Power off button disabled 993 bool, // Reset button disabled 994 bool, // Diagnostic Interrupt button disabled 995 bool, // Standby (sleep) button disabled 996 bool, // Power off button disable allowed 997 bool, // Reset button disable allowed 998 bool, // Diagnostic Interrupt button disable allowed 999 bool // Standby (sleep) button disable allowed 1000 > 1001 ipmiGetChassisStatus() 1002 { 1003 using namespace chassis::internal; 1004 std::optional<uint2_t> restorePolicy = 1005 power_policy::getPowerRestorePolicy(); 1006 std::optional<bool> powerGood = power_policy::getPowerStatus(); 1007 if (!restorePolicy || !powerGood) 1008 { 1009 return ipmi::responseUnspecifiedError(); 1010 } 1011 1012 // Front Panel Button Capabilities and disable/enable status(Optional) 1013 std::optional<bool> powerButtonReading = 1014 getButtonEnabled(powerButtonPath, powerButtonIntf); 1015 // allow disable if the interface is present 1016 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading); 1017 // default return the button is enabled (not disabled) 1018 bool powerButtonDisabled = false; 1019 if (powerButtonDisableAllow) 1020 { 1021 // return the real value of the button status, if present 1022 powerButtonDisabled = *powerButtonReading; 1023 } 1024 1025 std::optional<bool> resetButtonReading = 1026 getButtonEnabled(resetButtonPath, resetButtonIntf); 1027 // allow disable if the interface is present 1028 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading); 1029 // default return the button is enabled (not disabled) 1030 bool resetButtonDisabled = false; 1031 if (resetButtonDisableAllow) 1032 { 1033 // return the real value of the button status, if present 1034 resetButtonDisabled = *resetButtonReading; 1035 } 1036 1037 bool powerDownAcFailed = power_policy::getACFailStatus(); 1038 1039 // This response has a lot of hard-coded, unsupported fields 1040 // They are set to false or 0 1041 constexpr bool powerOverload = false; 1042 constexpr bool chassisInterlock = false; 1043 constexpr bool powerFault = false; 1044 constexpr bool powerControlFault = false; 1045 constexpr bool powerDownOverload = false; 1046 constexpr bool powerDownInterlock = false; 1047 constexpr bool powerDownPowerFault = false; 1048 constexpr bool powerStatusIPMI = false; 1049 constexpr bool chassisIntrusionActive = false; 1050 constexpr bool frontPanelLockoutActive = false; 1051 constexpr bool driveFault = false; 1052 constexpr bool coolingFanFault = false; 1053 // chassisIdentifySupport set because this command is implemented 1054 constexpr bool chassisIdentifySupport = true; 1055 uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState); 1056 constexpr bool diagButtonDisabled = false; 1057 constexpr bool sleepButtonDisabled = false; 1058 constexpr bool diagButtonDisableAllow = false; 1059 constexpr bool sleepButtonDisableAllow = false; 1060 1061 return ipmi::responseSuccess( 1062 *powerGood, powerOverload, chassisInterlock, powerFault, 1063 powerControlFault, *restorePolicy, 1064 false, // reserved 1065 1066 powerDownAcFailed, powerDownOverload, powerDownInterlock, 1067 powerDownPowerFault, powerStatusIPMI, 1068 uint3_t(0), // reserved 1069 1070 chassisIntrusionActive, frontPanelLockoutActive, driveFault, 1071 coolingFanFault, chassisIdentifyState, chassisIdentifySupport, 1072 false, // reserved 1073 1074 powerButtonDisabled, resetButtonDisabled, diagButtonDisabled, 1075 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow, 1076 diagButtonDisableAllow, sleepButtonDisableAllow); 1077 } 1078 1079 //------------------------------------------------------------- 1080 // Send a command to SoftPowerOff application to stop any timer 1081 //------------------------------------------------------------- 1082 int stop_soft_off_timer() 1083 { 1084 constexpr auto iface = "org.freedesktop.DBus.Properties"; 1085 constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." 1086 "SoftPowerOff"; 1087 1088 constexpr auto property = "ResponseReceived"; 1089 constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." 1090 "SoftPowerOff.HostResponse.HostShutdown"; 1091 1092 // Get the system bus where most system services are provided. 1093 auto bus = ipmid_get_sd_bus_connection(); 1094 1095 // Get the service name 1096 // TODO openbmc/openbmc#1661 - Mapper refactor 1097 // 1098 // See openbmc/openbmc#1743 for some details but high level summary is that 1099 // for now the code will directly call the soft off interface due to a 1100 // race condition with mapper usage 1101 // 1102 // char *busname = nullptr; 1103 // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); 1104 // if (r < 0) 1105 //{ 1106 // fprintf(stderr, "Failed to get %s bus name: %s\n", 1107 // SOFTOFF_OBJPATH, -r); 1108 // return r; 1109 //} 1110 1111 // No error object or reply expected. 1112 int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, 1113 "Set", nullptr, nullptr, "ssv", soft_off_iface, 1114 property, "s", value); 1115 if (rc < 0) 1116 { 1117 log<level::ERR>("Failed to set property in SoftPowerOff object", 1118 entry("ERRNO=0x%X", -rc)); 1119 } 1120 1121 // TODO openbmc/openbmc#1661 - Mapper refactor 1122 // free(busname); 1123 return rc; 1124 } 1125 1126 //---------------------------------------------------------------------- 1127 // Create file to indicate there is no need for softoff notification to host 1128 //---------------------------------------------------------------------- 1129 void indicate_no_softoff_needed() 1130 { 1131 fs::path path{HOST_INBAND_REQUEST_DIR}; 1132 if (!fs::is_directory(path)) 1133 { 1134 fs::create_directory(path); 1135 } 1136 1137 // Add the host instance (default 0 for now) to the file name 1138 std::string file{HOST_INBAND_REQUEST_FILE}; 1139 auto size = std::snprintf(nullptr, 0, file.c_str(), 0); 1140 size++; // null 1141 std::unique_ptr<char[]> buf(new char[size]); 1142 std::snprintf(buf.get(), size, file.c_str(), 0); 1143 1144 // Append file name to directory and create it 1145 path /= buf.get(); 1146 std::ofstream(path.c_str()); 1147 } 1148 1149 /** @brief Implementation of chassis control command 1150 * 1151 * @param - chassisControl command byte 1152 * 1153 * @return Success or InvalidFieldRequest. 1154 */ 1155 ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl) 1156 { 1157 int rc = 0; 1158 switch (chassisControl) 1159 { 1160 case CMD_POWER_ON: 1161 rc = initiate_state_transition(State::Host::Transition::On); 1162 break; 1163 case CMD_POWER_OFF: 1164 // This path would be hit in 2 conditions. 1165 // 1: When user asks for power off using ipmi chassis command 0x04 1166 // 2: Host asking for power off post shutting down. 1167 1168 // If it's a host requested power off, then need to nudge Softoff 1169 // application that it needs to stop the watchdog timer if running. 1170 // If it is a user requested power off, then this is not really 1171 // needed. But then we need to differentiate between user and host 1172 // calling this same command 1173 1174 // For now, we are going ahead with trying to nudge the soft off and 1175 // interpret the failure to do so as a non softoff case 1176 rc = stop_soft_off_timer(); 1177 1178 // Only request the Off transition if the soft power off 1179 // application is not running 1180 if (rc < 0) 1181 { 1182 // First create a file to indicate to the soft off application 1183 // that it should not run. Not doing this will result in State 1184 // manager doing a default soft power off when asked for power 1185 // off. 1186 indicate_no_softoff_needed(); 1187 1188 // Now request the shutdown 1189 rc = initiate_state_transition(State::Host::Transition::Off); 1190 } 1191 else 1192 { 1193 log<level::INFO>("Soft off is running, so let shutdown target " 1194 "stop the host"); 1195 } 1196 break; 1197 1198 case CMD_HARD_RESET: 1199 case CMD_POWER_CYCLE: 1200 // SPEC has a section that says certain implementations can trigger 1201 // PowerOn if power is Off when a command to power cycle is 1202 // requested 1203 1204 // First create a file to indicate to the soft off application 1205 // that it should not run since this is a direct user initiated 1206 // power reboot request (i.e. a reboot request that is not 1207 // originating via a soft power off SMS request) 1208 indicate_no_softoff_needed(); 1209 1210 rc = initiate_state_transition(State::Host::Transition::Reboot); 1211 break; 1212 1213 case CMD_SOFT_OFF_VIA_OVER_TEMP: 1214 // Request Host State Manager to do a soft power off 1215 rc = initiate_state_transition(State::Host::Transition::Off); 1216 break; 1217 1218 case CMD_PULSE_DIAGNOSTIC_INTR: 1219 rc = setNmiProperty(true); 1220 break; 1221 1222 default: 1223 { 1224 log<level::ERR>("Invalid Chassis Control command", 1225 entry("CMD=0x%X", chassisControl)); 1226 return ipmi::responseInvalidFieldRequest(); 1227 } 1228 } 1229 1230 return ((rc < 0) ? ipmi::responseUnspecifiedError() 1231 : ipmi::responseSuccess()); 1232 } 1233 1234 /** @brief Return D-Bus connection string to enclosure identify LED object 1235 * 1236 * @param[in, out] connection - connection to D-Bus object 1237 * @return a IPMI return code 1238 */ 1239 std::string getEnclosureIdentifyConnection() 1240 { 1241 // lookup enclosure_identify group owner(s) in mapper 1242 auto mapperCall = chassis::internal::dbus.new_method_call( 1243 ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF, 1244 "GetObject"); 1245 1246 mapperCall.append(identify_led_object_name); 1247 static const std::vector<std::string> interfaces = { 1248 "xyz.openbmc_project.Led.Group"}; 1249 mapperCall.append(interfaces); 1250 auto mapperReply = chassis::internal::dbus.call(mapperCall); 1251 if (mapperReply.is_method_error()) 1252 { 1253 log<level::ERR>("Chassis Identify: Error communicating to mapper."); 1254 elog<InternalFailure>(); 1255 } 1256 std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp; 1257 mapperReply.read(mapperResp); 1258 1259 if (mapperResp.size() != encIdentifyObjectsSize) 1260 { 1261 log<level::ERR>( 1262 "Invalid number of enclosure identify objects.", 1263 entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size())); 1264 elog<InternalFailure>(); 1265 } 1266 auto pair = mapperResp[encIdentifyObjectsSize - 1]; 1267 return pair.first; 1268 } 1269 1270 /** @brief Turn On/Off enclosure identify LED 1271 * 1272 * @param[in] flag - true to turn on LED, false to turn off 1273 * @return a IPMI return code 1274 */ 1275 void enclosureIdentifyLed(bool flag) 1276 { 1277 using namespace chassis::internal; 1278 std::string connection = std::move(getEnclosureIdentifyConnection()); 1279 auto msg = std::string("enclosureIdentifyLed(") + 1280 boost::lexical_cast<std::string>(flag) + ")"; 1281 log<level::DEBUG>(msg.c_str()); 1282 auto led = 1283 dbus.new_method_call(connection.c_str(), identify_led_object_name, 1284 "org.freedesktop.DBus.Properties", "Set"); 1285 led.append("xyz.openbmc_project.Led.Group", "Asserted", 1286 std::variant<bool>(flag)); 1287 auto ledReply = dbus.call(led); 1288 if (ledReply.is_method_error()) 1289 { 1290 log<level::ERR>("Chassis Identify: Error Setting State On/Off\n", 1291 entry("LED_STATE=%d", flag)); 1292 elog<InternalFailure>(); 1293 } 1294 } 1295 1296 /** @brief Callback method to turn off LED 1297 */ 1298 void enclosureIdentifyLedOff() 1299 { 1300 try 1301 { 1302 chassisIDState = ChassisIDState::off; 1303 enclosureIdentifyLed(false); 1304 } 1305 catch (const InternalFailure& e) 1306 { 1307 report<InternalFailure>(); 1308 } 1309 } 1310 1311 /** @brief Create timer to turn on and off the enclosure LED 1312 */ 1313 void createIdentifyTimer() 1314 { 1315 if (!identifyTimer) 1316 { 1317 identifyTimer = 1318 std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff); 1319 } 1320 } 1321 1322 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, 1323 std::optional<uint8_t> force) 1324 { 1325 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); 1326 bool forceIdentify = force.value_or(0) & 0x01; 1327 1328 if (identifyInterval || forceIdentify) 1329 { 1330 // stop the timer if already started; 1331 // for force identify we should not turn off LED 1332 identifyTimer->stop(); 1333 try 1334 { 1335 chassisIDState = ChassisIDState::temporaryOn; 1336 enclosureIdentifyLed(true); 1337 } 1338 catch (const InternalFailure& e) 1339 { 1340 report<InternalFailure>(); 1341 return ipmi::responseResponseError(); 1342 } 1343 1344 if (forceIdentify) 1345 { 1346 chassisIDState = ChassisIDState::indefiniteOn; 1347 return ipmi::responseSuccess(); 1348 } 1349 // start the timer 1350 auto time = std::chrono::duration_cast<std::chrono::microseconds>( 1351 std::chrono::seconds(identifyInterval)); 1352 identifyTimer->start(time); 1353 } 1354 else if (!identifyInterval) 1355 { 1356 identifyTimer->stop(); 1357 enclosureIdentifyLedOff(); 1358 } 1359 return ipmi::responseSuccess(); 1360 } 1361 1362 namespace boot_options 1363 { 1364 1365 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 1366 using IpmiValue = uint8_t; 1367 constexpr auto ipmiDefault = 0; 1368 1369 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 1370 {0x01, Source::Sources::Network}, 1371 {0x02, Source::Sources::Disk}, 1372 {0x05, Source::Sources::ExternalMedia}, 1373 {0x0f, Source::Sources::RemovableMedia}, 1374 {ipmiDefault, Source::Sources::Default}}; 1375 1376 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 1377 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1378 {0x03, Mode::Modes::Safe}, 1379 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1380 {0x06, Mode::Modes::Setup}, 1381 {ipmiDefault, Mode::Modes::Regular}}; 1382 1383 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 1384 {Source::Sources::Network, 0x01}, 1385 {Source::Sources::Disk, 0x02}, 1386 {Source::Sources::ExternalMedia, 0x05}, 1387 {Source::Sources::RemovableMedia, 0x0f}, 1388 {Source::Sources::Default, ipmiDefault}}; 1389 1390 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 1391 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1392 {Mode::Modes::Safe, 0x03}, 1393 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1394 {Mode::Modes::Setup, 0x06}, 1395 {Mode::Modes::Regular, ipmiDefault}}; 1396 1397 } // namespace boot_options 1398 1399 /** @brief Set the property value for boot source 1400 * @param[in] source - boot source value 1401 * @return On failure return IPMI error. 1402 */ 1403 static ipmi_ret_t setBootSource(const Source::Sources& source) 1404 { 1405 using namespace chassis::internal; 1406 using namespace chassis::internal::cache; 1407 std::variant<std::string> property = convertForMessage(source); 1408 settings::Objects& objects = getObjects(); 1409 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1410 const auto& bootSourceSetting = std::get<settings::Path>(bootSetting); 1411 auto method = dbus.new_method_call( 1412 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1413 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set"); 1414 method.append(bootSourceIntf, "BootSource", property); 1415 auto reply = dbus.call(method); 1416 if (reply.is_method_error()) 1417 { 1418 log<level::ERR>("Error in BootSource Set"); 1419 report<InternalFailure>(); 1420 return IPMI_CC_UNSPECIFIED_ERROR; 1421 } 1422 return IPMI_CC_OK; 1423 } 1424 1425 /** @brief Set the property value for boot mode 1426 * @param[in] mode - boot mode value 1427 * @return On failure return IPMI error. 1428 */ 1429 static ipmi_ret_t setBootMode(const Mode::Modes& mode) 1430 { 1431 using namespace chassis::internal; 1432 using namespace chassis::internal::cache; 1433 std::variant<std::string> property = convertForMessage(mode); 1434 settings::Objects& objects = getObjects(); 1435 auto bootSetting = settings::boot::setting(objects, bootModeIntf); 1436 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1437 auto method = dbus.new_method_call( 1438 objects.service(bootModeSetting, bootModeIntf).c_str(), 1439 bootModeSetting.c_str(), ipmi::PROP_INTF, "Set"); 1440 method.append(bootModeIntf, "BootMode", property); 1441 auto reply = dbus.call(method); 1442 if (reply.is_method_error()) 1443 { 1444 log<level::ERR>("Error in BootMode Set"); 1445 report<InternalFailure>(); 1446 return IPMI_CC_UNSPECIFIED_ERROR; 1447 } 1448 return IPMI_CC_OK; 1449 } 1450 1451 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1452 ipmi_request_t request, 1453 ipmi_response_t response, 1454 ipmi_data_len_t data_len, 1455 ipmi_context_t context) 1456 { 1457 using namespace boot_options; 1458 ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED; 1459 char* p = NULL; 1460 get_sys_boot_options_response_t* resp = 1461 (get_sys_boot_options_response_t*)response; 1462 get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request; 1463 IpmiValue bootOption = ipmiDefault; 1464 1465 std::memset(resp, 0, sizeof(*resp)); 1466 resp->version = SET_PARM_VERSION; 1467 resp->parm = 5; 1468 resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME; 1469 1470 /* 1471 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1472 * This is the only parameter used by petitboot. 1473 */ 1474 if (reqptr->parameter == 1475 static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS)) 1476 { 1477 1478 *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS); 1479 using namespace chassis::internal; 1480 using namespace chassis::internal::cache; 1481 1482 try 1483 { 1484 settings::Objects& objects = getObjects(); 1485 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1486 const auto& bootSourceSetting = 1487 std::get<settings::Path>(bootSetting); 1488 auto oneTimeEnabled = 1489 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1490 auto method = dbus.new_method_call( 1491 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1492 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get"); 1493 method.append(bootSourceIntf, "BootSource"); 1494 auto reply = dbus.call(method); 1495 if (reply.is_method_error()) 1496 { 1497 log<level::ERR>("Error in BootSource Get"); 1498 report<InternalFailure>(); 1499 *data_len = 0; 1500 return IPMI_CC_UNSPECIFIED_ERROR; 1501 } 1502 std::variant<std::string> result; 1503 reply.read(result); 1504 auto bootSource = 1505 Source::convertSourcesFromString(std::get<std::string>(result)); 1506 1507 bootSetting = settings::boot::setting(objects, bootModeIntf); 1508 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1509 method = dbus.new_method_call( 1510 objects.service(bootModeSetting, bootModeIntf).c_str(), 1511 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get"); 1512 method.append(bootModeIntf, "BootMode"); 1513 reply = dbus.call(method); 1514 if (reply.is_method_error()) 1515 { 1516 log<level::ERR>("Error in BootMode Get"); 1517 report<InternalFailure>(); 1518 *data_len = 0; 1519 return IPMI_CC_UNSPECIFIED_ERROR; 1520 } 1521 reply.read(result); 1522 auto bootMode = 1523 Mode::convertModesFromString(std::get<std::string>(result)); 1524 1525 bootOption = sourceDbusToIpmi.at(bootSource); 1526 if ((Mode::Modes::Regular == bootMode) && 1527 (Source::Sources::Default == bootSource)) 1528 { 1529 bootOption = ipmiDefault; 1530 } 1531 else if (Source::Sources::Default == bootSource) 1532 { 1533 bootOption = modeDbusToIpmi.at(bootMode); 1534 } 1535 resp->data[1] = (bootOption << 2); 1536 1537 resp->data[0] = oneTimeEnabled 1538 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 1539 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT; 1540 1541 rc = IPMI_CC_OK; 1542 } 1543 catch (InternalFailure& e) 1544 { 1545 cache::objectsPtr.reset(); 1546 report<InternalFailure>(); 1547 *data_len = 0; 1548 return IPMI_CC_UNSPECIFIED_ERROR; 1549 } 1550 } 1551 else if (reqptr->parameter == 1552 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS)) 1553 { 1554 1555 *data_len = 1556 static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS); 1557 1558 resp->parm = 1559 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS); 1560 1561 int ret = getHostNetworkData(resp); 1562 1563 if (ret < 0) 1564 { 1565 1566 log<level::ERR>( 1567 "getHostNetworkData failed for get_sys_boot_options."); 1568 rc = IPMI_CC_UNSPECIFIED_ERROR; 1569 } 1570 else 1571 rc = IPMI_CC_OK; 1572 } 1573 1574 else 1575 { 1576 log<level::ERR>("Unsupported parameter", 1577 entry("PARAM=0x%x", reqptr->parameter)); 1578 } 1579 1580 if (p) 1581 free(p); 1582 1583 if (rc == IPMI_CC_OK) 1584 { 1585 *data_len += 2; 1586 } 1587 1588 return rc; 1589 } 1590 1591 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1592 ipmi_request_t request, 1593 ipmi_response_t response, 1594 ipmi_data_len_t data_len, 1595 ipmi_context_t context) 1596 { 1597 using namespace boot_options; 1598 ipmi_ret_t rc = IPMI_CC_OK; 1599 set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request; 1600 1601 std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n", 1602 reqptr->parameter); 1603 1604 // This IPMI command does not have any resposne data 1605 *data_len = 0; 1606 1607 /* 000101 1608 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1609 * This is the only parameter used by petitboot. 1610 */ 1611 1612 if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) 1613 { 1614 IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2); 1615 using namespace chassis::internal; 1616 using namespace chassis::internal::cache; 1617 auto oneTimeEnabled = false; 1618 constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable"; 1619 constexpr auto oneTimePath = 1620 "/xyz/openbmc_project/control/host0/boot/one_time"; 1621 1622 try 1623 { 1624 bool permanent = 1625 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) == 1626 SET_PARM_BOOT_FLAGS_PERMANENT; 1627 1628 settings::Objects& objects = getObjects(); 1629 1630 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1631 1632 oneTimeEnabled = 1633 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1634 1635 /* 1636 * Check if the current boot setting is onetime or permanent, if the 1637 * request in the command is otherwise, then set the "Enabled" 1638 * property in one_time object path to 'True' to indicate onetime 1639 * and 'False' to indicate permanent. 1640 * 1641 * Once the onetime/permanent setting is applied, then the bootMode 1642 * and bootSource is updated for the corresponding object. 1643 */ 1644 if ((permanent && oneTimeEnabled) || 1645 (!permanent && !oneTimeEnabled)) 1646 { 1647 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath); 1648 1649 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf, 1650 "Enabled", !permanent); 1651 } 1652 1653 auto modeItr = modeIpmiToDbus.find(bootOption); 1654 auto sourceItr = sourceIpmiToDbus.find(bootOption); 1655 if (sourceIpmiToDbus.end() != sourceItr) 1656 { 1657 rc = setBootSource(sourceItr->second); 1658 if (rc != IPMI_CC_OK) 1659 { 1660 *data_len = 0; 1661 return rc; 1662 } 1663 // If a set boot device is mapping to a boot source, then reset 1664 // the boot mode D-Bus property to default. 1665 // This way the ipmid code can determine which property is not 1666 // at the default value 1667 if (sourceItr->second != Source::Sources::Default) 1668 { 1669 setBootMode(Mode::Modes::Regular); 1670 } 1671 } 1672 if (modeIpmiToDbus.end() != modeItr) 1673 { 1674 rc = setBootMode(modeItr->second); 1675 if (rc != IPMI_CC_OK) 1676 { 1677 *data_len = 0; 1678 return rc; 1679 } 1680 // If a set boot device is mapping to a boot mode, then reset 1681 // the boot source D-Bus property to default. 1682 // This way the ipmid code can determine which property is not 1683 // at the default value 1684 if (modeItr->second != Mode::Modes::Regular) 1685 { 1686 setBootSource(Source::Sources::Default); 1687 } 1688 } 1689 if ((modeIpmiToDbus.end() == modeItr) && 1690 (sourceIpmiToDbus.end() == sourceItr)) 1691 { 1692 // return error if boot option is not supported 1693 *data_len = 0; 1694 return IPMI_CC_INVALID_FIELD_REQUEST; 1695 } 1696 } 1697 catch (InternalFailure& e) 1698 { 1699 objectsPtr.reset(); 1700 report<InternalFailure>(); 1701 *data_len = 0; 1702 return IPMI_CC_UNSPECIFIED_ERROR; 1703 } 1704 } 1705 else if (reqptr->parameter == 1706 (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) 1707 { 1708 1709 int ret = setHostNetworkData(reqptr); 1710 if (ret < 0) 1711 { 1712 log<level::ERR>( 1713 "setHostNetworkData failed for set_sys_boot_options"); 1714 rc = IPMI_CC_UNSPECIFIED_ERROR; 1715 } 1716 } 1717 else if (reqptr->parameter == 1718 static_cast<uint8_t>(BootOptionParameter::BOOT_INFO)) 1719 { 1720 // Handle parameter #4 and return command completed normally 1721 // (IPMI_CC_OK). There is no implementation in OpenBMC for this 1722 // parameter. This is added to support the ipmitool command `chassis 1723 // bootdev` which sends set on parameter #4, before setting the boot 1724 // flags. 1725 rc = IPMI_CC_OK; 1726 } 1727 else 1728 { 1729 log<level::ERR>("Unsupported parameter", 1730 entry("PARAM=0x%x", reqptr->parameter)); 1731 rc = IPMI_CC_PARM_NOT_SUPPORTED; 1732 } 1733 1734 return rc; 1735 } 1736 1737 /** @brief implements Get POH counter command 1738 * @parameter 1739 * - none 1740 * @returns IPMI completion code plus response data 1741 * - minPerCount - Minutes per count 1742 * - counterReading - counter reading 1743 */ 1744 ipmi::RspType<uint8_t, // Minutes per count 1745 uint32_t // Counter reading 1746 > 1747 ipmiGetPOHCounter() 1748 { 1749 // sd_bus error 1750 try 1751 { 1752 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), 1753 getPOHCounter()); 1754 } 1755 catch (std::exception& e) 1756 { 1757 log<level::ERR>(e.what()); 1758 return ipmi::responseUnspecifiedError(); 1759 } 1760 } 1761 1762 ipmi::RspType<uint3_t, // policy support 1763 uint5_t // reserved 1764 > 1765 ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield, 1766 uint3_t policy, uint5_t reserved) 1767 { 1768 power_policy::DbusValue value = 1769 power_policy::RestorePolicy::Policy::AlwaysOff; 1770 1771 if (reserved || (policy > power_policy::noChange)) 1772 { 1773 phosphor::logging::log<level::ERR>( 1774 "Reserved request parameter", 1775 entry("REQ=0x%x", static_cast<int>(policy))); 1776 return ipmi::responseInvalidFieldRequest(); 1777 } 1778 1779 if (policy == power_policy::noChange) 1780 { 1781 // just return the supported policy 1782 return ipmi::responseSuccess(power_policy::allSupport, reserved); 1783 } 1784 1785 for (auto const& it : power_policy::dbusToIpmi) 1786 { 1787 if (it.second == policy) 1788 { 1789 value = it.first; 1790 break; 1791 } 1792 } 1793 1794 try 1795 { 1796 settings::Objects& objects = chassis::internal::cache::getObjects(); 1797 const settings::Path& powerRestoreSetting = 1798 objects.map.at(chassis::internal::powerRestoreIntf).front(); 1799 std::variant<std::string> property = convertForMessage(value); 1800 1801 auto sdbusp = getSdBus(); 1802 boost::system::error_code ec; 1803 sdbusp->yield_method_call<void>( 1804 yield, ec, 1805 objects 1806 .service(powerRestoreSetting, 1807 chassis::internal::powerRestoreIntf) 1808 .c_str(), 1809 powerRestoreSetting, ipmi::PROP_INTF, "Set", 1810 chassis::internal::powerRestoreIntf, "PowerRestorePolicy", 1811 property); 1812 if (ec) 1813 { 1814 phosphor::logging::log<level::ERR>("Unspecified Error"); 1815 return ipmi::responseUnspecifiedError(); 1816 } 1817 } 1818 catch (InternalFailure& e) 1819 { 1820 chassis::internal::cache::objectsPtr.reset(); 1821 report<InternalFailure>(); 1822 return ipmi::responseUnspecifiedError(); 1823 } 1824 1825 return ipmi::responseSuccess(power_policy::allSupport, reserved); 1826 } 1827 1828 void register_netfn_chassis_functions() 1829 { 1830 createIdentifyTimer(); 1831 1832 // <Wildcard Command> 1833 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL, 1834 ipmi_chassis_wildcard, PRIVILEGE_USER); 1835 1836 // Get Chassis Capabilities 1837 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, 1838 ipmi_get_chassis_cap, PRIVILEGE_USER); 1839 1840 // Set Chassis Capabilities 1841 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1842 ipmi::chassis::cmdSetChassisCapabilities, 1843 ipmi::Privilege::User, ipmiSetChassisCap); 1844 1845 // <Get System Boot Options> 1846 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL, 1847 ipmi_chassis_get_sys_boot_options, 1848 PRIVILEGE_OPERATOR); 1849 1850 // <Get Chassis Status> 1851 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1852 ipmi::chassis::cmdGetChassisStatus, 1853 ipmi::Privilege::User, ipmiGetChassisStatus); 1854 1855 // <Chassis Control> 1856 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1857 ipmi::chassis::cmdChassisControl, 1858 ipmi::Privilege::Operator, ipmiChassisControl); 1859 1860 // <Chassis Identify> 1861 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1862 ipmi::chassis::cmdChassisIdentify, 1863 ipmi::Privilege::Operator, ipmiChassisIdentify); 1864 1865 // <Set System Boot Options> 1866 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL, 1867 ipmi_chassis_set_sys_boot_options, 1868 PRIVILEGE_OPERATOR); 1869 // <Get POH Counter> 1870 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1871 ipmi::chassis::cmdGetPohCounter, 1872 ipmi::Privilege::User, ipmiGetPOHCounter); 1873 1874 // <Set Power Restore Policy> 1875 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1876 ipmi::chassis::cmdSetPowerRestorePolicy, 1877 ipmi::Privilege::Operator, 1878 ipmiChassisSetPowerRestorePolicy); 1879 } 1880