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