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