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