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