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 std::optional<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.has_value()) 760 { 761 if ((bridgeDeviceAddr.value() & ~chassisCapAddrMask) != 0) 762 { 763 lg2::error( 764 "Unsupported request parameter(Bridge Addr) for REQ={REQ}", 765 "REQ", lg2::hex, bridgeDeviceAddr.value()); 766 return ipmi::responseInvalidFieldRequest(); 767 } 768 } 769 770 try 771 { 772 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 773 ipmi::DbusObjectInfo chassisCapObject = 774 ipmi::getDbusObject(bus, chassisCapIntf); 775 776 ipmi::setDbusProperty(bus, chassisCapObject.second, 777 chassisCapObject.first, chassisCapIntf, 778 chassisIntrusionProp, intrusion); 779 780 ipmi::setDbusProperty(bus, chassisCapObject.second, 781 chassisCapObject.first, chassisCapIntf, 782 chassisFrontPanelLockoutProp, fpLockout); 783 784 ipmi::setDbusProperty(bus, chassisCapObject.second, 785 chassisCapObject.first, chassisCapIntf, 786 chassisFRUDevAddrProp, fruDeviceAddr); 787 788 ipmi::setDbusProperty(bus, chassisCapObject.second, 789 chassisCapObject.first, chassisCapIntf, 790 chassisSDRDevAddrProp, sdrDeviceAddr); 791 792 ipmi::setDbusProperty(bus, chassisCapObject.second, 793 chassisCapObject.first, chassisCapIntf, 794 chassisSELDevAddrProp, selDeviceAddr); 795 796 ipmi::setDbusProperty(bus, chassisCapObject.second, 797 chassisCapObject.first, chassisCapIntf, 798 chassisSMDevAddrProp, smDeviceAddr); 799 800 if (bridgeDeviceAddr.has_value()) 801 { 802 ipmi::setDbusProperty(bus, chassisCapObject.second, 803 chassisCapObject.first, chassisCapIntf, 804 chassisBridgeDevAddrProp, 805 bridgeDeviceAddr.value()); 806 } 807 } 808 catch (const std::exception& e) 809 { 810 lg2::error("Failed to set chassis capability properties: {ERR}", "ERR", 811 e); 812 return ipmi::responseUnspecifiedError(); 813 } 814 return ipmi::responseSuccess(); 815 } 816 817 //------------------------------------------ 818 // Calls into Host State Manager Dbus object 819 //------------------------------------------ 820 int initiateHostStateTransition(ipmi::Context::ptr& ctx, 821 State::Host::Transition transition) 822 { 823 // OpenBMC Host State Manager dbus framework 824 constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; 825 constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host"; 826 827 // Convert to string equivalent of the passed in transition enum. 828 auto request = 829 sdbusplus::common::xyz::openbmc_project::state::convertForMessage( 830 transition); 831 832 std::string service; 833 boost::system::error_code ec = 834 ipmi::getService(ctx, hostStateIntf, hostStatePath, service); 835 836 if (!ec) 837 { 838 ec = ipmi::setDbusProperty(ctx, service, hostStatePath, hostStateIntf, 839 "RequestedHostTransition", request); 840 } 841 if (ec) 842 { 843 lg2::error( 844 "Failed to initiate transition for request {REQUEST}: {EXCEPTION}", 845 "REQUEST", request, "EXCEPTION", ec.message()); 846 return -1; 847 } 848 lg2::info( 849 "Transition request {REQUEST} initiated successfully by user {USERID}", 850 "REQUEST", request, "USERID", ctx->userId); 851 return 0; 852 } 853 854 //------------------------------------------ 855 // Calls into Chassis State Manager Dbus object 856 //------------------------------------------ 857 int initiateChassisStateTransition(ipmi::Context::ptr& ctx, 858 State::Chassis::Transition transition) 859 { 860 // OpenBMC Chassis State Manager dbus framework 861 constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0"; 862 constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis"; 863 864 std::string service; 865 boost::system::error_code ec = 866 ipmi::getService(ctx, chassisStateIntf, chassisStatePath, service); 867 868 // Convert to string equivalent of the passed in transition enum. 869 auto request = 870 sdbusplus::common::xyz::openbmc_project::state::convertForMessage( 871 transition); 872 873 if (!ec) 874 { 875 ec = ipmi::setDbusProperty(ctx, service, chassisStatePath, 876 chassisStateIntf, "RequestedPowerTransition", 877 request); 878 } 879 if (ec) 880 { 881 lg2::error("Failed to initiate transition {REQUEST}: {EXCEPTION}", 882 "REQUEST", request, "EXCEPTION", ec.message()); 883 884 return -1; 885 } 886 887 return 0; 888 } 889 890 //------------------------------------------ 891 // Trigger an NMI on the host via dbus 892 //------------------------------------------ 893 static int doNmi(ipmi::Context::ptr& ctx) 894 { 895 constexpr const char* nmiIntfName = "xyz.openbmc_project.Control.Host.NMI"; 896 ipmi::DbusObjectInfo nmiObj{}; 897 boost::system::error_code ec; 898 899 ec = ipmi::getDbusObject(ctx, nmiIntfName, nmiObj); 900 if (ec) 901 { 902 lg2::error("Failed to find NMI service: {ERROR}", "ERROR", 903 ec.message()); 904 return -1; 905 } 906 907 ec = ipmi::callDbusMethod(ctx, nmiObj.second, nmiObj.first, nmiIntfName, 908 "NMI"); 909 if (ec) 910 { 911 lg2::error("NMI call failed: {ERROR}", "ERROR", ec.message()); 912 elog<InternalFailure>(); 913 return -1; 914 } 915 916 return 0; 917 } 918 919 namespace power_policy 920 { 921 922 using namespace sdbusplus::server::xyz::openbmc_project::control::power; 923 using IpmiValue = uint8_t; 924 using DbusValue = RestorePolicy::Policy; 925 926 const std::map<DbusValue, IpmiValue> dbusToIpmi = { 927 {RestorePolicy::Policy::AlwaysOff, 0x00}, 928 {RestorePolicy::Policy::Restore, 0x01}, 929 {RestorePolicy::Policy::AlwaysOn, 0x02}, 930 {RestorePolicy::Policy::None, 0x03}}; 931 932 static constexpr uint8_t noChange = 0x03; 933 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; 934 935 /* helper function for Get Chassis Status Command 936 */ 937 std::optional<uint2_t> getPowerRestorePolicy() 938 { 939 uint2_t restorePolicy = 0; 940 using namespace chassis::internal; 941 942 settings::Objects& objects = cache::getObjects(); 943 944 try 945 { 946 const auto& powerRestoreSetting = 947 objects.map.at(powerRestoreIntf).front(); 948 ipmi::Value result = ipmi::getDbusProperty( 949 *getSdBus(), 950 objects.service(powerRestoreSetting, powerRestoreIntf).c_str(), 951 powerRestoreSetting.c_str(), powerRestoreIntf, 952 "PowerRestorePolicy"); 953 auto powerRestore = RestorePolicy::convertPolicyFromString( 954 std::get<std::string>(result)); 955 restorePolicy = dbusToIpmi.at(powerRestore); 956 } 957 catch (const std::exception& e) 958 { 959 lg2::error( 960 "Failed to fetch pgood property ({PATH}/{INTERFACE}): {ERROR}", 961 "PATH", objects.map.at(powerRestoreIntf).front(), "INTERFACE", 962 powerRestoreIntf, "ERROR", e); 963 cache::objectsPtr.reset(); 964 return std::nullopt; 965 } 966 return std::make_optional(restorePolicy); 967 } 968 969 /* 970 * getPowerStatus 971 * helper function for Get Chassis Status Command 972 * return - optional value for pgood (no value on error) 973 */ 974 std::optional<bool> getPowerStatus() 975 { 976 bool powerGood = false; 977 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 978 try 979 { 980 constexpr const char* chassisStatePath = 981 "/xyz/openbmc_project/state/chassis0"; 982 constexpr const char* chassisStateIntf = 983 "xyz.openbmc_project.State.Chassis"; 984 auto service = 985 ipmi::getService(*busp, chassisStateIntf, chassisStatePath); 986 987 ipmi::Value powerState = 988 ipmi::getDbusProperty(*busp, service, chassisStatePath, 989 chassisStateIntf, "CurrentPowerState"); 990 std::string powerStateStr = std::get<std::string>(powerState); 991 if (powerStateStr.ends_with(".On") || 992 powerStateStr.ends_with(".TransitioningToOff")) 993 { 994 powerGood = true; 995 } 996 } 997 catch (const std::exception& e) 998 { 999 try 1000 { 1001 // FIXME: some legacy modules use the older path; try that next 1002 constexpr const char* legacyPwrCtrlObj = 1003 "/org/openbmc/control/power0"; 1004 constexpr const char* legacyPwrCtrlIntf = 1005 "org.openbmc.control.Power"; 1006 auto service = 1007 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj); 1008 1009 ipmi::Value variant = ipmi::getDbusProperty( 1010 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood"); 1011 powerGood = static_cast<bool>(std::get<int>(variant)); 1012 } 1013 catch (const std::exception& e) 1014 { 1015 lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e); 1016 return std::nullopt; 1017 } 1018 } 1019 return std::make_optional(powerGood); 1020 } 1021 1022 /* 1023 * getACFailStatus 1024 * helper function for Get Chassis Status Command 1025 * return - bool value for ACFail (false on error) 1026 */ 1027 bool getACFailStatus() 1028 { 1029 constexpr const char* powerControlObj = 1030 "/xyz/openbmc_project/Chassis/Control/Power0"; 1031 constexpr const char* powerControlIntf = 1032 "xyz.openbmc_project.Chassis.Control.Power"; 1033 bool acFail = false; 1034 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1035 try 1036 { 1037 auto service = 1038 ipmi::getService(*bus, powerControlIntf, powerControlObj); 1039 1040 ipmi::Value variant = ipmi::getDbusProperty( 1041 *bus, service, powerControlObj, powerControlIntf, "PFail"); 1042 acFail = std::get<bool>(variant); 1043 } 1044 catch (const std::exception& e) 1045 { 1046 lg2::error( 1047 "Failed to fetch PFail property ({PATH}/{INTERFACE}): {ERROR}", 1048 "PATH", powerControlObj, "INTERFACE", powerControlIntf, "ERROR", e); 1049 } 1050 return acFail; 1051 } 1052 } // namespace power_policy 1053 1054 static std::optional<bool> getButtonDisabled(ipmi::Context::ptr& ctx, 1055 const std::string& buttonPath, 1056 const std::string& buttonIntf) 1057 { 1058 bool buttonDisabled = false; 1059 boost::system::error_code ec; 1060 std::string service; 1061 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service); 1062 if (!ec) 1063 { 1064 bool enabled; 1065 ec = ipmi::getDbusProperty(ctx, service, buttonPath, buttonIntf, 1066 "Enabled", enabled); 1067 buttonDisabled = !enabled; 1068 } 1069 1070 if (ec) 1071 { 1072 lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}", 1073 "PATH", buttonPath, "ERROR", ec.message()); 1074 return std::nullopt; 1075 } 1076 return std::make_optional(buttonDisabled); 1077 } 1078 1079 static bool setButtonDisabled(ipmi::Context::ptr& ctx, 1080 const std::string& buttonPath, 1081 const std::string& buttonIntf, bool disable) 1082 { 1083 std::string service; 1084 boost::system::error_code ec; 1085 ec = ipmi::getService(ctx, buttonIntf, buttonPath, service); 1086 if (!ec) 1087 { 1088 ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf, 1089 "Enabled", !disable); 1090 } 1091 if (ec) 1092 { 1093 lg2::error( 1094 "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}", 1095 "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message()); 1096 return false; 1097 } 1098 return true; 1099 } 1100 1101 static std::optional<bool> getChassisIntrusionStatus(ipmi::Context::ptr& ctx) 1102 { 1103 std::vector<std::string> interfaces = {std::string(Intrusion::interface)}; 1104 ipmi::ObjectTree objs; 1105 std::string propVal; 1106 std::optional<bool> ret = std::nullopt; 1107 1108 boost::system::error_code ec = 1109 ipmi::getSubTree(ctx, interfaces, std::string("/"), 0, objs); 1110 1111 if (ec) 1112 { 1113 lg2::error("Fail to find Chassis Intrusion Interface on D-Bus " 1114 "({INTERFACE}): {ERROR}", 1115 "INTERFACE", Intrusion::interface, "ERROR", ec.message()); 1116 return ret; 1117 } 1118 1119 for (const auto& [path, map] : objs) 1120 { 1121 for (const auto& [service, intfs] : map) 1122 { 1123 ec = ipmi::getDbusProperty<std::string>( 1124 ctx, service, path, Intrusion::interface, "Status", propVal); 1125 1126 if (ec) 1127 { 1128 lg2::error("Failed to get Chassis Intrusion Status property " 1129 "({SERVICE}/{PATH}/{INTERFACE}): {ERROR}", 1130 "SERVICE", service, "PATH", path, "INTERFACE", 1131 Intrusion::interface, "ERROR", ec.message()); 1132 continue; 1133 } 1134 1135 auto statusOpt = 1136 sdbusplus::message::convert_from_string<Intrusion::Status>( 1137 propVal); 1138 if (statusOpt) 1139 { 1140 if (*statusOpt == Intrusion::Status::Normal) 1141 { 1142 ret = std::make_optional(false); 1143 } 1144 else 1145 { 1146 ret = std::make_optional(true); 1147 return ret; // Early return on first non-Normal status 1148 } 1149 } 1150 else 1151 { 1152 lg2::warning( 1153 "Invalid Intrusion::Status value received: {VALUE}", 1154 "VALUE", propVal); 1155 return std::nullopt; 1156 } 1157 } 1158 } 1159 return ret; 1160 } 1161 1162 //---------------------------------------------------------------------- 1163 // Get Chassis Status commands 1164 //---------------------------------------------------------------------- 1165 ipmi::RspType<bool, // Power is on 1166 bool, // Power overload 1167 bool, // Interlock 1168 bool, // power fault 1169 bool, // power control fault 1170 uint2_t, // power restore policy 1171 bool, // reserved 1172 1173 bool, // AC failed 1174 bool, // last power down caused by a Power overload 1175 bool, // last power down caused by a power interlock 1176 bool, // last power down caused by power fault 1177 bool, // last ‘Power is on’ state was entered via IPMI command 1178 uint3_t, // reserved 1179 1180 bool, // Chassis intrusion active 1181 bool, // Front Panel Lockout active 1182 bool, // Drive Fault 1183 bool, // Cooling/fan fault detected 1184 uint2_t, // Chassis Identify State 1185 bool, // Chassis Identify command and state info supported 1186 bool, // reserved 1187 1188 bool, // Power off button disabled 1189 bool, // Reset button disabled 1190 bool, // Diagnostic Interrupt button disabled 1191 bool, // Standby (sleep) button disabled 1192 bool, // Power off button disable allowed 1193 bool, // Reset button disable allowed 1194 bool, // Diagnostic Interrupt button disable allowed 1195 bool // Standby (sleep) button disable allowed 1196 > 1197 ipmiGetChassisStatus(ipmi::Context::ptr& ctx) 1198 { 1199 using namespace chassis::internal; 1200 std::optional<uint2_t> restorePolicy = 1201 power_policy::getPowerRestorePolicy(); 1202 std::optional<bool> powerGood = power_policy::getPowerStatus(); 1203 if (!restorePolicy || !powerGood) 1204 { 1205 return ipmi::responseUnspecifiedError(); 1206 } 1207 1208 // Front Panel Button Capabilities and disable/enable status(Optional) 1209 std::optional<bool> powerButtonReading = 1210 getButtonDisabled(ctx, powerButtonPath, powerButtonIntf); 1211 // allow disable if the interface is present 1212 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading); 1213 // default return the button is enabled (not disabled) 1214 bool powerButtonDisabled = false; 1215 if (powerButtonDisableAllow) 1216 { 1217 // return the real value of the button status, if present 1218 powerButtonDisabled = *powerButtonReading; 1219 } 1220 1221 std::optional<bool> resetButtonReading = 1222 getButtonDisabled(ctx, resetButtonPath, resetButtonIntf); 1223 // allow disable if the interface is present 1224 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading); 1225 // default return the button is enabled (not disabled) 1226 bool resetButtonDisabled = false; 1227 if (resetButtonDisableAllow) 1228 { 1229 // return the real value of the button status, if present 1230 resetButtonDisabled = *resetButtonReading; 1231 } 1232 1233 bool powerDownAcFailed = power_policy::getACFailStatus(); 1234 1235 bool chassisIntrusionActive = false; 1236 std::optional<bool> chassisIntrusionStatus = getChassisIntrusionStatus(ctx); 1237 if (chassisIntrusionStatus) 1238 { 1239 chassisIntrusionActive = chassisIntrusionStatus.value(); 1240 } 1241 1242 // This response has a lot of hard-coded, unsupported fields 1243 // They are set to false or 0 1244 constexpr bool powerOverload = false; 1245 constexpr bool chassisInterlock = false; 1246 constexpr bool powerFault = false; 1247 constexpr bool powerControlFault = false; 1248 constexpr bool powerDownOverload = false; 1249 constexpr bool powerDownInterlock = false; 1250 constexpr bool powerDownPowerFault = false; 1251 constexpr bool powerStatusIPMI = false; 1252 constexpr bool frontPanelLockoutActive = false; 1253 constexpr bool driveFault = false; 1254 constexpr bool coolingFanFault = false; 1255 // chassisIdentifySupport set because this command is implemented 1256 constexpr bool chassisIdentifySupport = true; 1257 uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState); 1258 constexpr bool diagButtonDisabled = false; 1259 constexpr bool sleepButtonDisabled = false; 1260 constexpr bool diagButtonDisableAllow = false; 1261 constexpr bool sleepButtonDisableAllow = false; 1262 1263 return ipmi::responseSuccess( 1264 *powerGood, powerOverload, chassisInterlock, powerFault, 1265 powerControlFault, *restorePolicy, 1266 false, // reserved 1267 1268 powerDownAcFailed, powerDownOverload, powerDownInterlock, 1269 powerDownPowerFault, powerStatusIPMI, 1270 uint3_t(0), // reserved 1271 1272 chassisIntrusionActive, frontPanelLockoutActive, driveFault, 1273 coolingFanFault, chassisIdentifyState, chassisIdentifySupport, 1274 false, // reserved 1275 1276 powerButtonDisabled, resetButtonDisabled, diagButtonDisabled, 1277 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow, 1278 diagButtonDisableAllow, sleepButtonDisableAllow); 1279 } 1280 1281 enum class IpmiRestartCause 1282 { 1283 Unknown = 0x0, 1284 RemoteCommand = 0x1, 1285 ResetButton = 0x2, 1286 PowerButton = 0x3, 1287 WatchdogTimer = 0x4, 1288 PowerPolicyAlwaysOn = 0x6, 1289 PowerPolicyPreviousState = 0x7, 1290 SoftReset = 0xa, 1291 }; 1292 1293 static IpmiRestartCause restartCauseToIpmiRestartCause( 1294 State::Host::RestartCause cause) 1295 { 1296 switch (cause) 1297 { 1298 case State::Host::RestartCause::Unknown: 1299 { 1300 return IpmiRestartCause::Unknown; 1301 } 1302 case State::Host::RestartCause::RemoteCommand: 1303 { 1304 return IpmiRestartCause::RemoteCommand; 1305 } 1306 case State::Host::RestartCause::ResetButton: 1307 { 1308 return IpmiRestartCause::ResetButton; 1309 } 1310 case State::Host::RestartCause::PowerButton: 1311 { 1312 return IpmiRestartCause::PowerButton; 1313 } 1314 case State::Host::RestartCause::WatchdogTimer: 1315 { 1316 return IpmiRestartCause::WatchdogTimer; 1317 } 1318 case State::Host::RestartCause::PowerPolicyAlwaysOn: 1319 { 1320 return IpmiRestartCause::PowerPolicyAlwaysOn; 1321 } 1322 case State::Host::RestartCause::PowerPolicyPreviousState: 1323 { 1324 return IpmiRestartCause::PowerPolicyPreviousState; 1325 } 1326 case State::Host::RestartCause::SoftReset: 1327 { 1328 return IpmiRestartCause::SoftReset; 1329 } 1330 default: 1331 { 1332 return IpmiRestartCause::Unknown; 1333 } 1334 } 1335 } 1336 1337 /* 1338 * getRestartCause 1339 * helper function for Get Host restart cause Command 1340 * return - optional value for RestartCause (no value on error) 1341 */ 1342 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx) 1343 { 1344 constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0"; 1345 constexpr const char* restartCauseIntf = "xyz.openbmc_project.State.Host"; 1346 1347 std::string service; 1348 boost::system::error_code ec = 1349 ipmi::getService(ctx, restartCauseIntf, restartCausePath, service); 1350 if (!ec) 1351 { 1352 std::string restartCauseStr; 1353 ec = ipmi::getDbusProperty<std::string>( 1354 ctx, service, restartCausePath, restartCauseIntf, "RestartCause", 1355 restartCauseStr); 1356 if (!ec) 1357 { 1358 auto cause = 1359 State::Host::convertRestartCauseFromString(restartCauseStr); 1360 return types::enum_cast<uint4_t>( 1361 restartCauseToIpmiRestartCause(cause)); 1362 } 1363 } 1364 1365 lg2::error( 1366 "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}", 1367 "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE", 1368 restartCauseIntf); 1369 return std::nullopt; 1370 } 1371 1372 ipmi::RspType<uint4_t, // Restart Cause 1373 uint4_t, // reserved 1374 uint8_t // channel number (not supported) 1375 > 1376 ipmiGetSystemRestartCause(ipmi::Context::ptr ctx) 1377 { 1378 std::optional<uint4_t> cause = getRestartCause(ctx); 1379 if (!cause) 1380 { 1381 return ipmi::responseUnspecifiedError(); 1382 } 1383 1384 constexpr uint4_t reserved = 0; 1385 auto channel = static_cast<uint8_t>(ctx->channel); 1386 return ipmi::responseSuccess(cause.value(), reserved, channel); 1387 } 1388 /** @brief Implementation of chassis control command 1389 * 1390 * @param - chassisControl command byte 1391 * 1392 * @return Success or InvalidFieldRequest. 1393 */ 1394 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx, 1395 uint8_t chassisControl) 1396 { 1397 int rc = 0; 1398 switch (chassisControl) 1399 { 1400 case cmdPowerOn: 1401 rc = initiateHostStateTransition(ctx, State::Host::Transition::On); 1402 break; 1403 case cmdPowerOff: 1404 rc = initiateChassisStateTransition( 1405 ctx, State::Chassis::Transition::Off); 1406 break; 1407 case cmdHardReset: 1408 rc = initiateHostStateTransition( 1409 ctx, State::Host::Transition::ForceWarmReboot); 1410 break; 1411 case cmdPowerCycle: 1412 { 1413 auto powerState = power_policy::getPowerStatus(); 1414 1415 if (powerState == std::nullopt) 1416 { 1417 return ipmi::responseUnspecifiedError(); 1418 } 1419 1420 /* 1421 * As define in the Chapter 28.3 - Chassis Control Command of IPMI 1422 * specification: It is recommended that no action occur if system 1423 * power is off (S4/S5) when this action is selected, and that a D5 1424 * "Requiest parameter(s) not supported in this presenst state." 1425 * error completion code be returned. 1426 */ 1427 if (powerState.value() == false) 1428 { 1429 return ipmi::responseCommandNotAvailable(); 1430 } 1431 1432 rc = initiateHostStateTransition(ctx, 1433 State::Host::Transition::Reboot); 1434 break; 1435 } 1436 case cmdSoftOffViaOverTemp: 1437 rc = initiateHostStateTransition(ctx, State::Host::Transition::Off); 1438 break; 1439 case cmdPulseDiagnosticInterrupt: 1440 rc = doNmi(ctx); 1441 break; 1442 1443 default: 1444 { 1445 lg2::error("Invalid Chassis Control command: {CMD}", "CMD", 1446 lg2::hex, chassisControl); 1447 return ipmi::responseInvalidFieldRequest(); 1448 } 1449 } 1450 1451 return ((rc < 0) ? ipmi::responseUnspecifiedError() 1452 : ipmi::responseSuccess()); 1453 } 1454 1455 /** @brief Return D-Bus connection string to enclosure identify LED object 1456 * 1457 * @param[in, out] connection - connection to D-Bus object 1458 * @return a IPMI return code 1459 */ 1460 std::string getEnclosureIdentifyConnection() 1461 { 1462 // lookup enclosure_identify group owner(s) in mapper 1463 try 1464 { 1465 return ipmi::getService(*getSdBus(), "xyz.openbmc_project.Led.Group", 1466 identify_led_object_name); 1467 } 1468 catch (const std::exception& e) 1469 { 1470 lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}", 1471 "ERROR", e); 1472 elog<InternalFailure>(); 1473 } 1474 } 1475 1476 /** @brief Turn On/Off enclosure identify LED 1477 * 1478 * @param[in] flag - true to turn on LED, false to turn off 1479 * @return a IPMI return code 1480 */ 1481 void enclosureIdentifyLed(bool flag) 1482 { 1483 using namespace chassis::internal; 1484 1485 std::string connection = getEnclosureIdentifyConnection(); 1486 1487 lg2::debug("enclosureIdentifyLed({FLAG})", "FLAG", flag); 1488 1489 try 1490 { 1491 ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name, 1492 "xyz.openbmc_project.Led.Group", "Asserted", 1493 flag); 1494 } 1495 catch (const std::exception& e) 1496 { 1497 lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}", 1498 "LED_STATE", flag, "ERROR", e); 1499 elog<InternalFailure>(); 1500 } 1501 } 1502 1503 /** @brief Callback method to turn off LED 1504 */ 1505 void enclosureIdentifyLedOff() 1506 { 1507 try 1508 { 1509 chassisIDState = ChassisIDState::off; 1510 enclosureIdentifyLed(false); 1511 } 1512 catch (const InternalFailure& e) 1513 { 1514 report<InternalFailure>(); 1515 } 1516 } 1517 1518 /** @brief Create timer to turn on and off the enclosure LED 1519 */ 1520 void createIdentifyTimer() 1521 { 1522 if (!identifyTimer) 1523 { 1524 identifyTimer = 1525 std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedOff); 1526 } 1527 } 1528 1529 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, 1530 std::optional<uint8_t> force) 1531 { 1532 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); 1533 bool forceIdentify = force.value_or(0) & 0x01; 1534 1535 if (identifyInterval || forceIdentify) 1536 { 1537 // stop the timer if already started; 1538 // for force identify we should not turn off LED 1539 identifyTimer->stop(); 1540 try 1541 { 1542 chassisIDState = ChassisIDState::temporaryOn; 1543 enclosureIdentifyLed(true); 1544 } 1545 catch (const InternalFailure& e) 1546 { 1547 report<InternalFailure>(); 1548 return ipmi::responseResponseError(); 1549 } 1550 1551 if (forceIdentify) 1552 { 1553 chassisIDState = ChassisIDState::indefiniteOn; 1554 return ipmi::responseSuccess(); 1555 } 1556 // start the timer 1557 auto time = std::chrono::duration_cast<std::chrono::microseconds>( 1558 std::chrono::seconds(identifyInterval)); 1559 identifyTimer->start(time); 1560 } 1561 else if (!identifyInterval) 1562 { 1563 identifyTimer->stop(); 1564 enclosureIdentifyLedOff(); 1565 } 1566 return ipmi::responseSuccess(); 1567 } 1568 1569 namespace boot_options 1570 { 1571 1572 using namespace sdbusplus::server::xyz::openbmc_project::control::boot; 1573 using IpmiValue = uint8_t; 1574 constexpr auto ipmiDefault = 0; 1575 1576 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy}, 1577 {0x01, Type::Types::EFI}}; 1578 1579 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 1580 {0x01, Source::Sources::Network}, 1581 {0x02, Source::Sources::Disk}, 1582 {0x05, Source::Sources::ExternalMedia}, 1583 {0x0f, Source::Sources::RemovableMedia}, 1584 {ipmiDefault, Source::Sources::Default}}; 1585 1586 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 1587 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1588 {0x03, Mode::Modes::Safe}, 1589 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1590 {0x06, Mode::Modes::Setup}, 1591 {ipmiDefault, Mode::Modes::Regular}}; 1592 1593 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00}, 1594 {Type::Types::EFI, 0x01}}; 1595 1596 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 1597 {Source::Sources::Network, 0x01}, 1598 {Source::Sources::Disk, 0x02}, 1599 {Source::Sources::ExternalMedia, 0x05}, 1600 {Source::Sources::RemovableMedia, 0x0f}, 1601 {Source::Sources::Default, ipmiDefault}}; 1602 1603 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 1604 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT 1605 {Mode::Modes::Safe, 0x03}, 1606 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT 1607 {Mode::Modes::Setup, 0x06}, 1608 {Mode::Modes::Regular, ipmiDefault}}; 1609 1610 } // namespace boot_options 1611 1612 /** @brief Get the property value for boot source 1613 * @param[in] ctx - context pointer 1614 * @param[out] source - boot source value 1615 * @return On failure return IPMI error. 1616 */ 1617 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source) 1618 { 1619 using namespace chassis::internal; 1620 std::string result; 1621 std::string service; 1622 boost::system::error_code ec = 1623 getService(ctx, bootSourceIntf, bootSettingsPath, service); 1624 if (!ec) 1625 { 1626 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, 1627 bootSourceIntf, "BootSource", result); 1628 if (!ec) 1629 { 1630 source = Source::convertSourcesFromString(result); 1631 return ipmi::ccSuccess; 1632 } 1633 } 1634 lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message()); 1635 return ipmi::ccUnspecifiedError; 1636 } 1637 1638 /** @brief Set the property value for boot source 1639 * @param[in] ctx - context pointer 1640 * @param[in] source - boot source value 1641 * @return On failure return IPMI error. 1642 */ 1643 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx, 1644 const Source::Sources& source) 1645 { 1646 using namespace chassis::internal; 1647 std::string service; 1648 boost::system::error_code ec = 1649 getService(ctx, bootSourceIntf, bootSettingsPath, service); 1650 if (!ec) 1651 { 1652 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, 1653 bootSourceIntf, "BootSource", 1654 convertForMessage(source)); 1655 if (!ec) 1656 { 1657 return ipmi::ccSuccess; 1658 } 1659 } 1660 lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message()); 1661 return ipmi::ccUnspecifiedError; 1662 } 1663 1664 /** @brief Get the property value for boot mode 1665 * @param[in] ctx - context pointer 1666 * @param[out] mode - boot mode value 1667 * @return On failure return IPMI error. 1668 */ 1669 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode) 1670 { 1671 using namespace chassis::internal; 1672 std::string result; 1673 std::string service; 1674 boost::system::error_code ec = 1675 getService(ctx, bootModeIntf, bootSettingsPath, service); 1676 if (!ec) 1677 { 1678 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, 1679 "BootMode", result); 1680 if (!ec) 1681 { 1682 mode = Mode::convertModesFromString(result); 1683 return ipmi::ccSuccess; 1684 } 1685 } 1686 lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message()); 1687 return ipmi::ccUnspecifiedError; 1688 } 1689 1690 /** @brief Set the property value for boot mode 1691 * @param[in] ctx - context pointer 1692 * @param[in] mode - boot mode value 1693 * @return On failure return IPMI error. 1694 */ 1695 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode) 1696 { 1697 using namespace chassis::internal; 1698 std::string service; 1699 boost::system::error_code ec = 1700 getService(ctx, bootModeIntf, bootSettingsPath, service); 1701 if (!ec) 1702 { 1703 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, 1704 "BootMode", convertForMessage(mode)); 1705 if (!ec) 1706 { 1707 return ipmi::ccSuccess; 1708 } 1709 } 1710 lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message()); 1711 return ipmi::ccUnspecifiedError; 1712 } 1713 1714 /** @brief Get the property value for boot type 1715 * @param[in] ctx - context pointer 1716 * @param[out] type - boot type value 1717 * @return On failure return IPMI error. 1718 */ 1719 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type) 1720 { 1721 using namespace chassis::internal; 1722 std::string result; 1723 std::string service; 1724 boost::system::error_code ec = 1725 getService(ctx, bootTypeIntf, bootSettingsPath, service); 1726 1727 // Don't throw error if BootType interface is not present. 1728 // This interface is not relevant for some Host architectures 1729 // (for example POWER). In this case we don't won't IPMI to 1730 // return an error, but simply return bootType as EFI. 1731 type = Type::Types::EFI; 1732 if (!ec) 1733 { 1734 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, 1735 "BootType", result); 1736 if (ec) 1737 { 1738 lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message()); 1739 return ipmi::ccUnspecifiedError; 1740 } 1741 type = Type::convertTypesFromString(result); 1742 } 1743 1744 return ipmi::ccSuccess; 1745 } 1746 1747 /** @brief Set the property value for boot type 1748 * @param[in] ctx - context pointer 1749 * @param[in] type - boot type value 1750 * @return On failure return IPMI error. 1751 */ 1752 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type) 1753 { 1754 using namespace chassis::internal; 1755 std::string service; 1756 boost::system::error_code ec = 1757 getService(ctx, bootTypeIntf, bootSettingsPath, service); 1758 if (!ec) 1759 { 1760 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, 1761 "BootType", convertForMessage(type)); 1762 if (ec) 1763 { 1764 lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message()); 1765 return ipmi::ccUnspecifiedError; 1766 } 1767 } 1768 // Don't throw error if BootType interface is not present. 1769 // This interface is not relevant for some Host architectures 1770 // (for example POWER). In this case we don't won't IPMI to 1771 // return an error, but want to just skip this function. 1772 return ipmi::ccSuccess; 1773 } 1774 1775 /** @brief Get the property value for boot override enable 1776 * @param[in] ctx - context pointer 1777 * @param[out] enable - boot override enable 1778 * @return On failure return IPMI error. 1779 */ 1780 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable) 1781 { 1782 using namespace chassis::internal; 1783 std::string result; 1784 std::string service; 1785 boost::system::error_code ec = 1786 getService(ctx, bootEnableIntf, bootSettingsPath, service); 1787 if (!ec) 1788 { 1789 ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, 1790 bootEnableIntf, "Enabled", enable); 1791 if (!ec) 1792 { 1793 return ipmi::ccSuccess; 1794 } 1795 } 1796 lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR", 1797 ec.message()); 1798 return ipmi::ccUnspecifiedError; 1799 } 1800 1801 /** @brief Set the property value for boot override enable 1802 * @param[in] ctx - context pointer 1803 * @param[in] enable - boot override enable 1804 * @return On failure return IPMI error. 1805 */ 1806 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable) 1807 { 1808 using namespace chassis::internal; 1809 std::string service; 1810 boost::system::error_code ec = 1811 getService(ctx, bootEnableIntf, bootSettingsPath, service); 1812 if (!ec) 1813 { 1814 ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, 1815 bootEnableIntf, "Enabled", enable); 1816 if (!ec) 1817 { 1818 return ipmi::ccSuccess; 1819 } 1820 } 1821 lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR", 1822 ec.message()); 1823 return ipmi::ccUnspecifiedError; 1824 } 1825 1826 /** @brief Get the property value for boot override one-time 1827 * @param[in] ctx - context pointer 1828 * @param[out] onetime - boot override one-time 1829 * @return On failure return IPMI error. 1830 */ 1831 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime) 1832 { 1833 using namespace chassis::internal; 1834 std::string result; 1835 std::string service; 1836 boost::system::error_code ec = 1837 getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); 1838 if (!ec) 1839 { 1840 ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath, 1841 bootOneTimeIntf, "Enabled", onetime); 1842 if (!ec) 1843 { 1844 return ipmi::ccSuccess; 1845 } 1846 } 1847 lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR", 1848 ec.message()); 1849 return ipmi::ccUnspecifiedError; 1850 } 1851 1852 /** @brief Set the property value for boot override one-time 1853 * @param[in] ctx - context pointer 1854 * @param[in] onetime - boot override one-time 1855 * @return On failure return IPMI error. 1856 */ 1857 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime) 1858 { 1859 using namespace chassis::internal; 1860 std::string service; 1861 boost::system::error_code ec = 1862 getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); 1863 if (!ec) 1864 { 1865 ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath, 1866 bootOneTimeIntf, "Enabled", onetime); 1867 if (!ec) 1868 { 1869 return ipmi::ccSuccess; 1870 } 1871 } 1872 lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR", 1873 ec.message()); 1874 return ipmi::ccUnspecifiedError; 1875 } 1876 1877 static constexpr uint8_t setComplete = 0x0; 1878 static constexpr uint8_t setInProgress = 0x1; 1879 static uint8_t transferStatus = setComplete; 1880 static uint8_t bootFlagValidBitClr = 0; 1881 static uint5_t bootInitiatorAckData = 0x0; 1882 static bool cmosClear = false; 1883 static uint2_t biosVerbosity = 0x0; 1884 1885 /** @brief implements the Get Chassis system boot option 1886 * @param ctx - context pointer 1887 * @param bootOptionParameter - boot option parameter selector 1888 * @param reserved1 - reserved bit 1889 * @param setSelector - selects a particular block or set of parameters 1890 * under the given parameter selector 1891 * write as 00h if parameter doesn't use a setSelector 1892 * @param blockSelector- selects a particular block within a set of 1893 * parameters write as 00h if parameter doesn't use a 1894 * blockSelector 1895 * 1896 * @return IPMI completion code plus response data 1897 * @return Payload contains below parameters: 1898 * version - parameter version 1899 * bootOptionParameter - boot option parameter selector 1900 * parmIndicator - parameter valid/invalid indicator 1901 * data - configuration parameter data 1902 */ 1903 ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions( 1904 ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1, 1905 [[maybe_unused]] uint8_t setSelector, 1906 [[maybe_unused]] uint8_t blockSelector) 1907 { 1908 ipmi::Cc rc; 1909 if (reserved1) 1910 { 1911 return ipmi::responseInvalidFieldRequest(); 1912 } 1913 1914 constexpr uint4_t version = 0x01; 1915 ipmi::message::Payload response; 1916 response.pack(version, uint4_t{}); 1917 using namespace boot_options; 1918 1919 IpmiValue bootOption = ipmiDefault; 1920 1921 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1922 BootOptionParameter::setInProgress) 1923 { 1924 response.pack(bootOptionParameter, reserved1, transferStatus); 1925 return ipmi::responseSuccess(std::move(response)); 1926 } 1927 1928 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1929 BootOptionParameter::bootInfo) 1930 { 1931 constexpr uint8_t writeMask = 0; 1932 response.pack(bootOptionParameter, reserved1, writeMask, 1933 bootInitiatorAckData); 1934 return ipmi::responseSuccess(std::move(response)); 1935 } 1936 1937 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1938 BootOptionParameter::bootFlagValidClr) 1939 { 1940 response.pack(bootOptionParameter, reserved1, 1941 uint5_t{bootFlagValidBitClr}, uint3_t{}); 1942 return ipmi::responseSuccess(std::move(response)); 1943 } 1944 1945 /* 1946 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1947 * This is the only parameter used by petitboot. 1948 */ 1949 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1950 BootOptionParameter::bootFlags) 1951 { 1952 using namespace chassis::internal; 1953 using namespace chassis::internal::cache; 1954 1955 try 1956 { 1957 Source::Sources bootSource; 1958 rc = getBootSource(ctx, bootSource); 1959 if (rc != ipmi::ccSuccess) 1960 { 1961 return ipmi::response(rc); 1962 } 1963 1964 Type::Types bootType; 1965 rc = getBootType(ctx, bootType); 1966 if (rc != ipmi::ccSuccess) 1967 { 1968 return ipmi::response(rc); 1969 } 1970 1971 Mode::Modes bootMode; 1972 rc = getBootMode(ctx, bootMode); 1973 if (rc != ipmi::ccSuccess) 1974 { 1975 return ipmi::response(rc); 1976 } 1977 1978 bootOption = sourceDbusToIpmi.at(bootSource); 1979 if ((Mode::Modes::Regular == bootMode) && 1980 (Source::Sources::Default == bootSource)) 1981 { 1982 bootOption = ipmiDefault; 1983 } 1984 else if (Source::Sources::Default == bootSource) 1985 { 1986 bootOption = modeDbusToIpmi.at(bootMode); 1987 } 1988 1989 IpmiValue biosBootType = typeDbusToIpmi.at(bootType); 1990 1991 bool oneTimeEnabled; 1992 rc = getBootOneTime(ctx, oneTimeEnabled); 1993 if (rc != ipmi::ccSuccess) 1994 { 1995 return ipmi::response(rc); 1996 } 1997 1998 uint1_t permanent = oneTimeEnabled ? 0 : 1; 1999 2000 bool valid; 2001 rc = getBootEnable(ctx, valid); 2002 if (rc != ipmi::ccSuccess) 2003 { 2004 return ipmi::response(rc); 2005 } 2006 2007 uint1_t validFlag = valid ? 1 : 0; 2008 2009 response.pack( 2010 bootOptionParameter, reserved1, uint5_t{}, 2011 uint1_t{biosBootType}, uint1_t{permanent}, uint1_t{validFlag}, 2012 uint2_t{}, uint4_t{bootOption}, uint1_t{}, cmosClear, uint5_t{}, 2013 uint2_t{biosVerbosity}, uint1_t{}, uint8_t{}, uint8_t{}); 2014 return ipmi::responseSuccess(std::move(response)); 2015 } 2016 catch (const InternalFailure& e) 2017 { 2018 cache::objectsPtr.reset(); 2019 report<InternalFailure>(); 2020 return ipmi::responseUnspecifiedError(); 2021 } 2022 } 2023 else 2024 { 2025 if ((bootOptionParameter >= oemParmStart) && 2026 (bootOptionParameter <= oemParmEnd)) 2027 { 2028 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 2029 BootOptionParameter::opalNetworkSettings) 2030 { 2031 response.pack(bootOptionParameter, reserved1); 2032 int ret = getHostNetworkData(response); 2033 if (ret < 0) 2034 { 2035 response.trailingOk = true; 2036 lg2::error( 2037 "getHostNetworkData failed for GetSysBootOptions."); 2038 return ipmi::responseUnspecifiedError(); 2039 } 2040 else 2041 { 2042 return ipmi::responseSuccess(std::move(response)); 2043 } 2044 } 2045 else 2046 { 2047 lg2::error( 2048 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 2049 "PARAM", lg2::hex, 2050 static_cast<uint8_t>(bootOptionParameter)); 2051 return ipmi::responseParmNotSupported(); 2052 } 2053 } 2054 else 2055 { 2056 lg2::error( 2057 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 2058 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter)); 2059 return ipmi::responseParmNotSupported(); 2060 } 2061 } 2062 return ipmi::responseUnspecifiedError(); 2063 } 2064 2065 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, 2066 uint7_t parameterSelector, bool, 2067 ipmi::message::Payload& data) 2068 { 2069 using namespace boot_options; 2070 ipmi::Cc rc; 2071 2072 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2073 BootOptionParameter::setInProgress) 2074 { 2075 uint2_t setInProgressFlag; 2076 uint6_t rsvd; 2077 if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked()) 2078 { 2079 return ipmi::responseReqDataLenInvalid(); 2080 } 2081 if (rsvd) 2082 { 2083 return ipmi::responseInvalidFieldRequest(); 2084 } 2085 if ((transferStatus == setInProgress) && 2086 (static_cast<uint8_t>(setInProgressFlag) != setComplete)) 2087 { 2088 return ipmi::responsefailSetInProgress(); 2089 } 2090 transferStatus = static_cast<uint8_t>(setInProgressFlag); 2091 return ipmi::responseSuccess(); 2092 } 2093 2094 /* 000101 2095 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 2096 * This is the only parameter used by petitboot. 2097 */ 2098 2099 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2100 BootOptionParameter::bootFlags) 2101 { 2102 uint5_t rsvd; 2103 bool validFlag; 2104 bool permanent; 2105 bool biosBootType; 2106 bool lockOutResetButton; 2107 bool screenBlank; 2108 uint4_t bootDeviceSelector; 2109 bool lockKeyboard; 2110 uint5_t biosCtrls; 2111 bool lockOutPower; 2112 uint4_t biosInfo; 2113 uint4_t rsvd1; 2114 uint5_t deviceInstance; 2115 uint3_t rsvd2; 2116 2117 if (data.unpack(rsvd, biosBootType, permanent, validFlag, 2118 lockOutResetButton, screenBlank, bootDeviceSelector, 2119 lockKeyboard, cmosClear, biosCtrls, biosVerbosity, 2120 lockOutPower, biosInfo, rsvd1, deviceInstance, rsvd2) != 2121 0 || 2122 !data.fullyUnpacked()) 2123 { 2124 return ipmi::responseReqDataLenInvalid(); 2125 } 2126 if (rsvd || rsvd1 || rsvd2) 2127 { 2128 return ipmi::responseInvalidFieldRequest(); 2129 } 2130 2131 using namespace chassis::internal; 2132 using namespace chassis::internal::cache; 2133 2134 try 2135 { 2136 rc = setBootOneTime(ctx, !permanent); 2137 if (rc != ipmi::ccSuccess) 2138 { 2139 return ipmi::response(rc); 2140 } 2141 2142 rc = setBootEnable(ctx, validFlag); 2143 if (rc != ipmi::ccSuccess) 2144 { 2145 return ipmi::response(rc); 2146 } 2147 2148 auto modeItr = 2149 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2150 auto typeItr = 2151 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType)); 2152 auto sourceItr = 2153 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2154 if (sourceIpmiToDbus.end() != sourceItr) 2155 { 2156 rc = setBootSource(ctx, sourceItr->second); 2157 if (rc != ipmi::ccSuccess) 2158 { 2159 return ipmi::response(rc); 2160 } 2161 // If a set boot device is mapping to a boot source, then reset 2162 // the boot mode D-Bus property to default. 2163 // This way the ipmid code can determine which property is not 2164 // at the default value 2165 if (sourceItr->second != Source::Sources::Default) 2166 { 2167 rc = setBootMode(ctx, Mode::Modes::Regular); 2168 if (rc != ipmi::ccSuccess) 2169 { 2170 return ipmi::response(rc); 2171 } 2172 } 2173 } 2174 2175 if (typeIpmiToDbus.end() != typeItr) 2176 { 2177 rc = setBootType(ctx, typeItr->second); 2178 if (rc != ipmi::ccSuccess) 2179 { 2180 return ipmi::response(rc); 2181 } 2182 } 2183 2184 if (modeIpmiToDbus.end() != modeItr) 2185 { 2186 rc = setBootMode(ctx, modeItr->second); 2187 if (rc != ipmi::ccSuccess) 2188 { 2189 return ipmi::response(rc); 2190 } 2191 // If a set boot device is mapping to a boot mode, then reset 2192 // the boot source D-Bus property to default. 2193 // This way the ipmid code can determine which property is not 2194 // at the default value 2195 if (modeItr->second != Mode::Modes::Regular) 2196 { 2197 rc = setBootSource(ctx, Source::Sources::Default); 2198 if (rc != ipmi::ccSuccess) 2199 { 2200 return ipmi::response(rc); 2201 } 2202 } 2203 } 2204 if ((modeIpmiToDbus.end() == modeItr) && 2205 (typeIpmiToDbus.end() == typeItr) && 2206 (sourceIpmiToDbus.end() == sourceItr)) 2207 { 2208 // return error if boot option is not supported 2209 lg2::error( 2210 "ipmiChassisSetSysBootOptions: Boot option not supported"); 2211 return ipmi::responseInvalidFieldRequest(); 2212 } 2213 } 2214 catch (const sdbusplus::exception_t& e) 2215 { 2216 objectsPtr.reset(); 2217 report<InternalFailure>(); 2218 lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot " 2219 "flag parameters"); 2220 return ipmi::responseUnspecifiedError(); 2221 } 2222 } 2223 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2224 BootOptionParameter::bootInfo) 2225 { 2226 uint8_t writeMak; 2227 uint5_t bootInfoAck; 2228 uint3_t rsvd; 2229 2230 if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 || 2231 !data.fullyUnpacked()) 2232 { 2233 return ipmi::responseReqDataLenInvalid(); 2234 } 2235 if (rsvd) 2236 { 2237 return ipmi::responseInvalidFieldRequest(); 2238 } 2239 bootInitiatorAckData &= ~writeMak; 2240 bootInitiatorAckData |= (writeMak & bootInfoAck); 2241 lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set " 2242 "successfully"); 2243 data.trailingOk = true; 2244 return ipmi::responseSuccess(); 2245 } 2246 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2247 BootOptionParameter::bootFlagValidClr) 2248 { 2249 uint5_t bootFlagValidClr; 2250 uint3_t rsvd; 2251 2252 if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked()) 2253 { 2254 return ipmi::responseReqDataLenInvalid(); 2255 } 2256 if (rsvd) 2257 { 2258 return ipmi::responseInvalidFieldRequest(); 2259 } 2260 // store boot flag valid bits clear value 2261 bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr); 2262 lg2::info( 2263 "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set " 2264 "successfully to {VALUE}", 2265 "VALUE", lg2::hex, bootFlagValidBitClr); 2266 return ipmi::responseSuccess(); 2267 } 2268 else 2269 { 2270 if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) && 2271 (parameterSelector <= static_cast<uint7_t>(oemParmEnd))) 2272 { 2273 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2274 BootOptionParameter::opalNetworkSettings) 2275 { 2276 ipmi::Cc ret = setHostNetworkData(data); 2277 if (ret != ipmi::ccSuccess) 2278 { 2279 lg2::error("ipmiChassisSetSysBootOptions: Error in " 2280 "setHostNetworkData"); 2281 data.trailingOk = true; 2282 return ipmi::response(ret); 2283 } 2284 data.trailingOk = true; 2285 return ipmi::responseSuccess(); 2286 } 2287 else 2288 { 2289 lg2::error( 2290 "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}", 2291 "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector)); 2292 data.trailingOk = true; 2293 return ipmi::responseParmNotSupported(); 2294 } 2295 } 2296 data.trailingOk = true; 2297 return ipmi::responseParmNotSupported(); 2298 } 2299 return ipmi::responseSuccess(); 2300 } 2301 2302 /** @brief implements Get POH counter command 2303 * @parameter 2304 * - none 2305 * @returns IPMI completion code plus response data 2306 * - minPerCount - Minutes per count 2307 * - counterReading - counter reading 2308 */ 2309 ipmi::RspType<uint8_t, // Minutes per count 2310 uint32_t // Counter reading 2311 > 2312 ipmiGetPOHCounter() 2313 { 2314 // sd_bus error 2315 try 2316 { 2317 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), 2318 getPOHCounter()); 2319 } 2320 catch (const std::exception& e) 2321 { 2322 lg2::error("getPOHCounter error: {ERROR}", "ERROR", e); 2323 return ipmi::responseUnspecifiedError(); 2324 } 2325 } 2326 2327 ipmi::RspType<uint3_t, // policy support 2328 uint5_t // reserved 2329 > 2330 ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx, uint3_t policy, 2331 uint5_t reserved) 2332 { 2333 power_policy::DbusValue value = 2334 power_policy::RestorePolicy::Policy::AlwaysOff; 2335 2336 if (reserved || (policy > power_policy::noChange)) 2337 { 2338 lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex, 2339 static_cast<int>(policy)); 2340 return ipmi::responseInvalidFieldRequest(); 2341 } 2342 2343 if (policy == power_policy::noChange) 2344 { 2345 // just return the supported policy 2346 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2347 } 2348 2349 for (const auto& it : power_policy::dbusToIpmi) 2350 { 2351 if (it.second == policy) 2352 { 2353 value = it.first; 2354 break; 2355 } 2356 } 2357 2358 try 2359 { 2360 settings::Objects& objects = chassis::internal::cache::getObjects(); 2361 const settings::Path& powerRestoreSetting = 2362 objects.map.at(chassis::internal::powerRestoreIntf).front(); 2363 2364 boost::system::error_code ec = ipmi::setDbusProperty( 2365 ctx, 2366 objects.service(powerRestoreSetting, 2367 chassis::internal::powerRestoreIntf), 2368 powerRestoreSetting, chassis::internal::powerRestoreIntf, 2369 "PowerRestorePolicy", convertForMessage(value)); 2370 if (ec) 2371 { 2372 lg2::error("Unspecified Error"); 2373 return ipmi::responseUnspecifiedError(); 2374 } 2375 } 2376 catch (const InternalFailure& e) 2377 { 2378 chassis::internal::cache::objectsPtr.reset(); 2379 report<InternalFailure>(); 2380 return ipmi::responseUnspecifiedError(); 2381 } 2382 2383 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2384 } 2385 2386 ipmi::RspType<> ipmiSetFrontPanelButtonEnables( 2387 ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton, 2388 bool, bool, uint4_t) 2389 { 2390 using namespace chassis::internal; 2391 2392 // set power button Enabled property 2393 bool success = setButtonDisabled(ctx, powerButtonPath, powerButtonIntf, 2394 disablePowerButton); 2395 2396 // set reset button Enabled property 2397 success &= setButtonDisabled(ctx, resetButtonPath, resetButtonIntf, 2398 disableResetButton); 2399 2400 if (!success) 2401 { 2402 // not all buttons were successfully set 2403 return ipmi::responseUnspecifiedError(); 2404 } 2405 return ipmi::responseSuccess(); 2406 } 2407 2408 void registerNetFnChassisFunctions() 2409 { 2410 createIdentifyTimer(); 2411 2412 // Get Chassis Capabilities 2413 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2414 ipmi::chassis::cmdGetChassisCapabilities, 2415 ipmi::Privilege::User, ipmiGetChassisCap); 2416 2417 // Set Front Panel Button Enables 2418 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2419 ipmi::chassis::cmdSetFrontPanelButtonEnables, 2420 ipmi::Privilege::Admin, 2421 ipmiSetFrontPanelButtonEnables); 2422 2423 // Set Chassis Capabilities 2424 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2425 ipmi::chassis::cmdSetChassisCapabilities, 2426 ipmi::Privilege::User, ipmiSetChassisCap); 2427 2428 // <Get System Boot Options> 2429 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2430 ipmi::chassis::cmdGetSystemBootOptions, 2431 ipmi::Privilege::Operator, 2432 ipmiChassisGetSysBootOptions); 2433 2434 // <Get Chassis Status> 2435 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2436 ipmi::chassis::cmdGetChassisStatus, 2437 ipmi::Privilege::User, ipmiGetChassisStatus); 2438 2439 // <Chassis Get System Restart Cause> 2440 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2441 ipmi::chassis::cmdGetSystemRestartCause, 2442 ipmi::Privilege::User, ipmiGetSystemRestartCause); 2443 2444 // <Chassis Control> 2445 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2446 ipmi::chassis::cmdChassisControl, 2447 ipmi::Privilege::Operator, ipmiChassisControl); 2448 2449 // <Chassis Identify> 2450 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2451 ipmi::chassis::cmdChassisIdentify, 2452 ipmi::Privilege::Operator, ipmiChassisIdentify); 2453 2454 // <Set System Boot Options> 2455 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2456 ipmi::chassis::cmdSetSystemBootOptions, 2457 ipmi::Privilege::Operator, 2458 ipmiChassisSetSysBootOptions); 2459 2460 // <Get POH Counter> 2461 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2462 ipmi::chassis::cmdGetPohCounter, 2463 ipmi::Privilege::User, ipmiGetPOHCounter); 2464 2465 // <Set Power Restore Policy> 2466 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2467 ipmi::chassis::cmdSetPowerRestorePolicy, 2468 ipmi::Privilege::Operator, 2469 ipmiChassisSetPowerRestorePolicy); 2470 } 2471