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 <netinet/in.h> 9 10 #include <ipmid/api.hpp> 11 #include <ipmid/types.hpp> 12 #include <ipmid/utils.hpp> 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/lg2.hpp> 15 #include <sdbusplus/bus.hpp> 16 #include <sdbusplus/message/types.hpp> 17 #include <sdbusplus/server/object.hpp> 18 #include <sdbusplus/timer.hpp> 19 #include <settings.hpp> 20 #include <xyz/openbmc_project/Chassis/Intrusion/client.hpp> 21 #include <xyz/openbmc_project/Common/error.hpp> 22 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> 23 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> 24 #include <xyz/openbmc_project/Control/Boot/Type/server.hpp> 25 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> 26 #include <xyz/openbmc_project/State/Chassis/server.hpp> 27 #include <xyz/openbmc_project/State/Host/server.hpp> 28 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> 29 30 #include <array> 31 #include <chrono> 32 #include <cstring> 33 #include <filesystem> 34 #include <fstream> 35 #include <future> 36 #include <map> 37 #include <sstream> 38 #include <string> 39 40 std::unique_ptr<sdbusplus::Timer> identifyTimer 41 __attribute__((init_priority(101))); 42 43 static ChassisIDState chassisIDState = ChassisIDState::reserved; 44 45 constexpr size_t sizeVersion = 2; 46 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15; 47 48 // PetiBoot-Specific 49 static constexpr uint8_t netConfInitialBytes[] = {0x80, 0x21, 0x70, 0x62, 50 0x21, 0x00, 0x01, 0x06}; 51 static constexpr uint8_t oemParmStart = 96; 52 static constexpr uint8_t oemParmEnd = 127; 53 54 static constexpr size_t cookieOffset = 1; 55 static constexpr size_t versionOffset = 5; 56 static constexpr size_t addrSizeOffset = 8; 57 static constexpr size_t macOffset = 9; 58 static constexpr size_t addrTypeOffset = 16; 59 static constexpr size_t ipAddrOffset = 17; 60 61 namespace ipmi 62 { 63 constexpr Cc ccParmNotSupported = 0x80; 64 constexpr Cc ccFailSetInProgress = 0x81; 65 66 static inline auto responseParmNotSupported() 67 { 68 return response(ccParmNotSupported); 69 } 70 71 static inline auto responsefailSetInProgress() 72 { 73 return response(ccFailSetInProgress); 74 } 75 } // namespace ipmi 76 77 void registerNetFnChassisFunctions() __attribute__((constructor)); 78 79 // Host settings in dbus 80 // Service name should be referenced by connection name got via object mapper 81 const char* settings_object_name = "/org/openbmc/settings/host0"; 82 const char* settings_intf_name = "org.freedesktop.DBus.Properties"; 83 const char* identify_led_object_name = 84 "/xyz/openbmc_project/led/groups/enclosure_identify"; 85 86 constexpr auto SETTINGS_ROOT = "/"; 87 constexpr auto SETTINGS_MATCH = "host0"; 88 89 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; 90 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; 91 92 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state"; 93 static constexpr auto chassisPOHStateIntf = 94 "xyz.openbmc_project.State.PowerOnHours"; 95 static constexpr auto pohCounterProperty = "POHCounter"; 96 static constexpr auto match = "chassis0"; 97 const static constexpr char chassisCapIntf[] = 98 "xyz.openbmc_project.Control.ChassisCapabilities"; 99 const static constexpr char chassisIntrusionProp[] = "ChassisIntrusionEnabled"; 100 const static constexpr char chassisFrontPanelLockoutProp[] = 101 "ChassisFrontPanelLockoutEnabled"; 102 const static constexpr char chassisNMIProp[] = "ChassisNMIEnabled"; 103 const static constexpr char chassisPowerInterlockProp[] = 104 "ChassisPowerInterlockEnabled"; 105 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress"; 106 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress"; 107 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress"; 108 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; 109 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; 110 static constexpr uint8_t chassisCapAddrMask = 0xfe; 111 static constexpr const char* powerButtonIntf = 112 "xyz.openbmc_project.Chassis.Buttons.Power"; 113 static constexpr const char* powerButtonPath = 114 "/xyz/openbmc_project/Chassis/Buttons/Power0"; 115 static constexpr const char* resetButtonIntf = 116 "xyz.openbmc_project.Chassis.Buttons.Reset"; 117 static constexpr const char* resetButtonPath = 118 "/xyz/openbmc_project/Chassis/Buttons/Reset0"; 119 120 // Phosphor Host State manager 121 namespace State = sdbusplus::server::xyz::openbmc_project::state; 122 namespace fs = std::filesystem; 123 124 using namespace phosphor::logging; 125 using namespace sdbusplus::error::xyz::openbmc_project::common; 126 using namespace sdbusplus::server::xyz::openbmc_project::control::boot; 127 using Intrusion = sdbusplus::client::xyz::openbmc_project::chassis::Intrusion<>; 128 129 namespace chassis 130 { 131 namespace internal 132 { 133 134 constexpr auto bootSettingsPath = "/xyz/openbmc_project/control/host0/boot"; 135 constexpr auto bootEnableIntf = "xyz.openbmc_project.Object.Enable"; 136 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; 137 constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type"; 138 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source"; 139 constexpr auto bootSettingsOneTimePath = 140 "/xyz/openbmc_project/control/host0/boot/one_time"; 141 constexpr auto bootOneTimeIntf = "xyz.openbmc_project.Object.Enable"; 142 143 constexpr auto powerRestoreIntf = 144 "xyz.openbmc_project.Control.Power.RestorePolicy"; 145 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection()); 146 147 namespace cache 148 { 149 150 std::unique_ptr<settings::Objects> objectsPtr = nullptr; 151 152 settings::Objects& getObjects() 153 { 154 if (objectsPtr == nullptr) 155 { 156 objectsPtr = std::make_unique<settings::Objects>( 157 dbus, std::vector<std::string>{bootModeIntf, bootTypeIntf, 158 bootSourceIntf, powerRestoreIntf}); 159 } 160 return *objectsPtr; 161 } 162 163 } // namespace cache 164 } // namespace internal 165 } // namespace chassis 166 167 namespace poh 168 { 169 170 constexpr auto minutesPerCount = 60; 171 172 } // namespace poh 173 174 int getHostNetworkData(ipmi::message::Payload& payload) 175 { 176 ipmi::PropertyMap properties; 177 int rc = 0; 178 uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE; 179 180 try 181 { 182 // TODO There may be cases where an interface is implemented by multiple 183 // objects,to handle such cases we are interested on that object 184 // which are on interested busname. 185 // Currenlty mapper doesn't give the readable busname(gives busid) 186 // so we can't match with bus name so giving some object specific info 187 // as SETTINGS_MATCH. 188 // Later SETTINGS_MATCH will be replaced with busname. 189 190 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 191 192 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 193 SETTINGS_ROOT, SETTINGS_MATCH); 194 195 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 196 SETTINGS_ROOT, SETTINGS_MATCH); 197 198 properties = ipmi::getAllDbusProperties( 199 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE); 200 auto variant = ipmi::getDbusProperty( 201 bus, macObjectInfo.second, macObjectInfo.first, MAC_INTERFACE, 202 "MACAddress"); 203 204 auto ipAddress = std::get<std::string>(properties["Address"]); 205 206 auto gateway = std::get<std::string>(properties["Gateway"]); 207 208 auto prefix = std::get<uint8_t>(properties["PrefixLength"]); 209 210 uint8_t isStatic = 211 (std::get<std::string>(properties["Origin"]) == 212 "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 213 ? 1 214 : 0; 215 216 auto MACAddress = std::get<std::string>(variant); 217 218 // it is expected here that we should get the valid data 219 // but we may also get the default values. 220 // Validation of the data is done by settings. 221 // 222 // if mac address is default mac address then 223 // don't send blank override. 224 if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS)) 225 { 226 rc = -1; 227 return rc; 228 } 229 // if addr is static then ipaddress,gateway,prefix 230 // should not be default one,don't send blank override. 231 if (isStatic) 232 { 233 if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) || 234 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix)) 235 { 236 rc = -1; 237 return rc; 238 } 239 } 240 241 std::string token; 242 std::stringstream ss(MACAddress); 243 244 // First pack macOffset no of bytes in payload. 245 // Latter this PetiBoot-Specific data will be populated. 246 std::vector<uint8_t> payloadInitialBytes(macOffset); 247 payload.pack(payloadInitialBytes); 248 249 while (std::getline(ss, token, ':')) 250 { 251 payload.pack(stoi(token, nullptr, 16)); 252 } 253 254 payload.pack(0x00); 255 256 payload.pack(isStatic); 257 258 uint8_t addressFamily = (std::get<std::string>(properties["Type"]) == 259 "xyz.openbmc_project.Network.IP.Protocol.IPv4") 260 ? AF_INET 261 : AF_INET6; 262 263 addrSize = (addressFamily == AF_INET) 264 ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE 265 : ipmi::network::IPV6_ADDRESS_SIZE_BYTE; 266 267 // ipaddress and gateway would be in IPv4 format 268 std::vector<uint8_t> addrInBinary(addrSize); 269 inet_pton(addressFamily, ipAddress.c_str(), 270 reinterpret_cast<void*>(addrInBinary.data())); 271 272 payload.pack(addrInBinary); 273 274 payload.pack(prefix); 275 276 std::vector<uint8_t> gatewayDetails(addrSize); 277 inet_pton(addressFamily, gateway.c_str(), 278 reinterpret_cast<void*>(gatewayDetails.data())); 279 payload.pack(gatewayDetails); 280 } 281 catch (const InternalFailure& e) 282 { 283 commit<InternalFailure>(); 284 rc = -1; 285 return rc; 286 } 287 288 // PetiBoot-Specific 289 // If success then copy the first 9 bytes to the payload message 290 // payload first 2 bytes contain the parameter values. Skip that 2 bytes. 291 uint8_t skipFirstTwoBytes = 2; 292 size_t payloadSize = payload.size(); 293 uint8_t* configDataStartingAddress = payload.data() + skipFirstTwoBytes; 294 295 if (payloadSize < skipFirstTwoBytes + sizeof(netConfInitialBytes)) 296 { 297 lg2::error("Invalid net config"); 298 rc = -1; 299 return rc; 300 } 301 std::copy(netConfInitialBytes, 302 netConfInitialBytes + sizeof(netConfInitialBytes), 303 configDataStartingAddress); 304 305 if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize)) 306 { 307 lg2::error("Invalid length of address size"); 308 rc = -1; 309 return rc; 310 } 311 std::copy(&addrSize, &(addrSize) + sizeof(addrSize), 312 configDataStartingAddress + addrSizeOffset); 313 314 #ifdef _IPMI_DEBUG_ 315 std::printf("\n===Printing the IPMI Formatted Data========\n"); 316 317 for (uint8_t pos = 0; pos < index; pos++) 318 { 319 std::printf("%02x ", payloadStartingAddress[pos]); 320 } 321 #endif 322 323 return rc; 324 } 325 326 /** @brief convert IPv4 and IPv6 addresses from binary to text form. 327 * @param[in] family - IPv4/Ipv6 328 * @param[in] data - req data pointer. 329 * @param[in] offset - offset in the data. 330 * @param[in] addrSize - size of the data which needs to be read from offset. 331 * @returns address in text form. 332 */ 333 334 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset, 335 uint8_t addrSize) 336 { 337 char ipAddr[INET6_ADDRSTRLEN] = {}; 338 339 switch (family) 340 { 341 case AF_INET: 342 { 343 struct sockaddr_in addr4{}; 344 std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize); 345 346 inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN); 347 348 break; 349 } 350 case AF_INET6: 351 { 352 struct sockaddr_in6 addr6{}; 353 std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize); 354 355 inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN); 356 357 break; 358 } 359 default: 360 { 361 return {}; 362 } 363 } 364 365 return ipAddr; 366 } 367 368 ipmi::Cc setHostNetworkData(ipmi::message::Payload& data) 369 { 370 using namespace std::string_literals; 371 std::string hostNetworkConfig; 372 std::string mac("00:00:00:00:00:00"); 373 std::string ipAddress, gateway; 374 std::string addrOrigin{0}; 375 uint8_t addrSize{0}; 376 std::string addressOrigin = 377 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 378 std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; 379 uint8_t prefix{0}; 380 uint8_t family = AF_INET; 381 382 // cookie starts from second byte 383 // version starts from sixth byte 384 385 try 386 { 387 do 388 { 389 // cookie == 0x21 0x70 0x62 0x21 390 data.trailingOk = true; 391 auto msgLen = data.size(); 392 std::vector<uint8_t> msgPayloadBytes(msgLen); 393 if (data.unpack(msgPayloadBytes) != 0 || !data.fullyUnpacked()) 394 { 395 lg2::error("Error in unpacking message of setHostNetworkData"); 396 return ipmi::ccReqDataLenInvalid; 397 } 398 399 uint8_t* msgPayloadStartingPos = msgPayloadBytes.data(); 400 constexpr size_t cookieSize = 4; 401 if (msgLen < cookieOffset + cookieSize) 402 { 403 lg2::error("Error in cookie getting of setHostNetworkData"); 404 return ipmi::ccReqDataLenInvalid; 405 } 406 if (std::equal(msgPayloadStartingPos + cookieOffset, 407 msgPayloadStartingPos + cookieOffset + cookieSize, 408 (netConfInitialBytes + cookieOffset)) != 0) 409 { 410 // all cookie == 0 411 if (std::all_of(msgPayloadStartingPos + cookieOffset, 412 msgPayloadStartingPos + cookieOffset + 413 cookieSize, 414 [](int i) { return i == 0; }) == true) 415 { 416 // need to zero out the network settings. 417 break; 418 } 419 420 lg2::error("Invalid Cookie"); 421 elog<InternalFailure>(); 422 } 423 424 // vesion == 0x00 0x01 425 if (msgLen < versionOffset + sizeVersion) 426 { 427 lg2::error("Error in version getting of setHostNetworkData"); 428 return ipmi::ccReqDataLenInvalid; 429 } 430 if (std::equal(msgPayloadStartingPos + versionOffset, 431 msgPayloadStartingPos + versionOffset + sizeVersion, 432 (netConfInitialBytes + versionOffset)) != 0) 433 { 434 lg2::error("Invalid Version"); 435 elog<InternalFailure>(); 436 } 437 438 if (msgLen < macOffset + 6) 439 { 440 lg2::error( 441 "Error in mac address getting of setHostNetworkData"); 442 return ipmi::ccReqDataLenInvalid; 443 } 444 std::stringstream result; 445 std::copy((msgPayloadStartingPos + macOffset), 446 (msgPayloadStartingPos + macOffset + 5), 447 std::ostream_iterator<int>(result, ":")); 448 mac = result.str(); 449 450 if (msgLen < addrTypeOffset + sizeof(decltype(addrOrigin))) 451 { 452 lg2::error( 453 "Error in original address getting of setHostNetworkData"); 454 return ipmi::ccReqDataLenInvalid; 455 } 456 std::copy(msgPayloadStartingPos + addrTypeOffset, 457 msgPayloadStartingPos + addrTypeOffset + 458 sizeof(decltype(addrOrigin)), 459 std::ostream_iterator<int>(result, "")); 460 addrOrigin = result.str(); 461 462 if (!addrOrigin.empty()) 463 { 464 addressOrigin = 465 "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 466 } 467 468 if (msgLen < addrSizeOffset + sizeof(decltype(addrSize))) 469 { 470 lg2::error( 471 "Error in address size getting of setHostNetworkData"); 472 return ipmi::ccReqDataLenInvalid; 473 } 474 // Get the address size 475 std::copy(msgPayloadStartingPos + addrSizeOffset, 476 (msgPayloadStartingPos + addrSizeOffset + 477 sizeof(decltype(addrSize))), 478 &addrSize); 479 480 uint8_t prefixOffset = ipAddrOffset + addrSize; 481 if (msgLen < prefixOffset + sizeof(decltype(prefix))) 482 { 483 lg2::error("Error in prefix getting of setHostNetworkData"); 484 return ipmi::ccReqDataLenInvalid; 485 } 486 // std::copy(msgPayloadStartingPos + prefixOffset, 487 // msgPayloadStartingPos + prefixOffset + 488 // sizeof(decltype(prefix)), 489 // &prefix); 490 // Workaround compiler misdetecting out of bounds memcpy 491 prefix = msgPayloadStartingPos[prefixOffset]; 492 493 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); 494 if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE) 495 { 496 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; 497 family = AF_INET6; 498 } 499 500 if (msgLen < ipAddrOffset + addrSize) 501 { 502 lg2::error("Error in IP address getting of setHostNetworkData"); 503 return ipmi::ccReqDataLenInvalid; 504 } 505 ipAddress = getAddrStr(family, msgPayloadStartingPos, ipAddrOffset, 506 addrSize); 507 508 if (msgLen < gatewayOffset + addrSize) 509 { 510 lg2::error( 511 "Error in gateway address getting of setHostNetworkData"); 512 return ipmi::ccReqDataLenInvalid; 513 } 514 gateway = getAddrStr(family, msgPayloadStartingPos, gatewayOffset, 515 addrSize); 516 517 } while (0); 518 519 // Cookie == 0 or it is a valid cookie 520 hostNetworkConfig += 521 "ipaddress="s + ipAddress + ",prefix="s + std::to_string(prefix) + 522 ",gateway="s + gateway + ",mac="s + mac + ",addressOrigin="s + 523 addressOrigin; 524 525 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 526 527 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 528 SETTINGS_ROOT, SETTINGS_MATCH); 529 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 530 SETTINGS_ROOT, SETTINGS_MATCH); 531 // set the dbus property 532 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 533 IP_INTERFACE, "Address", std::string(ipAddress)); 534 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 535 IP_INTERFACE, "PrefixLength", prefix); 536 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 537 IP_INTERFACE, "Origin", addressOrigin); 538 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 539 IP_INTERFACE, "Gateway", std::string(gateway)); 540 ipmi::setDbusProperty( 541 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type", 542 std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4")); 543 ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first, 544 MAC_INTERFACE, "MACAddress", std::string(mac)); 545 546 lg2::debug("Network configuration changed: {NETWORKCONFIG}", 547 "NETWORKCONFIG", hostNetworkConfig); 548 } 549 catch (const sdbusplus::exception_t& e) 550 { 551 commit<InternalFailure>(); 552 lg2::error("Error in ipmiChassisSetSysBootOptions call"); 553 return ipmi::ccUnspecifiedError; 554 } 555 556 return ipmi::ccSuccess; 557 } 558 559 uint32_t getPOHCounter() 560 { 561 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 562 563 auto chassisStateObj = 564 ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match); 565 566 auto service = 567 ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first); 568 569 auto propValue = 570 ipmi::getDbusProperty(bus, service, chassisStateObj.first, 571 chassisPOHStateIntf, pohCounterProperty); 572 573 return std::get<uint32_t>(propValue); 574 } 575 576 /** @brief Implements the get chassis capabilities command 577 * 578 * @returns IPMI completion code plus response data 579 * chassisCapFlags - chassis capability flag 580 * chassisFRUInfoDevAddr - chassis FRU info Device Address 581 * chassisSDRDevAddr - chassis SDR device address 582 * chassisSELDevAddr - chassis SEL device address 583 * chassisSMDevAddr - chassis system management device address 584 * chassisBridgeDevAddr - chassis bridge device address 585 */ 586 ipmi::RspType<bool, // chassis intrusion sensor 587 bool, // chassis Front panel lockout 588 bool, // chassis NMI 589 bool, // chassis power interlock 590 uint4_t, // reserved 591 uint8_t, // chassis FRU info Device Address 592 uint8_t, // chassis SDR device address 593 uint8_t, // chassis SEL device address 594 uint8_t, // chassis system management device address 595 uint8_t // chassis bridge device address 596 > 597 ipmiGetChassisCap() 598 { 599 ipmi::PropertyMap properties; 600 try 601 { 602 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 603 604 ipmi::DbusObjectInfo chassisCapObject = 605 ipmi::getDbusObject(bus, chassisCapIntf); 606 607 // capabilities flags 608 // [7..4] - reserved 609 // [3] – 1b = provides power interlock (IPM 1.5) 610 // [2] – 1b = provides Diagnostic Interrupt (FP NMI) 611 // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis 612 // has capabilities 613 // to lock out external power control and reset button or 614 // front panel interfaces and/or detect tampering with those 615 // interfaces). 616 // [0] -1b = Chassis provides intrusion (physical security) sensor. 617 // set to default value 0x0. 618 619 properties = 620 ipmi::getAllDbusProperties(bus, chassisCapObject.second, 621 chassisCapObject.first, chassisCapIntf); 622 } 623 catch (const std::exception& e) 624 { 625 lg2::error("Failed to fetch Chassis Capability properties: {ERROR}", 626 "ERROR", e); 627 return ipmi::responseUnspecifiedError(); 628 } 629 630 bool* chassisIntrusionFlag = 631 std::get_if<bool>(&properties[chassisIntrusionProp]); 632 if (chassisIntrusionFlag == nullptr) 633 { 634 lg2::error("Error to get chassis Intrusion flags"); 635 return ipmi::responseUnspecifiedError(); 636 } 637 bool* chassisFrontPanelFlag = 638 std::get_if<bool>(&properties[chassisFrontPanelLockoutProp]); 639 if (chassisFrontPanelFlag == nullptr) 640 { 641 lg2::error("Error to get chassis intrusion flags"); 642 return ipmi::responseUnspecifiedError(); 643 } 644 bool* chassisNMIFlag = std::get_if<bool>(&properties[chassisNMIProp]); 645 if (chassisNMIFlag == nullptr) 646 { 647 lg2::error("Error to get chassis NMI flags"); 648 return ipmi::responseUnspecifiedError(); 649 } 650 bool* chassisPowerInterlockFlag = 651 std::get_if<bool>(&properties[chassisPowerInterlockProp]); 652 if (chassisPowerInterlockFlag == nullptr) 653 { 654 lg2::error("Error to get chassis power interlock flags"); 655 return ipmi::responseUnspecifiedError(); 656 } 657 uint8_t* chassisFRUInfoDevAddr = 658 std::get_if<uint8_t>(&properties[chassisFRUDevAddrProp]); 659 if (chassisFRUInfoDevAddr == nullptr) 660 { 661 lg2::error("Error to get chassis FRU info device address"); 662 return ipmi::responseUnspecifiedError(); 663 } 664 uint8_t* chassisSDRDevAddr = 665 std::get_if<uint8_t>(&properties[chassisSDRDevAddrProp]); 666 if (chassisSDRDevAddr == nullptr) 667 { 668 lg2::error("Error to get chassis SDR device address"); 669 return ipmi::responseUnspecifiedError(); 670 } 671 uint8_t* chassisSELDevAddr = 672 std::get_if<uint8_t>(&properties[chassisSELDevAddrProp]); 673 if (chassisSELDevAddr == nullptr) 674 { 675 lg2::error("Error to get chassis SEL device address"); 676 return ipmi::responseUnspecifiedError(); 677 } 678 uint8_t* chassisSMDevAddr = 679 std::get_if<uint8_t>(&properties[chassisSMDevAddrProp]); 680 if (chassisSMDevAddr == nullptr) 681 { 682 lg2::error("Error to get chassis SM device address"); 683 return ipmi::responseUnspecifiedError(); 684 } 685 uint8_t* chassisBridgeDevAddr = 686 std::get_if<uint8_t>(&properties[chassisBridgeDevAddrProp]); 687 if (chassisBridgeDevAddr == nullptr) 688 { 689 lg2::error("Error to get chassis bridge device address"); 690 return ipmi::responseUnspecifiedError(); 691 } 692 693 return ipmi::responseSuccess( 694 *chassisIntrusionFlag, *chassisFrontPanelFlag, *chassisNMIFlag, 695 *chassisPowerInterlockFlag, 0, *chassisFRUInfoDevAddr, 696 *chassisSDRDevAddr, *chassisSELDevAddr, *chassisSMDevAddr, 697 *chassisBridgeDevAddr); 698 } 699 700 /** @brief implements set chassis capalibities command 701 * @param intrusion - chassis intrusion 702 * @param fpLockout - frontpannel lockout 703 * @param reserved1 - skip one bit 704 * @param fruDeviceAddr - chassis FRU info Device Address 705 * @param sdrDeviceAddr - chassis SDR device address 706 * @param selDeviceAddr - chassis SEL device address 707 * @param smDeviceAddr - chassis system management device address 708 * @param bridgeDeviceAddr - chassis bridge device address 709 * 710 * @returns IPMI completion code 711 */ 712 ipmi::RspType<> ipmiSetChassisCap( 713 bool intrusion, bool fpLockout, uint6_t reserved1, 714 715 uint8_t fruDeviceAddr, 716 717 uint8_t sdrDeviceAddr, 718 719 uint8_t selDeviceAddr, 720 721 uint8_t smDeviceAddr, 722 723 uint8_t bridgeDeviceAddr) 724 { 725 // check input data 726 if (reserved1 != 0) 727 { 728 lg2::error("Unsupported request parameter"); 729 return ipmi::responseInvalidFieldRequest(); 730 } 731 732 if ((fruDeviceAddr & ~chassisCapAddrMask) != 0) 733 { 734 lg2::error("Unsupported request parameter(FRU Addr) for REQ={REQ}", 735 "REQ", lg2::hex, fruDeviceAddr); 736 return ipmi::responseInvalidFieldRequest(); 737 } 738 if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0) 739 { 740 lg2::error("Unsupported request parameter(SDR Addr) for REQ={REQ}", 741 "REQ", lg2::hex, sdrDeviceAddr); 742 return ipmi::responseInvalidFieldRequest(); 743 } 744 745 if ((selDeviceAddr & ~chassisCapAddrMask) != 0) 746 { 747 lg2::error("Unsupported request parameter(SEL Addr) for REQ={REQ}", 748 "REQ", lg2::hex, selDeviceAddr); 749 return ipmi::responseInvalidFieldRequest(); 750 } 751 752 if ((smDeviceAddr & ~chassisCapAddrMask) != 0) 753 { 754 lg2::error("Unsupported request parameter(SM Addr) for REQ={REQ}", 755 "REQ", lg2::hex, smDeviceAddr); 756 return ipmi::responseInvalidFieldRequest(); 757 } 758 759 if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0) 760 { 761 lg2::error("Unsupported request parameter(Bridge Addr) for REQ={REQ}", 762 "REQ", lg2::hex, bridgeDeviceAddr); 763 return ipmi::responseInvalidFieldRequest(); 764 } 765 766 try 767 { 768 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 769 ipmi::DbusObjectInfo chassisCapObject = 770 ipmi::getDbusObject(bus, chassisCapIntf); 771 772 ipmi::setDbusProperty(bus, chassisCapObject.second, 773 chassisCapObject.first, chassisCapIntf, 774 chassisIntrusionProp, intrusion); 775 776 ipmi::setDbusProperty(bus, chassisCapObject.second, 777 chassisCapObject.first, chassisCapIntf, 778 chassisFrontPanelLockoutProp, fpLockout); 779 780 ipmi::setDbusProperty(bus, chassisCapObject.second, 781 chassisCapObject.first, chassisCapIntf, 782 chassisFRUDevAddrProp, fruDeviceAddr); 783 784 ipmi::setDbusProperty(bus, chassisCapObject.second, 785 chassisCapObject.first, chassisCapIntf, 786 chassisSDRDevAddrProp, sdrDeviceAddr); 787 788 ipmi::setDbusProperty(bus, chassisCapObject.second, 789 chassisCapObject.first, chassisCapIntf, 790 chassisSELDevAddrProp, selDeviceAddr); 791 792 ipmi::setDbusProperty(bus, chassisCapObject.second, 793 chassisCapObject.first, chassisCapIntf, 794 chassisSMDevAddrProp, smDeviceAddr); 795 796 ipmi::setDbusProperty(bus, chassisCapObject.second, 797 chassisCapObject.first, chassisCapIntf, 798 chassisBridgeDevAddrProp, bridgeDeviceAddr); 799 } 800 catch (const std::exception& e) 801 { 802 lg2::error("Failed to set chassis capability properties: {ERR}", "ERR", 803 e); 804 return ipmi::responseUnspecifiedError(); 805 } 806 return ipmi::responseSuccess(); 807 } 808 809 //------------------------------------------ 810 // Calls into Host State Manager Dbus object 811 //------------------------------------------ 812 int initiateHostStateTransition(ipmi::Context::ptr& ctx, 813 State::Host::Transition transition) 814 { 815 // OpenBMC Host State Manager dbus framework 816 constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; 817 constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host"; 818 819 // Convert to string equivalent of the passed in transition enum. 820 auto request = 821 sdbusplus::common::xyz::openbmc_project::state::convertForMessage( 822 transition); 823 824 std::string service; 825 boost::system::error_code ec = 826 ipmi::getService(ctx, hostStateIntf, hostStatePath, service); 827 828 if (!ec) 829 { 830 ec = ipmi::setDbusProperty(ctx, service, hostStatePath, hostStateIntf, 831 "RequestedHostTransition", request); 832 } 833 if (ec) 834 { 835 lg2::error( 836 "Failed to initiate transition for request {REQUEST}: {EXCEPTION}", 837 "REQUEST", request, "EXCEPTION", ec.message()); 838 return -1; 839 } 840 lg2::info( 841 "Transition request {REQUEST} initiated successfully by user {USERID}", 842 "REQUEST", request, "USERID", ctx->userId); 843 return 0; 844 } 845 846 //------------------------------------------ 847 // Calls into Chassis State Manager Dbus object 848 //------------------------------------------ 849 int initiateChassisStateTransition(ipmi::Context::ptr& ctx, 850 State::Chassis::Transition transition) 851 { 852 // OpenBMC Chassis State Manager dbus framework 853 constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0"; 854 constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis"; 855 856 std::string service; 857 boost::system::error_code ec = 858 ipmi::getService(ctx, chassisStateIntf, chassisStatePath, service); 859 860 // Convert to string equivalent of the passed in transition enum. 861 auto request = 862 sdbusplus::common::xyz::openbmc_project::state::convertForMessage( 863 transition); 864 865 if (!ec) 866 { 867 ec = ipmi::setDbusProperty(ctx, service, chassisStatePath, 868 chassisStateIntf, "RequestedPowerTransition", 869 request); 870 } 871 if (ec) 872 { 873 lg2::error("Failed to initiate transition {REQUEST}: {EXCEPTION}", 874 "REQUEST", request, "EXCEPTION", ec.message()); 875 876 return -1; 877 } 878 879 return 0; 880 } 881 882 //------------------------------------------ 883 // Trigger an NMI on the host via dbus 884 //------------------------------------------ 885 static int doNmi(ipmi::Context::ptr& ctx) 886 { 887 constexpr const char* nmiIntfName = "xyz.openbmc_project.Control.Host.NMI"; 888 ipmi::DbusObjectInfo nmiObj{}; 889 boost::system::error_code ec; 890 891 ec = ipmi::getDbusObject(ctx, nmiIntfName, nmiObj); 892 if (ec) 893 { 894 lg2::error("Failed to find NMI service: {ERROR}", "ERROR", 895 ec.message()); 896 return -1; 897 } 898 899 ec = ipmi::callDbusMethod(ctx, nmiObj.second, nmiObj.first, nmiIntfName, 900 "NMI"); 901 if (ec) 902 { 903 lg2::error("NMI call failed: {ERROR}", "ERROR", ec.message()); 904 elog<InternalFailure>(); 905 return -1; 906 } 907 908 return 0; 909 } 910 911 namespace power_policy 912 { 913 914 using namespace sdbusplus::server::xyz::openbmc_project::control::power; 915 using IpmiValue = uint8_t; 916 using DbusValue = RestorePolicy::Policy; 917 918 const std::map<DbusValue, IpmiValue> dbusToIpmi = { 919 {RestorePolicy::Policy::AlwaysOff, 0x00}, 920 {RestorePolicy::Policy::Restore, 0x01}, 921 {RestorePolicy::Policy::AlwaysOn, 0x02}, 922 {RestorePolicy::Policy::None, 0x03}}; 923 924 static constexpr uint8_t noChange = 0x03; 925 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; 926 927 /* helper function for Get Chassis Status Command 928 */ 929 std::optional<uint2_t> getPowerRestorePolicy() 930 { 931 uint2_t restorePolicy = 0; 932 using namespace chassis::internal; 933 934 settings::Objects& objects = cache::getObjects(); 935 936 try 937 { 938 const auto& powerRestoreSetting = 939 objects.map.at(powerRestoreIntf).front(); 940 ipmi::Value result = ipmi::getDbusProperty( 941 *getSdBus(), 942 objects.service(powerRestoreSetting, powerRestoreIntf).c_str(), 943 powerRestoreSetting.c_str(), powerRestoreIntf, 944 "PowerRestorePolicy"); 945 auto powerRestore = RestorePolicy::convertPolicyFromString( 946 std::get<std::string>(result)); 947 restorePolicy = dbusToIpmi.at(powerRestore); 948 } 949 catch (const std::exception& e) 950 { 951 lg2::error( 952 "Failed to fetch pgood property ({PATH}/{INTERFACE}): {ERROR}", 953 "PATH", objects.map.at(powerRestoreIntf).front(), "INTERFACE", 954 powerRestoreIntf, "ERROR", e); 955 cache::objectsPtr.reset(); 956 return std::nullopt; 957 } 958 return std::make_optional(restorePolicy); 959 } 960 961 /* 962 * getPowerStatus 963 * helper function for Get Chassis Status Command 964 * return - optional value for pgood (no value on error) 965 */ 966 std::optional<bool> getPowerStatus() 967 { 968 bool powerGood = false; 969 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 970 try 971 { 972 constexpr const char* chassisStatePath = 973 "/xyz/openbmc_project/state/chassis0"; 974 constexpr const char* chassisStateIntf = 975 "xyz.openbmc_project.State.Chassis"; 976 auto service = 977 ipmi::getService(*busp, chassisStateIntf, chassisStatePath); 978 979 ipmi::Value powerState = 980 ipmi::getDbusProperty(*busp, service, chassisStatePath, 981 chassisStateIntf, "CurrentPowerState"); 982 std::string powerStateStr = std::get<std::string>(powerState); 983 if (powerStateStr.ends_with(".On") || 984 powerStateStr.ends_with(".TransitioningToOff")) 985 { 986 powerGood = true; 987 } 988 } 989 catch (const std::exception& e) 990 { 991 try 992 { 993 // FIXME: some legacy modules use the older path; try that next 994 constexpr const char* legacyPwrCtrlObj = 995 "/org/openbmc/control/power0"; 996 constexpr const char* legacyPwrCtrlIntf = 997 "org.openbmc.control.Power"; 998 auto service = 999 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj); 1000 1001 ipmi::Value variant = ipmi::getDbusProperty( 1002 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood"); 1003 powerGood = static_cast<bool>(std::get<int>(variant)); 1004 } 1005 catch (const std::exception& e) 1006 { 1007 lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e); 1008 return std::nullopt; 1009 } 1010 } 1011 return std::make_optional(powerGood); 1012 } 1013 1014 /* 1015 * getACFailStatus 1016 * helper function for Get Chassis Status Command 1017 * return - bool value for ACFail (false on error) 1018 */ 1019 bool getACFailStatus() 1020 { 1021 constexpr const char* powerControlObj = 1022 "/xyz/openbmc_project/Chassis/Control/Power0"; 1023 constexpr const char* powerControlIntf = 1024 "xyz.openbmc_project.Chassis.Control.Power"; 1025 bool acFail = false; 1026 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1027 try 1028 { 1029 auto service = 1030 ipmi::getService(*bus, powerControlIntf, powerControlObj); 1031 1032 ipmi::Value variant = ipmi::getDbusProperty( 1033 *bus, service, powerControlObj, powerControlIntf, "PFail"); 1034 acFail = std::get<bool>(variant); 1035 } 1036 catch (const std::exception& e) 1037 { 1038 lg2::error( 1039 "Failed to fetch PFail property ({PATH}/{INTERFACE}): {ERROR}", 1040 "PATH", powerControlObj, "INTERFACE", powerControlIntf, "ERROR", e); 1041 } 1042 return acFail; 1043 } 1044 } // namespace power_policy 1045 1046 static std::optional<bool> getButtonDisabled(ipmi::Context::ptr& ctx, 1047 const std::string& buttonPath, 1048 const std::string& buttonIntf) 1049 { 1050 bool buttonDisabled = false; 1051 boost::system::error_code ec; 1052 std::string service; 1053 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service); 1054 if (!ec) 1055 { 1056 bool enabled; 1057 ec = ipmi::getDbusProperty(ctx, service, buttonPath, buttonIntf, 1058 "Enabled", enabled); 1059 buttonDisabled = !enabled; 1060 } 1061 1062 if (ec) 1063 { 1064 lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}", 1065 "PATH", buttonPath, "ERROR", ec.message()); 1066 return std::nullopt; 1067 } 1068 return std::make_optional(buttonDisabled); 1069 } 1070 1071 static bool setButtonDisabled(ipmi::Context::ptr& ctx, 1072 const std::string& buttonPath, 1073 const std::string& buttonIntf, bool disable) 1074 { 1075 std::string service; 1076 boost::system::error_code ec; 1077 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service); 1078 if (!ec) 1079 { 1080 ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf, 1081 "Enabled", !disable); 1082 } 1083 if (ec) 1084 { 1085 lg2::error( 1086 "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}", 1087 "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message()); 1088 return false; 1089 } 1090 return true; 1091 } 1092 1093 static std::optional<bool> getChassisIntrusionStatus(ipmi::Context::ptr& ctx) 1094 { 1095 std::vector<std::string> interfaces = {std::string(Intrusion::interface)}; 1096 ipmi::ObjectTree objs; 1097 std::string propVal; 1098 std::optional<bool> ret = std::nullopt; 1099 1100 boost::system::error_code ec = 1101 ipmi::getSubTree(ctx, interfaces, std::string("/"), 0, objs); 1102 1103 if (ec) 1104 { 1105 lg2::error("Fail to find Chassis Intrusion Interface on D-Bus " 1106 "({INTERFACE}): {ERROR}", 1107 "INTERFACE", Intrusion::interface, "ERROR", ec.message()); 1108 return ret; 1109 } 1110 1111 for (const auto& [path, map] : objs) 1112 { 1113 for (const auto& [service, intfs] : map) 1114 { 1115 ec = ipmi::getDbusProperty<std::string>( 1116 ctx, service, path, Intrusion::interface, "Status", propVal); 1117 1118 if (ec) 1119 { 1120 lg2::error("Failed to get Chassis Intrusion Status property " 1121 "({SERVICE}/{PATH}/{INTERFACE}): {ERROR}", 1122 "SERVICE", service, "PATH", path, "INTERFACE", 1123 Intrusion::interface, "ERROR", ec.message()); 1124 continue; 1125 } 1126 1127 auto statusOpt = 1128 sdbusplus::message::convert_from_string<Intrusion::Status>( 1129 propVal); 1130 if (statusOpt) 1131 { 1132 if (*statusOpt == Intrusion::Status::Normal) 1133 { 1134 ret = std::make_optional(false); 1135 } 1136 else 1137 { 1138 ret = std::make_optional(true); 1139 return ret; // Early return on first non-Normal status 1140 } 1141 } 1142 else 1143 { 1144 lg2::warning( 1145 "Invalid Intrusion::Status value received: {VALUE}", 1146 "VALUE", propVal); 1147 return std::nullopt; 1148 } 1149 } 1150 } 1151 return ret; 1152 } 1153 1154 //---------------------------------------------------------------------- 1155 // Get Chassis Status commands 1156 //---------------------------------------------------------------------- 1157 ipmi::RspType<bool, // Power is on 1158 bool, // Power overload 1159 bool, // Interlock 1160 bool, // power fault 1161 bool, // power control fault 1162 uint2_t, // power restore policy 1163 bool, // reserved 1164 1165 bool, // AC failed 1166 bool, // last power down caused by a Power overload 1167 bool, // last power down caused by a power interlock 1168 bool, // last power down caused by power fault 1169 bool, // last ‘Power is on’ state was entered via IPMI command 1170 uint3_t, // reserved 1171 1172 bool, // Chassis intrusion active 1173 bool, // Front Panel Lockout active 1174 bool, // Drive Fault 1175 bool, // Cooling/fan fault detected 1176 uint2_t, // Chassis Identify State 1177 bool, // Chassis Identify command and state info supported 1178 bool, // reserved 1179 1180 bool, // Power off button disabled 1181 bool, // Reset button disabled 1182 bool, // Diagnostic Interrupt button disabled 1183 bool, // Standby (sleep) button disabled 1184 bool, // Power off button disable allowed 1185 bool, // Reset button disable allowed 1186 bool, // Diagnostic Interrupt button disable allowed 1187 bool // Standby (sleep) button disable allowed 1188 > 1189 ipmiGetChassisStatus(ipmi::Context::ptr& ctx) 1190 { 1191 using namespace chassis::internal; 1192 std::optional<uint2_t> restorePolicy = 1193 power_policy::getPowerRestorePolicy(); 1194 std::optional<bool> powerGood = power_policy::getPowerStatus(); 1195 if (!restorePolicy || !powerGood) 1196 { 1197 return ipmi::responseUnspecifiedError(); 1198 } 1199 1200 // Front Panel Button Capabilities and disable/enable status(Optional) 1201 std::optional<bool> powerButtonReading = 1202 getButtonDisabled(ctx, powerButtonPath, powerButtonIntf); 1203 // allow disable if the interface is present 1204 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading); 1205 // default return the button is enabled (not disabled) 1206 bool powerButtonDisabled = false; 1207 if (powerButtonDisableAllow) 1208 { 1209 // return the real value of the button status, if present 1210 powerButtonDisabled = *powerButtonReading; 1211 } 1212 1213 std::optional<bool> resetButtonReading = 1214 getButtonDisabled(ctx, resetButtonPath, resetButtonIntf); 1215 // allow disable if the interface is present 1216 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading); 1217 // default return the button is enabled (not disabled) 1218 bool resetButtonDisabled = false; 1219 if (resetButtonDisableAllow) 1220 { 1221 // return the real value of the button status, if present 1222 resetButtonDisabled = *resetButtonReading; 1223 } 1224 1225 bool powerDownAcFailed = power_policy::getACFailStatus(); 1226 1227 bool chassisIntrusionActive = false; 1228 std::optional<bool> chassisIntrusionStatus = getChassisIntrusionStatus(ctx); 1229 if (chassisIntrusionStatus) 1230 { 1231 chassisIntrusionActive = chassisIntrusionStatus.value(); 1232 } 1233 1234 // This response has a lot of hard-coded, unsupported fields 1235 // They are set to false or 0 1236 constexpr bool powerOverload = false; 1237 constexpr bool chassisInterlock = false; 1238 constexpr bool powerFault = false; 1239 constexpr bool powerControlFault = false; 1240 constexpr bool powerDownOverload = false; 1241 constexpr bool powerDownInterlock = false; 1242 constexpr bool powerDownPowerFault = false; 1243 constexpr bool powerStatusIPMI = false; 1244 constexpr bool frontPanelLockoutActive = false; 1245 constexpr bool driveFault = false; 1246 constexpr bool coolingFanFault = false; 1247 // chassisIdentifySupport set because this command is implemented 1248 constexpr bool chassisIdentifySupport = true; 1249 uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState); 1250 constexpr bool diagButtonDisabled = false; 1251 constexpr bool sleepButtonDisabled = false; 1252 constexpr bool diagButtonDisableAllow = false; 1253 constexpr bool sleepButtonDisableAllow = false; 1254 1255 return ipmi::responseSuccess( 1256 *powerGood, powerOverload, chassisInterlock, powerFault, 1257 powerControlFault, *restorePolicy, 1258 false, // reserved 1259 1260 powerDownAcFailed, powerDownOverload, powerDownInterlock, 1261 powerDownPowerFault, powerStatusIPMI, 1262 uint3_t(0), // reserved 1263 1264 chassisIntrusionActive, frontPanelLockoutActive, driveFault, 1265 coolingFanFault, chassisIdentifyState, chassisIdentifySupport, 1266 false, // reserved 1267 1268 powerButtonDisabled, resetButtonDisabled, diagButtonDisabled, 1269 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow, 1270 diagButtonDisableAllow, sleepButtonDisableAllow); 1271 } 1272 1273 enum class IpmiRestartCause 1274 { 1275 Unknown = 0x0, 1276 RemoteCommand = 0x1, 1277 ResetButton = 0x2, 1278 PowerButton = 0x3, 1279 WatchdogTimer = 0x4, 1280 PowerPolicyAlwaysOn = 0x6, 1281 PowerPolicyPreviousState = 0x7, 1282 SoftReset = 0xa, 1283 }; 1284 1285 static IpmiRestartCause restartCauseToIpmiRestartCause( 1286 State::Host::RestartCause cause) 1287 { 1288 switch (cause) 1289 { 1290 case State::Host::RestartCause::Unknown: 1291 { 1292 return IpmiRestartCause::Unknown; 1293 } 1294 case State::Host::RestartCause::RemoteCommand: 1295 { 1296 return IpmiRestartCause::RemoteCommand; 1297 } 1298 case State::Host::RestartCause::ResetButton: 1299 { 1300 return IpmiRestartCause::ResetButton; 1301 } 1302 case State::Host::RestartCause::PowerButton: 1303 { 1304 return IpmiRestartCause::PowerButton; 1305 } 1306 case State::Host::RestartCause::WatchdogTimer: 1307 { 1308 return IpmiRestartCause::WatchdogTimer; 1309 } 1310 case State::Host::RestartCause::PowerPolicyAlwaysOn: 1311 { 1312 return IpmiRestartCause::PowerPolicyAlwaysOn; 1313 } 1314 case State::Host::RestartCause::PowerPolicyPreviousState: 1315 { 1316 return IpmiRestartCause::PowerPolicyPreviousState; 1317 } 1318 case State::Host::RestartCause::SoftReset: 1319 { 1320 return IpmiRestartCause::SoftReset; 1321 } 1322 default: 1323 { 1324 return IpmiRestartCause::Unknown; 1325 } 1326 } 1327 } 1328 1329 /* 1330 * getRestartCause 1331 * helper function for Get Host restart cause Command 1332 * return - optional value for RestartCause (no value on error) 1333 */ 1334 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx) 1335 { 1336 constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0"; 1337 constexpr const char* restartCauseIntf = "xyz.openbmc_project.State.Host"; 1338 1339 std::string service; 1340 boost::system::error_code ec = 1341 ipmi::getService(ctx, restartCauseIntf, restartCausePath, service); 1342 if (!ec) 1343 { 1344 std::string restartCauseStr; 1345 ec = ipmi::getDbusProperty<std::string>( 1346 ctx, service, restartCausePath, restartCauseIntf, "RestartCause", 1347 restartCauseStr); 1348 if (!ec) 1349 { 1350 auto cause = 1351 State::Host::convertRestartCauseFromString(restartCauseStr); 1352 return types::enum_cast<uint4_t>( 1353 restartCauseToIpmiRestartCause(cause)); 1354 } 1355 } 1356 1357 lg2::error( 1358 "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}", 1359 "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE", 1360 restartCauseIntf); 1361 return std::nullopt; 1362 } 1363 1364 ipmi::RspType<uint4_t, // Restart Cause 1365 uint4_t, // reserved 1366 uint8_t // channel number (not supported) 1367 > 1368 ipmiGetSystemRestartCause(ipmi::Context::ptr ctx) 1369 { 1370 std::optional<uint4_t> cause = getRestartCause(ctx); 1371 if (!cause) 1372 { 1373 return ipmi::responseUnspecifiedError(); 1374 } 1375 1376 constexpr uint4_t reserved = 0; 1377 auto channel = static_cast<uint8_t>(ctx->channel); 1378 return ipmi::responseSuccess(cause.value(), reserved, channel); 1379 } 1380 /** @brief Implementation of chassis control command 1381 * 1382 * @param - chassisControl command byte 1383 * 1384 * @return Success or InvalidFieldRequest. 1385 */ 1386 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx, 1387 uint8_t chassisControl) 1388 { 1389 int rc = 0; 1390 switch (chassisControl) 1391 { 1392 case cmdPowerOn: 1393 rc = initiateHostStateTransition(ctx, State::Host::Transition::On); 1394 break; 1395 case cmdPowerOff: 1396 rc = initiateChassisStateTransition( 1397 ctx, State::Chassis::Transition::Off); 1398 break; 1399 case cmdHardReset: 1400 rc = initiateHostStateTransition( 1401 ctx, State::Host::Transition::ForceWarmReboot); 1402 break; 1403 case cmdPowerCycle: 1404 { 1405 auto powerState = power_policy::getPowerStatus(); 1406 1407 if (powerState == std::nullopt) 1408 { 1409 return ipmi::responseUnspecifiedError(); 1410 } 1411 1412 /* 1413 * As define in the Chapter 28.3 - Chassis Control Command of IPMI 1414 * specification: It is recommended that no action occur if system 1415 * power is off (S4/S5) when this action is selected, and that a D5 1416 * "Requiest parameter(s) not supported in this presenst state." 1417 * error completion code be returned. 1418 */ 1419 if (powerState.value() == false) 1420 { 1421 return ipmi::responseCommandNotAvailable(); 1422 } 1423 1424 rc = initiateHostStateTransition(ctx, 1425 State::Host::Transition::Reboot); 1426 break; 1427 } 1428 case cmdSoftOffViaOverTemp: 1429 rc = initiateHostStateTransition(ctx, State::Host::Transition::Off); 1430 break; 1431 case cmdPulseDiagnosticInterrupt: 1432 rc = doNmi(ctx); 1433 break; 1434 1435 default: 1436 { 1437 lg2::error("Invalid Chassis Control command: {CMD}", "CMD", 1438 lg2::hex, chassisControl); 1439 return ipmi::responseInvalidFieldRequest(); 1440 } 1441 } 1442 1443 return ((rc < 0) ? ipmi::responseUnspecifiedError() 1444 : ipmi::responseSuccess()); 1445 } 1446 1447 /** @brief Return D-Bus connection string to enclosure identify LED object 1448 * 1449 * @param[in, out] connection - connection to D-Bus object 1450 * @return a IPMI return code 1451 */ 1452 std::string getEnclosureIdentifyConnection() 1453 { 1454 // lookup enclosure_identify group owner(s) in mapper 1455 try 1456 { 1457 return ipmi::getService(*getSdBus(), "xyz.openbmc_project.Led.Group", 1458 identify_led_object_name); 1459 } 1460 catch (const std::exception& e) 1461 { 1462 lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}", 1463 "ERROR", e); 1464 elog<InternalFailure>(); 1465 } 1466 } 1467 1468 /** @brief Turn On/Off enclosure identify LED 1469 * 1470 * @param[in] flag - true to turn on LED, false to turn off 1471 * @return a IPMI return code 1472 */ 1473 void enclosureIdentifyLed(bool flag) 1474 { 1475 using namespace chassis::internal; 1476 1477 std::string connection = getEnclosureIdentifyConnection(); 1478 1479 lg2::debug("enclosureIdentifyLed({FLAG})", "FLAG", flag); 1480 1481 try 1482 { 1483 ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name, 1484 "xyz.openbmc_project.Led.Group", "Asserted", 1485 flag); 1486 } 1487 catch (const std::exception& e) 1488 { 1489 lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}", 1490 "LED_STATE", flag, "ERROR", e); 1491 elog<InternalFailure>(); 1492 } 1493 } 1494 1495 /** @brief Callback method to turn off LED 1496 */ 1497 void enclosureIdentifyLedOff() 1498 { 1499 try 1500 { 1501 chassisIDState = ChassisIDState::off; 1502 enclosureIdentifyLed(false); 1503 } 1504 catch (const InternalFailure& e) 1505 { 1506 report<InternalFailure>(); 1507 } 1508 } 1509 1510 /** @brief Create timer to turn on and off the enclosure LED 1511 */ 1512 void createIdentifyTimer() 1513 { 1514 if (!identifyTimer) 1515 { 1516 identifyTimer = 1517 std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedOff); 1518 } 1519 } 1520 1521 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, 1522 std::optional<uint8_t> force) 1523 { 1524 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); 1525 bool forceIdentify = force.value_or(0) & 0x01; 1526 1527 if (identifyInterval || forceIdentify) 1528 { 1529 // stop the timer if already started; 1530 // for force identify we should not turn off LED 1531 identifyTimer->stop(); 1532 try 1533 { 1534 chassisIDState = ChassisIDState::temporaryOn; 1535 enclosureIdentifyLed(true); 1536 } 1537 catch (const InternalFailure& e) 1538 { 1539 report<InternalFailure>(); 1540 return ipmi::responseResponseError(); 1541 } 1542 1543 if (forceIdentify) 1544 { 1545 chassisIDState = ChassisIDState::indefiniteOn; 1546 return ipmi::responseSuccess(); 1547 } 1548 // start the timer 1549 auto time = std::chrono::duration_cast<std::chrono::microseconds>( 1550 std::chrono::seconds(identifyInterval)); 1551 identifyTimer->start(time); 1552 } 1553 else if (!identifyInterval) 1554 { 1555 identifyTimer->stop(); 1556 enclosureIdentifyLedOff(); 1557 } 1558 return ipmi::responseSuccess(); 1559 } 1560 1561 namespace boot_options 1562 { 1563 1564 using namespace sdbusplus::server::xyz::openbmc_project::control::boot; 1565 using IpmiValue = uint8_t; 1566 constexpr auto ipmiDefault = 0; 1567 1568 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy}, 1569 {0x01, Type::Types::EFI}}; 1570 1571 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 1572 {0x01, Source::Sources::Network}, 1573 {0x02, Source::Sources::Disk}, 1574 {0x05, Source::Sources::ExternalMedia}, 1575 {0x0f, Source::Sources::RemovableMedia}, 1576 {ipmiDefault, Source::Sources::Default}}; 1577 1578 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 1579 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1580 {0x03, Mode::Modes::Safe}, 1581 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1582 {0x06, Mode::Modes::Setup}, 1583 {ipmiDefault, Mode::Modes::Regular}}; 1584 1585 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00}, 1586 {Type::Types::EFI, 0x01}}; 1587 1588 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 1589 {Source::Sources::Network, 0x01}, 1590 {Source::Sources::Disk, 0x02}, 1591 {Source::Sources::ExternalMedia, 0x05}, 1592 {Source::Sources::RemovableMedia, 0x0f}, 1593 {Source::Sources::Default, ipmiDefault}}; 1594 1595 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 1596 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1597 {Mode::Modes::Safe, 0x03}, 1598 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1599 {Mode::Modes::Setup, 0x06}, 1600 {Mode::Modes::Regular, ipmiDefault}}; 1601 1602 } // namespace boot_options 1603 1604 /** @brief Get the property value for boot source 1605 * @param[in] ctx - context pointer 1606 * @param[out] source - boot source value 1607 * @return On failure return IPMI error. 1608 */ 1609 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source) 1610 { 1611 using namespace chassis::internal; 1612 std::string result; 1613 std::string service; 1614 boost::system::error_code ec = 1615 getService(ctx, bootSourceIntf, bootSettingsPath, service); 1616 if (!ec) 1617 { 1618 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, 1619 bootSourceIntf, "BootSource", result); 1620 if (!ec) 1621 { 1622 source = Source::convertSourcesFromString(result); 1623 return ipmi::ccSuccess; 1624 } 1625 } 1626 lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message()); 1627 return ipmi::ccUnspecifiedError; 1628 } 1629 1630 /** @brief Set the property value for boot source 1631 * @param[in] ctx - context pointer 1632 * @param[in] source - boot source value 1633 * @return On failure return IPMI error. 1634 */ 1635 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx, 1636 const Source::Sources& source) 1637 { 1638 using namespace chassis::internal; 1639 std::string service; 1640 boost::system::error_code ec = 1641 getService(ctx, bootSourceIntf, bootSettingsPath, service); 1642 if (!ec) 1643 { 1644 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, 1645 bootSourceIntf, "BootSource", 1646 convertForMessage(source)); 1647 if (!ec) 1648 { 1649 return ipmi::ccSuccess; 1650 } 1651 } 1652 lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message()); 1653 return ipmi::ccUnspecifiedError; 1654 } 1655 1656 /** @brief Get the property value for boot mode 1657 * @param[in] ctx - context pointer 1658 * @param[out] mode - boot mode value 1659 * @return On failure return IPMI error. 1660 */ 1661 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode) 1662 { 1663 using namespace chassis::internal; 1664 std::string result; 1665 std::string service; 1666 boost::system::error_code ec = 1667 getService(ctx, bootModeIntf, bootSettingsPath, service); 1668 if (!ec) 1669 { 1670 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, 1671 "BootMode", result); 1672 if (!ec) 1673 { 1674 mode = Mode::convertModesFromString(result); 1675 return ipmi::ccSuccess; 1676 } 1677 } 1678 lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message()); 1679 return ipmi::ccUnspecifiedError; 1680 } 1681 1682 /** @brief Set the property value for boot mode 1683 * @param[in] ctx - context pointer 1684 * @param[in] mode - boot mode value 1685 * @return On failure return IPMI error. 1686 */ 1687 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode) 1688 { 1689 using namespace chassis::internal; 1690 std::string service; 1691 boost::system::error_code ec = 1692 getService(ctx, bootModeIntf, bootSettingsPath, service); 1693 if (!ec) 1694 { 1695 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, 1696 "BootMode", convertForMessage(mode)); 1697 if (!ec) 1698 { 1699 return ipmi::ccSuccess; 1700 } 1701 } 1702 lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message()); 1703 return ipmi::ccUnspecifiedError; 1704 } 1705 1706 /** @brief Get the property value for boot type 1707 * @param[in] ctx - context pointer 1708 * @param[out] type - boot type value 1709 * @return On failure return IPMI error. 1710 */ 1711 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type) 1712 { 1713 using namespace chassis::internal; 1714 std::string result; 1715 std::string service; 1716 boost::system::error_code ec = 1717 getService(ctx, bootTypeIntf, bootSettingsPath, service); 1718 1719 // Don't throw error if BootType interface is not present. 1720 // This interface is not relevant for some Host architectures 1721 // (for example POWER). In this case we don't won't IPMI to 1722 // return an error, but simply return bootType as EFI. 1723 type = Type::Types::EFI; 1724 if (!ec) 1725 { 1726 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, 1727 "BootType", result); 1728 if (ec) 1729 { 1730 lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message()); 1731 return ipmi::ccUnspecifiedError; 1732 } 1733 type = Type::convertTypesFromString(result); 1734 } 1735 1736 return ipmi::ccSuccess; 1737 } 1738 1739 /** @brief Set the property value for boot type 1740 * @param[in] ctx - context pointer 1741 * @param[in] type - boot type value 1742 * @return On failure return IPMI error. 1743 */ 1744 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type) 1745 { 1746 using namespace chassis::internal; 1747 std::string service; 1748 boost::system::error_code ec = 1749 getService(ctx, bootTypeIntf, bootSettingsPath, service); 1750 if (!ec) 1751 { 1752 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, 1753 "BootType", convertForMessage(type)); 1754 if (ec) 1755 { 1756 lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message()); 1757 return ipmi::ccUnspecifiedError; 1758 } 1759 } 1760 // Don't throw error if BootType interface is not present. 1761 // This interface is not relevant for some Host architectures 1762 // (for example POWER). In this case we don't won't IPMI to 1763 // return an error, but want to just skip this function. 1764 return ipmi::ccSuccess; 1765 } 1766 1767 /** @brief Get the property value for boot override enable 1768 * @param[in] ctx - context pointer 1769 * @param[out] enable - boot override enable 1770 * @return On failure return IPMI error. 1771 */ 1772 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable) 1773 { 1774 using namespace chassis::internal; 1775 std::string result; 1776 std::string service; 1777 boost::system::error_code ec = 1778 getService(ctx, bootEnableIntf, bootSettingsPath, service); 1779 if (!ec) 1780 { 1781 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, 1782 bootEnableIntf, "Enabled", enable); 1783 if (!ec) 1784 { 1785 return ipmi::ccSuccess; 1786 } 1787 } 1788 lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR", 1789 ec.message()); 1790 return ipmi::ccUnspecifiedError; 1791 } 1792 1793 /** @brief Set the property value for boot override enable 1794 * @param[in] ctx - context pointer 1795 * @param[in] enable - boot override enable 1796 * @return On failure return IPMI error. 1797 */ 1798 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable) 1799 { 1800 using namespace chassis::internal; 1801 std::string service; 1802 boost::system::error_code ec = 1803 getService(ctx, bootEnableIntf, bootSettingsPath, service); 1804 if (!ec) 1805 { 1806 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, 1807 bootEnableIntf, "Enabled", enable); 1808 if (!ec) 1809 { 1810 return ipmi::ccSuccess; 1811 } 1812 } 1813 lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR", 1814 ec.message()); 1815 return ipmi::ccUnspecifiedError; 1816 } 1817 1818 /** @brief Get the property value for boot override one-time 1819 * @param[in] ctx - context pointer 1820 * @param[out] onetime - boot override one-time 1821 * @return On failure return IPMI error. 1822 */ 1823 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime) 1824 { 1825 using namespace chassis::internal; 1826 std::string result; 1827 std::string service; 1828 boost::system::error_code ec = 1829 getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); 1830 if (!ec) 1831 { 1832 ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath, 1833 bootOneTimeIntf, "Enabled", onetime); 1834 if (!ec) 1835 { 1836 return ipmi::ccSuccess; 1837 } 1838 } 1839 lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR", 1840 ec.message()); 1841 return ipmi::ccUnspecifiedError; 1842 } 1843 1844 /** @brief Set the property value for boot override one-time 1845 * @param[in] ctx - context pointer 1846 * @param[in] onetime - boot override one-time 1847 * @return On failure return IPMI error. 1848 */ 1849 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime) 1850 { 1851 using namespace chassis::internal; 1852 std::string service; 1853 boost::system::error_code ec = 1854 getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); 1855 if (!ec) 1856 { 1857 ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath, 1858 bootOneTimeIntf, "Enabled", onetime); 1859 if (!ec) 1860 { 1861 return ipmi::ccSuccess; 1862 } 1863 } 1864 lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR", 1865 ec.message()); 1866 return ipmi::ccUnspecifiedError; 1867 } 1868 1869 static constexpr uint8_t setComplete = 0x0; 1870 static constexpr uint8_t setInProgress = 0x1; 1871 static uint8_t transferStatus = setComplete; 1872 static uint8_t bootFlagValidBitClr = 0; 1873 static uint5_t bootInitiatorAckData = 0x0; 1874 static bool cmosClear = false; 1875 static uint2_t biosVerbosity = 0x0; 1876 1877 /** @brief implements the Get Chassis system boot option 1878 * @param ctx - context pointer 1879 * @param bootOptionParameter - boot option parameter selector 1880 * @param reserved1 - reserved bit 1881 * @param setSelector - selects a particular block or set of parameters 1882 * under the given parameter selector 1883 * write as 00h if parameter doesn't use a setSelector 1884 * @param blockSelector- selects a particular block within a set of 1885 * parameters write as 00h if parameter doesn't use a 1886 * blockSelector 1887 * 1888 * @return IPMI completion code plus response data 1889 * @return Payload contains below parameters: 1890 * version - parameter version 1891 * bootOptionParameter - boot option parameter selector 1892 * parmIndicator - parameter valid/invalid indicator 1893 * data - configuration parameter data 1894 */ 1895 ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions( 1896 ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1, 1897 [[maybe_unused]] uint8_t setSelector, 1898 [[maybe_unused]] uint8_t blockSelector) 1899 { 1900 ipmi::Cc rc; 1901 if (reserved1) 1902 { 1903 return ipmi::responseInvalidFieldRequest(); 1904 } 1905 1906 constexpr uint4_t version = 0x01; 1907 ipmi::message::Payload response; 1908 response.pack(version, uint4_t{}); 1909 using namespace boot_options; 1910 1911 IpmiValue bootOption = ipmiDefault; 1912 1913 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1914 BootOptionParameter::setInProgress) 1915 { 1916 response.pack(bootOptionParameter, reserved1, transferStatus); 1917 return ipmi::responseSuccess(std::move(response)); 1918 } 1919 1920 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1921 BootOptionParameter::bootInfo) 1922 { 1923 constexpr uint8_t writeMask = 0; 1924 response.pack(bootOptionParameter, reserved1, writeMask, 1925 bootInitiatorAckData); 1926 return ipmi::responseSuccess(std::move(response)); 1927 } 1928 1929 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1930 BootOptionParameter::bootFlagValidClr) 1931 { 1932 response.pack(bootOptionParameter, reserved1, 1933 uint5_t{bootFlagValidBitClr}, uint3_t{}); 1934 return ipmi::responseSuccess(std::move(response)); 1935 } 1936 1937 /* 1938 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1939 * This is the only parameter used by petitboot. 1940 */ 1941 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1942 BootOptionParameter::bootFlags) 1943 { 1944 using namespace chassis::internal; 1945 using namespace chassis::internal::cache; 1946 1947 try 1948 { 1949 Source::Sources bootSource; 1950 rc = getBootSource(ctx, bootSource); 1951 if (rc != ipmi::ccSuccess) 1952 { 1953 return ipmi::response(rc); 1954 } 1955 1956 Type::Types bootType; 1957 rc = getBootType(ctx, bootType); 1958 if (rc != ipmi::ccSuccess) 1959 { 1960 return ipmi::response(rc); 1961 } 1962 1963 Mode::Modes bootMode; 1964 rc = getBootMode(ctx, bootMode); 1965 if (rc != ipmi::ccSuccess) 1966 { 1967 return ipmi::response(rc); 1968 } 1969 1970 bootOption = sourceDbusToIpmi.at(bootSource); 1971 if ((Mode::Modes::Regular == bootMode) && 1972 (Source::Sources::Default == bootSource)) 1973 { 1974 bootOption = ipmiDefault; 1975 } 1976 else if (Source::Sources::Default == bootSource) 1977 { 1978 bootOption = modeDbusToIpmi.at(bootMode); 1979 } 1980 1981 IpmiValue biosBootType = typeDbusToIpmi.at(bootType); 1982 1983 bool oneTimeEnabled; 1984 rc = getBootOneTime(ctx, oneTimeEnabled); 1985 if (rc != ipmi::ccSuccess) 1986 { 1987 return ipmi::response(rc); 1988 } 1989 1990 uint1_t permanent = oneTimeEnabled ? 0 : 1; 1991 1992 bool valid; 1993 rc = getBootEnable(ctx, valid); 1994 if (rc != ipmi::ccSuccess) 1995 { 1996 return ipmi::response(rc); 1997 } 1998 1999 uint1_t validFlag = valid ? 1 : 0; 2000 2001 response.pack( 2002 bootOptionParameter, reserved1, uint5_t{}, 2003 uint1_t{biosBootType}, uint1_t{permanent}, uint1_t{validFlag}, 2004 uint2_t{}, uint4_t{bootOption}, uint1_t{}, cmosClear, uint5_t{}, 2005 uint2_t{biosVerbosity}, uint1_t{}, uint8_t{}, uint8_t{}); 2006 return ipmi::responseSuccess(std::move(response)); 2007 } 2008 catch (const InternalFailure& e) 2009 { 2010 cache::objectsPtr.reset(); 2011 report<InternalFailure>(); 2012 return ipmi::responseUnspecifiedError(); 2013 } 2014 } 2015 else 2016 { 2017 if ((bootOptionParameter >= oemParmStart) && 2018 (bootOptionParameter <= oemParmEnd)) 2019 { 2020 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 2021 BootOptionParameter::opalNetworkSettings) 2022 { 2023 response.pack(bootOptionParameter, reserved1); 2024 int ret = getHostNetworkData(response); 2025 if (ret < 0) 2026 { 2027 response.trailingOk = true; 2028 lg2::error( 2029 "getHostNetworkData failed for GetSysBootOptions."); 2030 return ipmi::responseUnspecifiedError(); 2031 } 2032 else 2033 { 2034 return ipmi::responseSuccess(std::move(response)); 2035 } 2036 } 2037 else 2038 { 2039 lg2::error( 2040 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 2041 "PARAM", lg2::hex, 2042 static_cast<uint8_t>(bootOptionParameter)); 2043 return ipmi::responseParmNotSupported(); 2044 } 2045 } 2046 else 2047 { 2048 lg2::error( 2049 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 2050 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter)); 2051 return ipmi::responseParmNotSupported(); 2052 } 2053 } 2054 return ipmi::responseUnspecifiedError(); 2055 } 2056 2057 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, 2058 uint7_t parameterSelector, bool, 2059 ipmi::message::Payload& data) 2060 { 2061 using namespace boot_options; 2062 ipmi::Cc rc; 2063 2064 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2065 BootOptionParameter::setInProgress) 2066 { 2067 uint2_t setInProgressFlag; 2068 uint6_t rsvd; 2069 if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked()) 2070 { 2071 return ipmi::responseReqDataLenInvalid(); 2072 } 2073 if (rsvd) 2074 { 2075 return ipmi::responseInvalidFieldRequest(); 2076 } 2077 if ((transferStatus == setInProgress) && 2078 (static_cast<uint8_t>(setInProgressFlag) != setComplete)) 2079 { 2080 return ipmi::responsefailSetInProgress(); 2081 } 2082 transferStatus = static_cast<uint8_t>(setInProgressFlag); 2083 return ipmi::responseSuccess(); 2084 } 2085 2086 /* 000101 2087 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 2088 * This is the only parameter used by petitboot. 2089 */ 2090 2091 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2092 BootOptionParameter::bootFlags) 2093 { 2094 uint5_t rsvd; 2095 bool validFlag; 2096 bool permanent; 2097 bool biosBootType; 2098 bool lockOutResetButton; 2099 bool screenBlank; 2100 uint4_t bootDeviceSelector; 2101 bool lockKeyboard; 2102 uint5_t biosCtrls; 2103 bool lockOutPower; 2104 uint4_t biosInfo; 2105 uint4_t rsvd1; 2106 uint5_t deviceInstance; 2107 uint3_t rsvd2; 2108 2109 if (data.unpack(rsvd, biosBootType, permanent, validFlag, 2110 lockOutResetButton, screenBlank, bootDeviceSelector, 2111 lockKeyboard, cmosClear, biosCtrls, biosVerbosity, 2112 lockOutPower, biosInfo, rsvd1, deviceInstance, rsvd2) != 2113 0 || 2114 !data.fullyUnpacked()) 2115 { 2116 return ipmi::responseReqDataLenInvalid(); 2117 } 2118 if (rsvd || rsvd1 || rsvd2) 2119 { 2120 return ipmi::responseInvalidFieldRequest(); 2121 } 2122 2123 using namespace chassis::internal; 2124 using namespace chassis::internal::cache; 2125 2126 try 2127 { 2128 rc = setBootOneTime(ctx, !permanent); 2129 if (rc != ipmi::ccSuccess) 2130 { 2131 return ipmi::response(rc); 2132 } 2133 2134 rc = setBootEnable(ctx, validFlag); 2135 if (rc != ipmi::ccSuccess) 2136 { 2137 return ipmi::response(rc); 2138 } 2139 2140 auto modeItr = 2141 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2142 auto typeItr = 2143 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType)); 2144 auto sourceItr = 2145 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2146 if (sourceIpmiToDbus.end() != sourceItr) 2147 { 2148 rc = setBootSource(ctx, sourceItr->second); 2149 if (rc != ipmi::ccSuccess) 2150 { 2151 return ipmi::response(rc); 2152 } 2153 // If a set boot device is mapping to a boot source, then reset 2154 // the boot mode D-Bus property to default. 2155 // This way the ipmid code can determine which property is not 2156 // at the default value 2157 if (sourceItr->second != Source::Sources::Default) 2158 { 2159 rc = setBootMode(ctx, Mode::Modes::Regular); 2160 if (rc != ipmi::ccSuccess) 2161 { 2162 return ipmi::response(rc); 2163 } 2164 } 2165 } 2166 2167 if (typeIpmiToDbus.end() != typeItr) 2168 { 2169 rc = setBootType(ctx, typeItr->second); 2170 if (rc != ipmi::ccSuccess) 2171 { 2172 return ipmi::response(rc); 2173 } 2174 } 2175 2176 if (modeIpmiToDbus.end() != modeItr) 2177 { 2178 rc = setBootMode(ctx, modeItr->second); 2179 if (rc != ipmi::ccSuccess) 2180 { 2181 return ipmi::response(rc); 2182 } 2183 // If a set boot device is mapping to a boot mode, then reset 2184 // the boot source D-Bus property to default. 2185 // This way the ipmid code can determine which property is not 2186 // at the default value 2187 if (modeItr->second != Mode::Modes::Regular) 2188 { 2189 rc = setBootSource(ctx, Source::Sources::Default); 2190 if (rc != ipmi::ccSuccess) 2191 { 2192 return ipmi::response(rc); 2193 } 2194 } 2195 } 2196 if ((modeIpmiToDbus.end() == modeItr) && 2197 (typeIpmiToDbus.end() == typeItr) && 2198 (sourceIpmiToDbus.end() == sourceItr)) 2199 { 2200 // return error if boot option is not supported 2201 lg2::error( 2202 "ipmiChassisSetSysBootOptions: Boot option not supported"); 2203 return ipmi::responseInvalidFieldRequest(); 2204 } 2205 } 2206 catch (const sdbusplus::exception_t& e) 2207 { 2208 objectsPtr.reset(); 2209 report<InternalFailure>(); 2210 lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot " 2211 "flag parameters"); 2212 return ipmi::responseUnspecifiedError(); 2213 } 2214 } 2215 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2216 BootOptionParameter::bootInfo) 2217 { 2218 uint8_t writeMak; 2219 uint5_t bootInfoAck; 2220 uint3_t rsvd; 2221 2222 if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 || 2223 !data.fullyUnpacked()) 2224 { 2225 return ipmi::responseReqDataLenInvalid(); 2226 } 2227 if (rsvd) 2228 { 2229 return ipmi::responseInvalidFieldRequest(); 2230 } 2231 bootInitiatorAckData &= ~writeMak; 2232 bootInitiatorAckData |= (writeMak & bootInfoAck); 2233 lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set " 2234 "successfully"); 2235 data.trailingOk = true; 2236 return ipmi::responseSuccess(); 2237 } 2238 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2239 BootOptionParameter::bootFlagValidClr) 2240 { 2241 uint5_t bootFlagValidClr; 2242 uint3_t rsvd; 2243 2244 if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked()) 2245 { 2246 return ipmi::responseReqDataLenInvalid(); 2247 } 2248 if (rsvd) 2249 { 2250 return ipmi::responseInvalidFieldRequest(); 2251 } 2252 // store boot flag valid bits clear value 2253 bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr); 2254 lg2::info( 2255 "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set " 2256 "successfully to {VALUE}", 2257 "VALUE", lg2::hex, bootFlagValidBitClr); 2258 return ipmi::responseSuccess(); 2259 } 2260 else 2261 { 2262 if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) && 2263 (parameterSelector <= static_cast<uint7_t>(oemParmEnd))) 2264 { 2265 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2266 BootOptionParameter::opalNetworkSettings) 2267 { 2268 ipmi::Cc ret = setHostNetworkData(data); 2269 if (ret != ipmi::ccSuccess) 2270 { 2271 lg2::error("ipmiChassisSetSysBootOptions: Error in " 2272 "setHostNetworkData"); 2273 data.trailingOk = true; 2274 return ipmi::response(ret); 2275 } 2276 data.trailingOk = true; 2277 return ipmi::responseSuccess(); 2278 } 2279 else 2280 { 2281 lg2::error( 2282 "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}", 2283 "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector)); 2284 data.trailingOk = true; 2285 return ipmi::responseParmNotSupported(); 2286 } 2287 } 2288 data.trailingOk = true; 2289 return ipmi::responseParmNotSupported(); 2290 } 2291 return ipmi::responseSuccess(); 2292 } 2293 2294 /** @brief implements Get POH counter command 2295 * @parameter 2296 * - none 2297 * @returns IPMI completion code plus response data 2298 * - minPerCount - Minutes per count 2299 * - counterReading - counter reading 2300 */ 2301 ipmi::RspType<uint8_t, // Minutes per count 2302 uint32_t // Counter reading 2303 > 2304 ipmiGetPOHCounter() 2305 { 2306 // sd_bus error 2307 try 2308 { 2309 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), 2310 getPOHCounter()); 2311 } 2312 catch (const std::exception& e) 2313 { 2314 lg2::error("getPOHCounter error: {ERROR}", "ERROR", e); 2315 return ipmi::responseUnspecifiedError(); 2316 } 2317 } 2318 2319 ipmi::RspType<uint3_t, // policy support 2320 uint5_t // reserved 2321 > 2322 ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx, uint3_t policy, 2323 uint5_t reserved) 2324 { 2325 power_policy::DbusValue value = 2326 power_policy::RestorePolicy::Policy::AlwaysOff; 2327 2328 if (reserved || (policy > power_policy::noChange)) 2329 { 2330 lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex, 2331 static_cast<int>(policy)); 2332 return ipmi::responseInvalidFieldRequest(); 2333 } 2334 2335 if (policy == power_policy::noChange) 2336 { 2337 // just return the supported policy 2338 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2339 } 2340 2341 for (const auto& it : power_policy::dbusToIpmi) 2342 { 2343 if (it.second == policy) 2344 { 2345 value = it.first; 2346 break; 2347 } 2348 } 2349 2350 try 2351 { 2352 settings::Objects& objects = chassis::internal::cache::getObjects(); 2353 const settings::Path& powerRestoreSetting = 2354 objects.map.at(chassis::internal::powerRestoreIntf).front(); 2355 2356 boost::system::error_code ec = ipmi::setDbusProperty( 2357 ctx, 2358 objects.service(powerRestoreSetting, 2359 chassis::internal::powerRestoreIntf), 2360 powerRestoreSetting, chassis::internal::powerRestoreIntf, 2361 "PowerRestorePolicy", convertForMessage(value)); 2362 if (ec) 2363 { 2364 lg2::error("Unspecified Error"); 2365 return ipmi::responseUnspecifiedError(); 2366 } 2367 } 2368 catch (const InternalFailure& e) 2369 { 2370 chassis::internal::cache::objectsPtr.reset(); 2371 report<InternalFailure>(); 2372 return ipmi::responseUnspecifiedError(); 2373 } 2374 2375 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2376 } 2377 2378 ipmi::RspType<> ipmiSetFrontPanelButtonEnables( 2379 ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton, 2380 bool, bool, uint4_t) 2381 { 2382 using namespace chassis::internal; 2383 2384 // set power button Enabled property 2385 bool success = setButtonDisabled(ctx, powerButtonPath, powerButtonIntf, 2386 disablePowerButton); 2387 2388 // set reset button Enabled property 2389 success &= setButtonDisabled(ctx, resetButtonPath, resetButtonIntf, 2390 disableResetButton); 2391 2392 if (!success) 2393 { 2394 // not all buttons were successfully set 2395 return ipmi::responseUnspecifiedError(); 2396 } 2397 return ipmi::responseSuccess(); 2398 } 2399 2400 void registerNetFnChassisFunctions() 2401 { 2402 createIdentifyTimer(); 2403 2404 // Get Chassis Capabilities 2405 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2406 ipmi::chassis::cmdGetChassisCapabilities, 2407 ipmi::Privilege::User, ipmiGetChassisCap); 2408 2409 // Set Front Panel Button Enables 2410 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2411 ipmi::chassis::cmdSetFrontPanelButtonEnables, 2412 ipmi::Privilege::Admin, 2413 ipmiSetFrontPanelButtonEnables); 2414 2415 // Set Chassis Capabilities 2416 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2417 ipmi::chassis::cmdSetChassisCapabilities, 2418 ipmi::Privilege::User, ipmiSetChassisCap); 2419 2420 // <Get System Boot Options> 2421 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2422 ipmi::chassis::cmdGetSystemBootOptions, 2423 ipmi::Privilege::Operator, 2424 ipmiChassisGetSysBootOptions); 2425 2426 // <Get Chassis Status> 2427 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2428 ipmi::chassis::cmdGetChassisStatus, 2429 ipmi::Privilege::User, ipmiGetChassisStatus); 2430 2431 // <Chassis Get System Restart Cause> 2432 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2433 ipmi::chassis::cmdGetSystemRestartCause, 2434 ipmi::Privilege::User, ipmiGetSystemRestartCause); 2435 2436 // <Chassis Control> 2437 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2438 ipmi::chassis::cmdChassisControl, 2439 ipmi::Privilege::Operator, ipmiChassisControl); 2440 2441 // <Chassis Identify> 2442 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2443 ipmi::chassis::cmdChassisIdentify, 2444 ipmi::Privilege::Operator, ipmiChassisIdentify); 2445 2446 // <Set System Boot Options> 2447 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2448 ipmi::chassis::cmdSetSystemBootOptions, 2449 ipmi::Privilege::Operator, 2450 ipmiChassisSetSysBootOptions); 2451 2452 // <Get POH Counter> 2453 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2454 ipmi::chassis::cmdGetPohCounter, 2455 ipmi::Privilege::User, ipmiGetPOHCounter); 2456 2457 // <Set Power Restore Policy> 2458 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2459 ipmi::chassis::cmdSetPowerRestorePolicy, 2460 ipmi::Privilege::Operator, 2461 ipmiChassisSetPowerRestorePolicy); 2462 } 2463