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