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