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