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