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