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