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(bus, macObjectInfo.second, 200 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 += "ipaddress="s + ipAddress + ",prefix="s + 522 std::to_string(prefix) + ",gateway="s + gateway + 523 ",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 = ipmi::getDbusObject(bus, chassisPOHStateIntf, 565 chassisStateRoot, match); 566 567 auto service = ipmi::getService(bus, chassisPOHStateIntf, 568 chassisStateObj.first); 569 570 auto propValue = ipmi::getDbusProperty(bus, service, chassisStateObj.first, 571 chassisPOHStateIntf, 572 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 = ipmi::getAllDbusProperties(bus, chassisCapObject.second, 621 chassisCapObject.first, 622 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(*chassisIntrusionFlag, *chassisFrontPanelFlag, 695 *chassisNMIFlag, *chassisPowerInterlockFlag, 0, 696 *chassisFRUInfoDevAddr, *chassisSDRDevAddr, 697 *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(bool intrusion, bool fpLockout, 714 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 = ipmi::getService(ctx, hostStateIntf, 827 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 = ipmi::getService(ctx, chassisStateIntf, 859 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 = ipmi::getService(*busp, chassisStateIntf, 978 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 = ipmi::getService(*busp, legacyPwrCtrlIntf, 996 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 = ipmi::getService(*bus, powerControlIntf, 1027 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 = (chassisIntrusionStr == "HardwareIntrusion") ? true 1105 : 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 = getButtonEnabled(powerButtonPath, 1164 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 = getButtonEnabled(resetButtonPath, 1176 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 = ipmi::getService(ctx, restartCauseIntf, 1303 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 = getService(ctx, bootSourceIntf, 1557 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 = getService(ctx, bootSourceIntf, 1583 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 = getService(ctx, bootModeIntf, 1609 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 = getService(ctx, bootModeIntf, 1634 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 = getService(ctx, bootTypeIntf, 1659 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 = getService(ctx, bootTypeIntf, 1691 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 = getService(ctx, bootEnableIntf, 1720 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 = getService(ctx, bootEnableIntf, 1745 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 = getService(ctx, bootOneTimeIntf, 1771 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 = getService(ctx, bootOneTimeIntf, 1796 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> 1837 ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx, 1838 uint7_t bootOptionParameter, bool reserved1, 1839 [[maybe_unused]] uint8_t setSelector, 1840 [[maybe_unused]] uint8_t blockSelector) 1841 { 1842 ipmi::Cc rc; 1843 if (reserved1) 1844 { 1845 return ipmi::responseInvalidFieldRequest(); 1846 } 1847 1848 constexpr uint4_t version = 0x01; 1849 ipmi::message::Payload response; 1850 response.pack(version, uint4_t{}); 1851 using namespace boot_options; 1852 1853 IpmiValue bootOption = ipmiDefault; 1854 1855 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1856 BootOptionParameter::setInProgress) 1857 { 1858 response.pack(bootOptionParameter, reserved1, transferStatus); 1859 return ipmi::responseSuccess(std::move(response)); 1860 } 1861 1862 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1863 BootOptionParameter::bootInfo) 1864 { 1865 constexpr uint8_t writeMask = 0; 1866 response.pack(bootOptionParameter, reserved1, writeMask, 1867 bootInitiatorAckData); 1868 return ipmi::responseSuccess(std::move(response)); 1869 } 1870 1871 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1872 BootOptionParameter::bootFlagValidClr) 1873 { 1874 response.pack(bootOptionParameter, reserved1, 1875 uint5_t{bootFlagValidBitClr}, uint3_t{}); 1876 return ipmi::responseSuccess(std::move(response)); 1877 } 1878 1879 /* 1880 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1881 * This is the only parameter used by petitboot. 1882 */ 1883 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1884 BootOptionParameter::bootFlags) 1885 { 1886 using namespace chassis::internal; 1887 using namespace chassis::internal::cache; 1888 1889 try 1890 { 1891 Source::Sources bootSource; 1892 rc = getBootSource(ctx, bootSource); 1893 if (rc != ipmi::ccSuccess) 1894 { 1895 return ipmi::response(rc); 1896 } 1897 1898 Type::Types bootType; 1899 rc = getBootType(ctx, bootType); 1900 if (rc != ipmi::ccSuccess) 1901 { 1902 return ipmi::response(rc); 1903 } 1904 1905 Mode::Modes bootMode; 1906 rc = getBootMode(ctx, bootMode); 1907 if (rc != ipmi::ccSuccess) 1908 { 1909 return ipmi::response(rc); 1910 } 1911 1912 bootOption = sourceDbusToIpmi.at(bootSource); 1913 if ((Mode::Modes::Regular == bootMode) && 1914 (Source::Sources::Default == bootSource)) 1915 { 1916 bootOption = ipmiDefault; 1917 } 1918 else if (Source::Sources::Default == bootSource) 1919 { 1920 bootOption = modeDbusToIpmi.at(bootMode); 1921 } 1922 1923 IpmiValue biosBootType = typeDbusToIpmi.at(bootType); 1924 1925 bool oneTimeEnabled; 1926 rc = getBootOneTime(ctx, oneTimeEnabled); 1927 if (rc != ipmi::ccSuccess) 1928 { 1929 return ipmi::response(rc); 1930 } 1931 1932 uint1_t permanent = oneTimeEnabled ? 0 : 1; 1933 1934 bool valid; 1935 rc = getBootEnable(ctx, valid); 1936 if (rc != ipmi::ccSuccess) 1937 { 1938 return ipmi::response(rc); 1939 } 1940 1941 uint1_t validFlag = valid ? 1 : 0; 1942 1943 response.pack(bootOptionParameter, reserved1, uint5_t{}, 1944 uint1_t{biosBootType}, uint1_t{permanent}, 1945 uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption}, 1946 uint1_t{}, cmosClear, uint8_t{}, uint8_t{}, 1947 uint8_t{}); 1948 return ipmi::responseSuccess(std::move(response)); 1949 } 1950 catch (const InternalFailure& e) 1951 { 1952 cache::objectsPtr.reset(); 1953 report<InternalFailure>(); 1954 return ipmi::responseUnspecifiedError(); 1955 } 1956 } 1957 else 1958 { 1959 if ((bootOptionParameter >= oemParmStart) && 1960 (bootOptionParameter <= oemParmEnd)) 1961 { 1962 if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == 1963 BootOptionParameter::opalNetworkSettings) 1964 { 1965 response.pack(bootOptionParameter, reserved1); 1966 int ret = getHostNetworkData(response); 1967 if (ret < 0) 1968 { 1969 response.trailingOk = true; 1970 lg2::error( 1971 "getHostNetworkData failed for GetSysBootOptions."); 1972 return ipmi::responseUnspecifiedError(); 1973 } 1974 else 1975 { 1976 return ipmi::responseSuccess(std::move(response)); 1977 } 1978 } 1979 else 1980 { 1981 lg2::error( 1982 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 1983 "PARAM", lg2::hex, 1984 static_cast<uint8_t>(bootOptionParameter)); 1985 return ipmi::responseParmNotSupported(); 1986 } 1987 } 1988 else 1989 { 1990 lg2::error( 1991 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", 1992 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter)); 1993 return ipmi::responseParmNotSupported(); 1994 } 1995 } 1996 return ipmi::responseUnspecifiedError(); 1997 } 1998 1999 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, 2000 uint7_t parameterSelector, bool, 2001 ipmi::message::Payload& data) 2002 { 2003 using namespace boot_options; 2004 ipmi::Cc rc; 2005 2006 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2007 BootOptionParameter::setInProgress) 2008 { 2009 uint2_t setInProgressFlag; 2010 uint6_t rsvd; 2011 if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked()) 2012 { 2013 return ipmi::responseReqDataLenInvalid(); 2014 } 2015 if (rsvd) 2016 { 2017 return ipmi::responseInvalidFieldRequest(); 2018 } 2019 if ((transferStatus == setInProgress) && 2020 (static_cast<uint8_t>(setInProgressFlag) != setComplete)) 2021 { 2022 return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS); 2023 } 2024 transferStatus = static_cast<uint8_t>(setInProgressFlag); 2025 return ipmi::responseSuccess(); 2026 } 2027 2028 /* 000101 2029 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 2030 * This is the only parameter used by petitboot. 2031 */ 2032 2033 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2034 BootOptionParameter::bootFlags) 2035 { 2036 uint5_t rsvd; 2037 bool validFlag; 2038 bool permanent; 2039 bool biosBootType; 2040 bool lockOutResetButton; 2041 bool screenBlank; 2042 uint4_t bootDeviceSelector; 2043 bool lockKeyboard; 2044 uint8_t data3; 2045 uint4_t biosInfo; 2046 uint4_t rsvd1; 2047 uint5_t deviceInstance; 2048 uint3_t rsvd2; 2049 2050 if (data.unpack(rsvd, biosBootType, permanent, validFlag, 2051 lockOutResetButton, screenBlank, bootDeviceSelector, 2052 lockKeyboard, cmosClear, data3, biosInfo, rsvd1, 2053 deviceInstance, rsvd2) != 0 || 2054 !data.fullyUnpacked()) 2055 { 2056 return ipmi::responseReqDataLenInvalid(); 2057 } 2058 if (rsvd || rsvd1 || rsvd2) 2059 { 2060 return ipmi::responseInvalidFieldRequest(); 2061 } 2062 2063 using namespace chassis::internal; 2064 using namespace chassis::internal::cache; 2065 2066 try 2067 { 2068 rc = setBootOneTime(ctx, !permanent); 2069 if (rc != ipmi::ccSuccess) 2070 { 2071 return ipmi::response(rc); 2072 } 2073 2074 rc = setBootEnable(ctx, validFlag); 2075 if (rc != ipmi::ccSuccess) 2076 { 2077 return ipmi::response(rc); 2078 } 2079 2080 auto modeItr = 2081 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2082 auto typeItr = 2083 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType)); 2084 auto sourceItr = 2085 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); 2086 if (sourceIpmiToDbus.end() != sourceItr) 2087 { 2088 rc = setBootSource(ctx, sourceItr->second); 2089 if (rc != ipmi::ccSuccess) 2090 { 2091 return ipmi::response(rc); 2092 } 2093 // If a set boot device is mapping to a boot source, then reset 2094 // the boot mode D-Bus property to default. 2095 // This way the ipmid code can determine which property is not 2096 // at the default value 2097 if (sourceItr->second != Source::Sources::Default) 2098 { 2099 rc = setBootMode(ctx, Mode::Modes::Regular); 2100 if (rc != ipmi::ccSuccess) 2101 { 2102 return ipmi::response(rc); 2103 } 2104 } 2105 } 2106 2107 if (typeIpmiToDbus.end() != typeItr) 2108 { 2109 rc = setBootType(ctx, typeItr->second); 2110 if (rc != ipmi::ccSuccess) 2111 { 2112 return ipmi::response(rc); 2113 } 2114 } 2115 2116 if (modeIpmiToDbus.end() != modeItr) 2117 { 2118 rc = setBootMode(ctx, modeItr->second); 2119 if (rc != ipmi::ccSuccess) 2120 { 2121 return ipmi::response(rc); 2122 } 2123 // If a set boot device is mapping to a boot mode, then reset 2124 // the boot source D-Bus property to default. 2125 // This way the ipmid code can determine which property is not 2126 // at the default value 2127 if (modeItr->second != Mode::Modes::Regular) 2128 { 2129 rc = setBootSource(ctx, Source::Sources::Default); 2130 if (rc != ipmi::ccSuccess) 2131 { 2132 return ipmi::response(rc); 2133 } 2134 } 2135 } 2136 if ((modeIpmiToDbus.end() == modeItr) && 2137 (typeIpmiToDbus.end() == typeItr) && 2138 (sourceIpmiToDbus.end() == sourceItr)) 2139 { 2140 // return error if boot option is not supported 2141 lg2::error( 2142 "ipmiChassisSetSysBootOptions: Boot option not supported"); 2143 return ipmi::responseInvalidFieldRequest(); 2144 } 2145 } 2146 catch (const sdbusplus::exception_t& e) 2147 { 2148 objectsPtr.reset(); 2149 report<InternalFailure>(); 2150 lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot " 2151 "flag parameters"); 2152 return ipmi::responseUnspecifiedError(); 2153 } 2154 } 2155 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2156 BootOptionParameter::bootInfo) 2157 { 2158 uint8_t writeMak; 2159 uint5_t bootInfoAck; 2160 uint3_t rsvd; 2161 2162 if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 || 2163 !data.fullyUnpacked()) 2164 { 2165 return ipmi::responseReqDataLenInvalid(); 2166 } 2167 if (rsvd) 2168 { 2169 return ipmi::responseInvalidFieldRequest(); 2170 } 2171 bootInitiatorAckData &= ~writeMak; 2172 bootInitiatorAckData |= (writeMak & bootInfoAck); 2173 lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set " 2174 "successfully"); 2175 data.trailingOk = true; 2176 return ipmi::responseSuccess(); 2177 } 2178 else if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2179 BootOptionParameter::bootFlagValidClr) 2180 { 2181 uint5_t bootFlagValidClr; 2182 uint3_t rsvd; 2183 2184 if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked()) 2185 { 2186 return ipmi::responseReqDataLenInvalid(); 2187 } 2188 if (rsvd) 2189 { 2190 return ipmi::responseInvalidFieldRequest(); 2191 } 2192 // store boot flag valid bits clear value 2193 bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr); 2194 lg2::info( 2195 "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set " 2196 "successfully to {VALUE}", 2197 "VALUE", lg2::hex, bootFlagValidBitClr); 2198 return ipmi::responseSuccess(); 2199 } 2200 else 2201 { 2202 if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) && 2203 (parameterSelector <= static_cast<uint7_t>(oemParmEnd))) 2204 { 2205 if (types::enum_cast<BootOptionParameter>(parameterSelector) == 2206 BootOptionParameter::opalNetworkSettings) 2207 { 2208 ipmi::Cc ret = setHostNetworkData(data); 2209 if (ret != ipmi::ccSuccess) 2210 { 2211 lg2::error("ipmiChassisSetSysBootOptions: Error in " 2212 "setHostNetworkData"); 2213 data.trailingOk = true; 2214 return ipmi::response(ret); 2215 } 2216 data.trailingOk = true; 2217 return ipmi::responseSuccess(); 2218 } 2219 else 2220 { 2221 lg2::error( 2222 "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}", 2223 "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector)); 2224 data.trailingOk = true; 2225 return ipmi::responseParmNotSupported(); 2226 } 2227 } 2228 data.trailingOk = true; 2229 return ipmi::responseParmNotSupported(); 2230 } 2231 return ipmi::responseSuccess(); 2232 } 2233 2234 /** @brief implements Get POH counter command 2235 * @parameter 2236 * - none 2237 * @returns IPMI completion code plus response data 2238 * - minPerCount - Minutes per count 2239 * - counterReading - counter reading 2240 */ 2241 ipmi::RspType<uint8_t, // Minutes per count 2242 uint32_t // Counter reading 2243 > 2244 ipmiGetPOHCounter() 2245 { 2246 // sd_bus error 2247 try 2248 { 2249 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), 2250 getPOHCounter()); 2251 } 2252 catch (const std::exception& e) 2253 { 2254 lg2::error("getPOHCounter error: {ERROR}", "ERROR", e); 2255 return ipmi::responseUnspecifiedError(); 2256 } 2257 } 2258 2259 ipmi::RspType<uint3_t, // policy support 2260 uint5_t // reserved 2261 > 2262 ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield, 2263 uint3_t policy, uint5_t reserved) 2264 { 2265 power_policy::DbusValue value = 2266 power_policy::RestorePolicy::Policy::AlwaysOff; 2267 2268 if (reserved || (policy > power_policy::noChange)) 2269 { 2270 lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex, 2271 static_cast<int>(policy)); 2272 return ipmi::responseInvalidFieldRequest(); 2273 } 2274 2275 if (policy == power_policy::noChange) 2276 { 2277 // just return the supported policy 2278 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2279 } 2280 2281 for (const auto& it : power_policy::dbusToIpmi) 2282 { 2283 if (it.second == policy) 2284 { 2285 value = it.first; 2286 break; 2287 } 2288 } 2289 2290 try 2291 { 2292 settings::Objects& objects = chassis::internal::cache::getObjects(); 2293 const settings::Path& powerRestoreSetting = 2294 objects.map.at(chassis::internal::powerRestoreIntf).front(); 2295 std::variant<std::string> property = convertForMessage(value); 2296 2297 auto sdbusp = getSdBus(); 2298 boost::system::error_code ec; 2299 sdbusp->yield_method_call<void>( 2300 yield, ec, 2301 objects 2302 .service(powerRestoreSetting, 2303 chassis::internal::powerRestoreIntf) 2304 .c_str(), 2305 powerRestoreSetting, ipmi::PROP_INTF, "Set", 2306 chassis::internal::powerRestoreIntf, "PowerRestorePolicy", 2307 property); 2308 if (ec) 2309 { 2310 lg2::error("Unspecified Error"); 2311 return ipmi::responseUnspecifiedError(); 2312 } 2313 } 2314 catch (const InternalFailure& e) 2315 { 2316 chassis::internal::cache::objectsPtr.reset(); 2317 report<InternalFailure>(); 2318 return ipmi::responseUnspecifiedError(); 2319 } 2320 2321 return ipmi::responseSuccess(power_policy::allSupport, reserved); 2322 } 2323 2324 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx, 2325 bool disablePowerButton, 2326 bool disableResetButton, bool, 2327 bool, uint4_t) 2328 { 2329 using namespace chassis::internal; 2330 2331 // set power button Enabled property 2332 bool success = setButtonEnabled(ctx, powerButtonPath, powerButtonIntf, 2333 !disablePowerButton); 2334 2335 // set reset button Enabled property 2336 success &= setButtonEnabled(ctx, resetButtonPath, resetButtonIntf, 2337 !disableResetButton); 2338 2339 if (!success) 2340 { 2341 // not all buttons were successfully set 2342 return ipmi::responseUnspecifiedError(); 2343 } 2344 return ipmi::responseSuccess(); 2345 } 2346 2347 void register_netfn_chassis_functions() 2348 { 2349 createIdentifyTimer(); 2350 2351 // Get Chassis Capabilities 2352 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2353 ipmi::chassis::cmdGetChassisCapabilities, 2354 ipmi::Privilege::User, ipmiGetChassisCap); 2355 2356 // Set Front Panel Button Enables 2357 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2358 ipmi::chassis::cmdSetFrontPanelButtonEnables, 2359 ipmi::Privilege::Admin, 2360 ipmiSetFrontPanelButtonEnables); 2361 2362 // Set Chassis Capabilities 2363 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2364 ipmi::chassis::cmdSetChassisCapabilities, 2365 ipmi::Privilege::User, ipmiSetChassisCap); 2366 2367 // <Get System Boot Options> 2368 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2369 ipmi::chassis::cmdGetSystemBootOptions, 2370 ipmi::Privilege::Operator, 2371 ipmiChassisGetSysBootOptions); 2372 2373 // <Get Chassis Status> 2374 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2375 ipmi::chassis::cmdGetChassisStatus, 2376 ipmi::Privilege::User, ipmiGetChassisStatus); 2377 2378 // <Chassis Get System Restart Cause> 2379 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2380 ipmi::chassis::cmdGetSystemRestartCause, 2381 ipmi::Privilege::User, ipmiGetSystemRestartCause); 2382 2383 // <Chassis Control> 2384 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2385 ipmi::chassis::cmdChassisControl, 2386 ipmi::Privilege::Operator, ipmiChassisControl); 2387 2388 // <Chassis Identify> 2389 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2390 ipmi::chassis::cmdChassisIdentify, 2391 ipmi::Privilege::Operator, ipmiChassisIdentify); 2392 2393 // <Set System Boot Options> 2394 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2395 ipmi::chassis::cmdSetSystemBootOptions, 2396 ipmi::Privilege::Operator, 2397 ipmiChassisSetSysBootOptions); 2398 2399 // <Get POH Counter> 2400 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2401 ipmi::chassis::cmdGetPohCounter, 2402 ipmi::Privilege::User, ipmiGetPOHCounter); 2403 2404 // <Set Power Restore Policy> 2405 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 2406 ipmi::chassis::cmdSetPowerRestorePolicy, 2407 ipmi::Privilege::Operator, 2408 ipmiChassisSetPowerRestorePolicy); 2409 } 2410