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