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