1 #include "config.h" 2 3 #include "chassishandler.hpp" 4 5 #include <arpa/inet.h> 6 #include <endian.h> 7 #include <limits.h> 8 #include <mapper.h> 9 #include <netinet/in.h> 10 11 #include <array> 12 #include <chrono> 13 #include <cstring> 14 #include <filesystem> 15 #include <fstream> 16 #include <future> 17 #include <ipmid/api.hpp> 18 #include <ipmid/types.hpp> 19 #include <ipmid/utils.hpp> 20 #include <map> 21 #include <phosphor-logging/elog-errors.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/bus.hpp> 24 #include <sdbusplus/message/types.hpp> 25 #include <sdbusplus/server/object.hpp> 26 #include <sdbusplus/timer.hpp> 27 #include <settings.hpp> 28 #include <sstream> 29 #include <string> 30 #include <xyz/openbmc_project/Common/error.hpp> 31 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> 32 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> 33 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> 34 #include <xyz/openbmc_project/State/Host/server.hpp> 35 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> 36 37 // Defines 38 #define SET_PARM_VERSION 0x01 39 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 40 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80 41 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 42 43 std::unique_ptr<phosphor::Timer> identifyTimer 44 __attribute__((init_priority(101))); 45 46 static ChassisIDState chassisIDState = ChassisIDState::reserved; 47 48 constexpr size_t SIZE_MAC = 18; 49 constexpr size_t SIZE_BOOT_OPTION = (uint8_t) 50 BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot 51 // option parametrs 52 constexpr size_t SIZE_PREFIX = 7; 53 constexpr size_t MAX_PREFIX_VALUE = 32; 54 constexpr size_t SIZE_COOKIE = 4; 55 constexpr size_t SIZE_VERSION = 2; 56 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15; 57 58 // PetiBoot-Specific 59 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62, 60 0x21, 0x00, 0x01, 0x06}; 61 62 static constexpr size_t COOKIE_OFFSET = 1; 63 static constexpr size_t VERSION_OFFSET = 5; 64 static constexpr size_t ADDR_SIZE_OFFSET = 8; 65 static constexpr size_t MAC_OFFSET = 9; 66 static constexpr size_t ADDRTYPE_OFFSET = 16; 67 static constexpr size_t IPADDR_OFFSET = 17; 68 69 static constexpr size_t encIdentifyObjectsSize = 1; 70 static constexpr size_t chassisIdentifyReqLength = 2; 71 static constexpr size_t identifyIntervalPos = 0; 72 static constexpr size_t forceIdentifyPos = 1; 73 74 void register_netfn_chassis_functions() __attribute__((constructor)); 75 76 // Host settings in dbus 77 // Service name should be referenced by connection name got via object mapper 78 const char* settings_object_name = "/org/openbmc/settings/host0"; 79 const char* settings_intf_name = "org.freedesktop.DBus.Properties"; 80 const char* identify_led_object_name = 81 "/xyz/openbmc_project/led/groups/enclosure_identify"; 82 83 constexpr auto SETTINGS_ROOT = "/"; 84 constexpr auto SETTINGS_MATCH = "host0"; 85 86 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; 87 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; 88 89 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state"; 90 static constexpr auto chassisPOHStateIntf = 91 "xyz.openbmc_project.State.PowerOnHours"; 92 static constexpr auto pOHCounterProperty = "POHCounter"; 93 static constexpr auto match = "chassis0"; 94 const static constexpr char chassisCapIntf[] = 95 "xyz.openbmc_project.Control.ChassisCapabilities"; 96 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags"; 97 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress"; 98 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress"; 99 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress"; 100 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; 101 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; 102 static constexpr uint8_t chassisCapFlagMask = 0x0f; 103 static constexpr uint8_t chassisCapAddrMask = 0xfe; 104 static constexpr const char* powerButtonIntf = 105 "xyz.openbmc_project.Chassis.Buttons.Power"; 106 static constexpr const char* powerButtonPath = 107 "/xyz/openbmc_project/Chassis/Buttons/Power0"; 108 static constexpr const char* resetButtonIntf = 109 "xyz.openbmc_project.Chassis.Buttons.Reset"; 110 static constexpr const char* resetButtonPath = 111 "/xyz/openbmc_project/Chassis/Buttons/Reset0"; 112 113 typedef struct 114 { 115 uint8_t cap_flags; 116 uint8_t fru_info_dev_addr; 117 uint8_t sdr_dev_addr; 118 uint8_t sel_dev_addr; 119 uint8_t system_management_dev_addr; 120 uint8_t bridge_dev_addr; 121 } __attribute__((packed)) ipmi_chassis_cap_t; 122 123 typedef struct 124 { 125 uint8_t cur_power_state; 126 uint8_t last_power_event; 127 uint8_t misc_power_state; 128 uint8_t front_panel_button_cap_status; 129 } __attribute__((packed)) ipmi_get_chassis_status_t; 130 131 // Phosphor Host State manager 132 namespace State = sdbusplus::xyz::openbmc_project::State::server; 133 134 namespace fs = std::filesystem; 135 136 using namespace phosphor::logging; 137 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 138 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 139 140 namespace chassis 141 { 142 namespace internal 143 { 144 145 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; 146 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source"; 147 constexpr auto powerRestoreIntf = 148 "xyz.openbmc_project.Control.Power.RestorePolicy"; 149 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 150 151 namespace cache 152 { 153 154 settings::Objects objects(dbus, 155 {bootModeIntf, bootSourceIntf, powerRestoreIntf}); 156 157 } // namespace cache 158 } // namespace internal 159 } // namespace chassis 160 161 namespace poh 162 { 163 164 constexpr auto minutesPerCount = 60; 165 166 } // namespace poh 167 168 struct get_sys_boot_options_t 169 { 170 uint8_t parameter; 171 uint8_t set; 172 uint8_t block; 173 } __attribute__((packed)); 174 175 struct get_sys_boot_options_response_t 176 { 177 uint8_t version; 178 uint8_t parm; 179 uint8_t data[SIZE_BOOT_OPTION]; 180 } __attribute__((packed)); 181 182 struct set_sys_boot_options_t 183 { 184 uint8_t parameter; 185 uint8_t data[SIZE_BOOT_OPTION]; 186 } __attribute__((packed)); 187 188 int getHostNetworkData(get_sys_boot_options_response_t* respptr) 189 { 190 ipmi::PropertyMap properties; 191 int rc = 0; 192 uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE; 193 194 try 195 { 196 // TODO There may be cases where an interface is implemented by multiple 197 // objects,to handle such cases we are interested on that object 198 // which are on interested busname. 199 // Currenlty mapper doesn't give the readable busname(gives busid) 200 // so we can't match with bus name so giving some object specific info 201 // as SETTINGS_MATCH. 202 // Later SETTINGS_MATCH will be replaced with busname. 203 204 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 205 206 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 207 SETTINGS_ROOT, SETTINGS_MATCH); 208 209 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 210 SETTINGS_ROOT, SETTINGS_MATCH); 211 212 properties = ipmi::getAllDbusProperties( 213 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE); 214 auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second, 215 macObjectInfo.first, MAC_INTERFACE, 216 "MACAddress"); 217 218 auto ipAddress = std::get<std::string>(properties["Address"]); 219 220 auto gateway = std::get<std::string>(properties["Gateway"]); 221 222 auto prefix = std::get<uint8_t>(properties["PrefixLength"]); 223 224 uint8_t isStatic = 225 (std::get<std::string>(properties["Origin"]) == 226 "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 227 ? 1 228 : 0; 229 230 auto MACAddress = std::get<std::string>(variant); 231 232 // it is expected here that we should get the valid data 233 // but we may also get the default values. 234 // Validation of the data is done by settings. 235 // 236 // if mac address is default mac address then 237 // don't send blank override. 238 if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS)) 239 { 240 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 241 rc = -1; 242 return rc; 243 } 244 // if addr is static then ipaddress,gateway,prefix 245 // should not be default one,don't send blank override. 246 if (isStatic) 247 { 248 if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) || 249 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix)) 250 { 251 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 252 rc = -1; 253 return rc; 254 } 255 } 256 257 sscanf( 258 MACAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, 259 (respptr->data + MAC_OFFSET), (respptr->data + MAC_OFFSET + 1), 260 (respptr->data + MAC_OFFSET + 2), (respptr->data + MAC_OFFSET + 3), 261 (respptr->data + MAC_OFFSET + 4), (respptr->data + MAC_OFFSET + 5)); 262 263 respptr->data[MAC_OFFSET + 6] = 0x00; 264 265 std::memcpy(respptr->data + ADDRTYPE_OFFSET, &isStatic, 266 sizeof(isStatic)); 267 268 uint8_t addressFamily = (std::get<std::string>(properties["Type"]) == 269 "xyz.openbmc_project.Network.IP.Protocol.IPv4") 270 ? AF_INET 271 : AF_INET6; 272 273 addrSize = (addressFamily == AF_INET) 274 ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE 275 : ipmi::network::IPV6_ADDRESS_SIZE_BYTE; 276 277 // ipaddress and gateway would be in IPv4 format 278 inet_pton(addressFamily, ipAddress.c_str(), 279 (respptr->data + IPADDR_OFFSET)); 280 281 uint8_t prefixOffset = IPADDR_OFFSET + addrSize; 282 283 std::memcpy(respptr->data + prefixOffset, &prefix, sizeof(prefix)); 284 285 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); 286 287 inet_pton(addressFamily, gateway.c_str(), 288 (respptr->data + gatewayOffset)); 289 } 290 catch (InternalFailure& e) 291 { 292 commit<InternalFailure>(); 293 std::memset(respptr->data, 0, SIZE_BOOT_OPTION); 294 rc = -1; 295 return rc; 296 } 297 298 // PetiBoot-Specific 299 // If success then copy the first 9 bytes to the data 300 std::memcpy(respptr->data, net_conf_initial_bytes, 301 sizeof(net_conf_initial_bytes)); 302 303 std::memcpy(respptr->data + ADDR_SIZE_OFFSET, &addrSize, sizeof(addrSize)); 304 305 #ifdef _IPMI_DEBUG_ 306 std::printf("\n===Printing the IPMI Formatted Data========\n"); 307 308 for (uint8_t pos = 0; pos < index; pos++) 309 { 310 std::printf("%02x ", respptr->data[pos]); 311 } 312 #endif 313 314 return rc; 315 } 316 317 /** @brief convert IPv4 and IPv6 addresses from binary to text form. 318 * @param[in] family - IPv4/Ipv6 319 * @param[in] data - req data pointer. 320 * @param[in] offset - offset in the data. 321 * @param[in] addrSize - size of the data which needs to be read from offset. 322 * @returns address in text form. 323 */ 324 325 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset, 326 uint8_t addrSize) 327 { 328 char ipAddr[INET6_ADDRSTRLEN] = {}; 329 330 switch (family) 331 { 332 case AF_INET: 333 { 334 struct sockaddr_in addr4 335 { 336 }; 337 std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize); 338 339 inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN); 340 341 break; 342 } 343 case AF_INET6: 344 { 345 struct sockaddr_in6 addr6 346 { 347 }; 348 std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize); 349 350 inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN); 351 352 break; 353 } 354 default: 355 { 356 return {}; 357 } 358 } 359 360 return ipAddr; 361 } 362 363 int setHostNetworkData(set_sys_boot_options_t* reqptr) 364 { 365 using namespace std::string_literals; 366 std::string host_network_config; 367 char mac[]{"00:00:00:00:00:00"}; 368 std::string ipAddress, gateway; 369 char addrOrigin{0}; 370 uint8_t addrSize{0}; 371 std::string addressOrigin = 372 "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 373 std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; 374 uint8_t prefix{0}; 375 uint32_t zeroCookie = 0; 376 uint8_t family = AF_INET; 377 378 // cookie starts from second byte 379 // version starts from sixth byte 380 381 try 382 { 383 do 384 { 385 // cookie == 0x21 0x70 0x62 0x21 386 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), 387 (net_conf_initial_bytes + COOKIE_OFFSET), 388 SIZE_COOKIE) != 0) 389 { 390 // cookie == 0 391 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), &zeroCookie, 392 SIZE_COOKIE) == 0) 393 { 394 // need to zero out the network settings. 395 break; 396 } 397 398 log<level::ERR>("Invalid Cookie"); 399 elog<InternalFailure>(); 400 } 401 402 // vesion == 0x00 0x01 403 if (memcmp(&(reqptr->data[VERSION_OFFSET]), 404 (net_conf_initial_bytes + VERSION_OFFSET), 405 SIZE_VERSION) != 0) 406 { 407 408 log<level::ERR>("Invalid Version"); 409 elog<InternalFailure>(); 410 } 411 412 std::snprintf( 413 mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT, 414 reqptr->data[MAC_OFFSET], reqptr->data[MAC_OFFSET + 1], 415 reqptr->data[MAC_OFFSET + 2], reqptr->data[MAC_OFFSET + 3], 416 reqptr->data[MAC_OFFSET + 4], reqptr->data[MAC_OFFSET + 5]); 417 418 std::memcpy(&addrOrigin, &(reqptr->data[ADDRTYPE_OFFSET]), 419 sizeof(decltype(addrOrigin))); 420 421 if (addrOrigin) 422 { 423 addressOrigin = 424 "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 425 } 426 427 // Get the address size 428 std::memcpy(&addrSize, &reqptr->data[ADDR_SIZE_OFFSET], 429 sizeof(addrSize)); 430 431 uint8_t prefixOffset = IPADDR_OFFSET + addrSize; 432 433 std::memcpy(&prefix, &(reqptr->data[prefixOffset]), 434 sizeof(decltype(prefix))); 435 436 uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); 437 438 if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE) 439 { 440 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; 441 family = AF_INET6; 442 } 443 444 ipAddress = 445 getAddrStr(family, reqptr->data, IPADDR_OFFSET, addrSize); 446 447 gateway = getAddrStr(family, reqptr->data, gatewayOffset, addrSize); 448 449 } while (0); 450 451 // Cookie == 0 or it is a valid cookie 452 host_network_config += "ipaddress="s + ipAddress + ",prefix="s + 453 std::to_string(prefix) + ",gateway="s + gateway + 454 ",mac="s + mac + ",addressOrigin="s + 455 addressOrigin; 456 457 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 458 459 auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, 460 SETTINGS_ROOT, SETTINGS_MATCH); 461 auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, 462 SETTINGS_ROOT, SETTINGS_MATCH); 463 // set the dbus property 464 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 465 IP_INTERFACE, "Address", std::string(ipAddress)); 466 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 467 IP_INTERFACE, "PrefixLength", prefix); 468 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 469 IP_INTERFACE, "Origin", addressOrigin); 470 ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, 471 IP_INTERFACE, "Gateway", std::string(gateway)); 472 ipmi::setDbusProperty( 473 bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type", 474 std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4")); 475 ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first, 476 MAC_INTERFACE, "MACAddress", std::string(mac)); 477 478 log<level::DEBUG>( 479 "Network configuration changed", 480 entry("NETWORKCONFIG=%s", host_network_config.c_str())); 481 } 482 catch (InternalFailure& e) 483 { 484 commit<InternalFailure>(); 485 return -1; 486 } 487 488 return 0; 489 } 490 491 uint32_t getPOHCounter() 492 { 493 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 494 495 auto chassisStateObj = 496 ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match); 497 498 auto service = 499 ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first); 500 501 auto propValue = 502 ipmi::getDbusProperty(bus, service, chassisStateObj.first, 503 chassisPOHStateIntf, pOHCounterProperty); 504 505 return std::get<uint32_t>(propValue); 506 } 507 508 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 509 ipmi_request_t request, 510 ipmi_response_t response, 511 ipmi_data_len_t data_len, 512 ipmi_context_t context) 513 { 514 // Status code. 515 ipmi_ret_t rc = IPMI_CC_INVALID; 516 *data_len = 0; 517 return rc; 518 } 519 520 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 521 ipmi_request_t request, 522 ipmi_response_t response, 523 ipmi_data_len_t data_len, 524 ipmi_context_t context) 525 { 526 // sd_bus error 527 ipmi_ret_t rc = IPMI_CC_OK; 528 529 ipmi_chassis_cap_t chassis_cap{}; 530 531 if (*data_len != 0) 532 { 533 return IPMI_CC_REQ_DATA_LEN_INVALID; 534 } 535 536 *data_len = sizeof(ipmi_chassis_cap_t); 537 538 try 539 { 540 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 541 542 ipmi::DbusObjectInfo chassisCapObject = 543 ipmi::getDbusObject(bus, chassisCapIntf); 544 545 // capabilities flags 546 // [7..4] - reserved 547 // [3] – 1b = provides power interlock (IPM 1.5) 548 // [2] – 1b = provides Diagnostic Interrupt (FP NMI) 549 // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis 550 // has capabilities 551 // to lock out external power control and reset button or 552 // front panel interfaces and/or detect tampering with those 553 // interfaces). 554 // [0] -1b = Chassis provides intrusion (physical security) sensor. 555 // set to default value 0x0. 556 ipmi::Value variant = ipmi::getDbusProperty( 557 bus, chassisCapObject.second, chassisCapObject.first, 558 chassisCapIntf, chassisCapFlagsProp); 559 chassis_cap.cap_flags = std::get<uint8_t>(variant); 560 561 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 562 chassisCapObject.first, chassisCapIntf, 563 chassisFRUDevAddrProp); 564 // Chassis FRU info Device Address. 565 chassis_cap.fru_info_dev_addr = std::get<uint8_t>(variant); 566 567 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 568 chassisCapObject.first, chassisCapIntf, 569 chassisSDRDevAddrProp); 570 // Chassis SDR Device Address. 571 chassis_cap.sdr_dev_addr = std::get<uint8_t>(variant); 572 573 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 574 chassisCapObject.first, chassisCapIntf, 575 chassisSELDevAddrProp); 576 // Chassis SEL Device Address. 577 chassis_cap.sel_dev_addr = std::get<uint8_t>(variant); 578 579 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 580 chassisCapObject.first, chassisCapIntf, 581 chassisSMDevAddrProp); 582 // Chassis System Management Device Address. 583 chassis_cap.system_management_dev_addr = std::get<uint8_t>(variant); 584 585 variant = ipmi::getDbusProperty(bus, chassisCapObject.second, 586 chassisCapObject.first, chassisCapIntf, 587 chassisBridgeDevAddrProp); 588 // Chassis Bridge Device Address. 589 chassis_cap.bridge_dev_addr = std::get<uint8_t>(variant); 590 uint8_t* respP = reinterpret_cast<uint8_t*>(response); 591 uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap); 592 std::copy(chassisP, chassisP + *data_len, respP); 593 } 594 catch (std::exception& e) 595 { 596 log<level::ERR>(e.what()); 597 rc = IPMI_CC_UNSPECIFIED_ERROR; 598 *data_len = 0; 599 return rc; 600 } 601 602 return rc; 603 } 604 605 /** @brief implements set chassis capalibities command 606 * @param intrusion - chassis intrusion 607 * @param fpLockout - frontpannel lockout 608 * @param reserved1 - skip one bit 609 * @param fruDeviceAddr - chassis FRU info Device Address 610 * @param sdrDeviceAddr - chassis SDR device address 611 * @param selDeviceAddr - chassis SEL device address 612 * @param smDeviceAddr - chassis system management device address 613 * @param bridgeDeviceAddr - chassis bridge device address 614 * 615 * @returns IPMI completion code 616 */ 617 ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout, 618 uint6_t reserved1, 619 620 uint8_t fruDeviceAddr, 621 622 uint8_t sdrDeviceAddr, 623 624 uint8_t selDeviceAddr, 625 626 uint8_t smDeviceAddr, 627 628 uint8_t bridgeDeviceAddr) 629 { 630 631 // check input data 632 if (reserved1 != 0) 633 { 634 log<level::ERR>("Unsupported request parameter"); 635 return ipmi::responseInvalidFieldRequest(); 636 } 637 638 if ((fruDeviceAddr & ~chassisCapAddrMask) != 0) 639 { 640 log<level::ERR>("Unsupported request parameter(FRU Addr)", 641 entry("REQ=0x%x", fruDeviceAddr)); 642 return ipmi::responseInvalidFieldRequest(); 643 } 644 if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0) 645 { 646 log<level::ERR>("Unsupported request parameter(SDR Addr)", 647 entry("REQ=0x%x", sdrDeviceAddr)); 648 return ipmi::responseInvalidFieldRequest(); 649 } 650 651 if ((selDeviceAddr & ~chassisCapAddrMask) != 0) 652 { 653 log<level::ERR>("Unsupported request parameter(SEL Addr)", 654 entry("REQ=0x%x", selDeviceAddr)); 655 return ipmi::responseInvalidFieldRequest(); 656 } 657 658 if ((smDeviceAddr & ~chassisCapAddrMask) != 0) 659 { 660 log<level::ERR>("Unsupported request parameter(SM Addr)", 661 entry("REQ=0x%x", smDeviceAddr)); 662 return ipmi::responseInvalidFieldRequest(); 663 } 664 665 if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0) 666 { 667 log<level::ERR>("Unsupported request parameter(Bridge Addr)", 668 entry("REQ=0x%x", bridgeDeviceAddr)); 669 return ipmi::responseInvalidFieldRequest(); 670 } 671 672 uint8_t capFlags = (static_cast<uint8_t>(intrusion)) | 673 ((static_cast<uint8_t>(fpLockout)) << 1); 674 try 675 { 676 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 677 ipmi::DbusObjectInfo chassisCapObject = 678 ipmi::getDbusObject(bus, chassisCapIntf); 679 680 ipmi::setDbusProperty(bus, chassisCapObject.second, 681 chassisCapObject.first, chassisCapIntf, 682 chassisCapFlagsProp, capFlags); 683 684 ipmi::setDbusProperty(bus, chassisCapObject.second, 685 chassisCapObject.first, chassisCapIntf, 686 chassisFRUDevAddrProp, fruDeviceAddr); 687 688 ipmi::setDbusProperty(bus, chassisCapObject.second, 689 chassisCapObject.first, chassisCapIntf, 690 chassisSDRDevAddrProp, sdrDeviceAddr); 691 692 ipmi::setDbusProperty(bus, chassisCapObject.second, 693 chassisCapObject.first, chassisCapIntf, 694 chassisSELDevAddrProp, selDeviceAddr); 695 696 ipmi::setDbusProperty(bus, chassisCapObject.second, 697 chassisCapObject.first, chassisCapIntf, 698 chassisSMDevAddrProp, smDeviceAddr); 699 700 ipmi::setDbusProperty(bus, chassisCapObject.second, 701 chassisCapObject.first, chassisCapIntf, 702 chassisBridgeDevAddrProp, bridgeDeviceAddr); 703 } 704 catch (std::exception& e) 705 { 706 log<level::ERR>(e.what()); 707 return ipmi::responseUnspecifiedError(); 708 } 709 return ipmi::responseSuccess(); 710 } 711 712 //------------------------------------------ 713 // Calls into Host State Manager Dbus object 714 //------------------------------------------ 715 int initiate_state_transition(State::Host::Transition transition) 716 { 717 // OpenBMC Host State Manager dbus framework 718 constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0"; 719 constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host"; 720 constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; 721 constexpr auto PROPERTY = "RequestedHostTransition"; 722 723 // sd_bus error 724 int rc = 0; 725 char* busname = NULL; 726 727 // SD Bus error report mechanism. 728 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 729 730 // Gets a hook onto either a SYSTEM or SESSION bus 731 sd_bus* bus_type = ipmid_get_sd_bus_connection(); 732 rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname); 733 if (rc < 0) 734 { 735 log<level::ERR>( 736 "Failed to get bus name", 737 entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT)); 738 return rc; 739 } 740 741 // Convert to string equivalent of the passed in transition enum. 742 auto request = State::convertForMessage(transition); 743 744 rc = sd_bus_call_method(bus_type, // On the system bus 745 busname, // Service to contact 746 HOST_STATE_MANAGER_ROOT, // Object path 747 DBUS_PROPERTY_IFACE, // Interface name 748 "Set", // Method to be called 749 &bus_error, // object to return error 750 nullptr, // Response buffer if any 751 "ssv", // Takes 3 arguments 752 HOST_STATE_MANAGER_IFACE, PROPERTY, "s", 753 request.c_str()); 754 if (rc < 0) 755 { 756 log<level::ERR>("Failed to initiate transition", 757 entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str())); 758 } 759 else 760 { 761 log<level::INFO>("Transition request initiated successfully"); 762 } 763 764 sd_bus_error_free(&bus_error); 765 free(busname); 766 767 return rc; 768 } 769 770 namespace power_policy 771 { 772 773 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; 774 using IpmiValue = uint8_t; 775 using DbusValue = RestorePolicy::Policy; 776 777 const std::map<DbusValue, IpmiValue> dbusToIpmi = { 778 {RestorePolicy::Policy::AlwaysOff, 0x00}, 779 {RestorePolicy::Policy::Restore, 0x01}, 780 {RestorePolicy::Policy::AlwaysOn, 0x02}}; 781 782 static constexpr uint8_t noChange = 0x03; 783 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; 784 785 /* helper function for Get Chassis Status Command 786 */ 787 std::optional<uint2_t> getPowerRestorePolicy() 788 { 789 uint2_t restorePolicy = 0; 790 using namespace chassis::internal; 791 792 try 793 { 794 const auto& powerRestoreSetting = 795 cache::objects.map.at(powerRestoreIntf).front(); 796 ipmi::Value result = ipmi::getDbusProperty( 797 *getSdBus(), 798 cache::objects.service(powerRestoreSetting, powerRestoreIntf) 799 .c_str(), 800 powerRestoreSetting.c_str(), powerRestoreIntf, 801 "PowerRestorePolicy"); 802 auto powerRestore = RestorePolicy::convertPolicyFromString( 803 std::get<std::string>(result)); 804 restorePolicy = dbusToIpmi.at(powerRestore); 805 } 806 catch (const std::exception& e) 807 { 808 log<level::ERR>( 809 "Failed to fetch pgood property", entry("ERROR=%s", e.what()), 810 entry("PATH=%s", 811 cache::objects.map.at(powerRestoreIntf).front().c_str()), 812 entry("INTERFACE=%s", powerRestoreIntf)); 813 return std::nullopt; 814 } 815 return std::make_optional(restorePolicy); 816 } 817 818 /* 819 * getPowerStatus 820 * helper function for Get Chassis Status Command 821 * return - optional value for pgood (no value on error) 822 */ 823 std::optional<bool> getPowerStatus() 824 { 825 bool powerGood = false; 826 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 827 try 828 { 829 constexpr const char* chassisStatePath = 830 "/xyz/openbmc_project/state/chassis0"; 831 constexpr const char* chassisStateIntf = 832 "xyz.openbmc_project.State.Chassis"; 833 auto service = 834 ipmi::getService(*busp, chassisStateIntf, chassisStatePath); 835 836 ipmi::Value powerState = 837 ipmi::getDbusProperty(*busp, service, chassisStatePath, 838 chassisStateIntf, "CurrentPowerState"); 839 powerGood = std::get<std::string>(powerState) == 840 "xyz.openbmc_project.State.Chassis.PowerState.On"; 841 } 842 catch (const std::exception& e) 843 { 844 try 845 { 846 // FIXME: some legacy modules use the older path; try that next 847 constexpr const char* legacyPwrCtrlObj = 848 "/org/openbmc/control/power0"; 849 constexpr const char* legacyPwrCtrlIntf = 850 "org.openbmc.control.Power"; 851 auto service = 852 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj); 853 854 ipmi::Value variant = ipmi::getDbusProperty( 855 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood"); 856 powerGood = static_cast<bool>(std::get<int>(variant)); 857 } 858 catch (const std::exception& e) 859 { 860 log<level::ERR>("Failed to fetch pgood property", 861 entry("ERROR=%s", e.what())); 862 return std::nullopt; 863 } 864 } 865 return std::make_optional(powerGood); 866 } 867 868 /* 869 * getACFailStatus 870 * helper function for Get Chassis Status Command 871 * return - bool value for ACFail (false on error) 872 */ 873 bool getACFailStatus() 874 { 875 constexpr const char* powerControlObj = 876 "/xyz/openbmc_project/Chassis/Control/Power0"; 877 constexpr const char* powerControlIntf = 878 "xyz.openbmc_project.Chassis.Control.Power"; 879 bool acFail = false; 880 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 881 try 882 { 883 auto service = 884 ipmi::getService(*bus, powerControlIntf, powerControlObj); 885 886 ipmi::Value variant = ipmi::getDbusProperty( 887 *bus, service, powerControlObj, powerControlIntf, "PFail"); 888 acFail = std::get<bool>(variant); 889 } 890 catch (const std::exception& e) 891 { 892 log<level::ERR>("Failed to fetch PFail property", 893 entry("ERROR=%s", e.what()), 894 entry("PATH=%s", powerControlObj), 895 entry("INTERFACE=%s", powerControlIntf)); 896 } 897 return acFail; 898 } 899 } // namespace power_policy 900 901 static std::optional<bool> getButtonEnabled(const std::string& buttonPath, 902 const std::string& buttonIntf) 903 { 904 std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); 905 bool buttonDisabled = false; 906 try 907 { 908 auto service = ipmi::getService(*busp, buttonIntf, buttonPath); 909 ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath, 910 buttonIntf, "Enabled"); 911 buttonDisabled = !std::get<bool>(enabled); 912 } 913 catch (sdbusplus::exception::SdBusError& e) 914 { 915 log<level::ERR>("Fail to get button Enabled property", 916 entry("PATH=%s", buttonPath.c_str()), 917 entry("ERROR=%s", e.what())); 918 return std::nullopt; 919 } 920 return std::make_optional(buttonDisabled); 921 } 922 923 //---------------------------------------------------------------------- 924 // Get Chassis Status commands 925 //---------------------------------------------------------------------- 926 ipmi::RspType<bool, // Power is on 927 bool, // Power overload 928 bool, // Interlock 929 bool, // power fault 930 bool, // power control fault 931 uint2_t, // power restore policy 932 bool, // reserved 933 934 bool, // AC failed 935 bool, // last power down caused by a Power overload 936 bool, // last power down caused by a power interlock 937 bool, // last power down caused by power fault 938 bool, // last ‘Power is on’ state was entered via IPMI command 939 uint3_t, // reserved 940 941 bool, // Chassis intrusion active 942 bool, // Front Panel Lockout active 943 bool, // Drive Fault 944 bool, // Cooling/fan fault detected 945 uint2_t, // Chassis Identify State 946 bool, // Chassis Identify command and state info supported 947 bool, // reserved 948 949 bool, // Power off button disabled 950 bool, // Reset button disabled 951 bool, // Diagnostic Interrupt button disabled 952 bool, // Standby (sleep) button disabled 953 bool, // Power off button disable allowed 954 bool, // Reset button disable allowed 955 bool, // Diagnostic Interrupt button disable allowed 956 bool // Standby (sleep) button disable allowed 957 > 958 ipmiGetChassisStatus() 959 { 960 using namespace chassis::internal; 961 std::optional<uint2_t> restorePolicy = 962 power_policy::getPowerRestorePolicy(); 963 std::optional<bool> powerGood = power_policy::getPowerStatus(); 964 if (!restorePolicy || !powerGood) 965 { 966 return ipmi::responseUnspecifiedError(); 967 } 968 969 // Front Panel Button Capabilities and disable/enable status(Optional) 970 std::optional<bool> powerButtonReading = 971 getButtonEnabled(powerButtonPath, powerButtonIntf); 972 // allow disable if the interface is present 973 bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading); 974 // default return the button is enabled (not disabled) 975 bool powerButtonDisabled = false; 976 if (powerButtonDisableAllow) 977 { 978 // return the real value of the button status, if present 979 powerButtonDisabled = *powerButtonReading; 980 } 981 982 std::optional<bool> resetButtonReading = 983 getButtonEnabled(resetButtonPath, resetButtonIntf); 984 // allow disable if the interface is present 985 bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading); 986 // default return the button is enabled (not disabled) 987 bool resetButtonDisabled = false; 988 if (resetButtonDisableAllow) 989 { 990 // return the real value of the button status, if present 991 resetButtonDisabled = *resetButtonReading; 992 } 993 994 bool powerDownAcFailed = power_policy::getACFailStatus(); 995 996 // This response has a lot of hard-coded, unsupported fields 997 // They are set to false or 0 998 constexpr bool powerOverload = false; 999 constexpr bool chassisInterlock = false; 1000 constexpr bool powerFault = false; 1001 constexpr bool powerControlFault = false; 1002 constexpr bool powerDownOverload = false; 1003 constexpr bool powerDownInterlock = false; 1004 constexpr bool powerDownPowerFault = false; 1005 constexpr bool powerStatusIPMI = false; 1006 constexpr bool chassisIntrusionActive = false; 1007 constexpr bool frontPanelLockoutActive = false; 1008 constexpr bool driveFault = false; 1009 constexpr bool coolingFanFault = false; 1010 // chassisIdentifySupport set because this command is implemented 1011 constexpr bool chassisIdentifySupport = true; 1012 uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState); 1013 constexpr bool diagButtonDisabled = false; 1014 constexpr bool sleepButtonDisabled = false; 1015 constexpr bool diagButtonDisableAllow = false; 1016 constexpr bool sleepButtonDisableAllow = false; 1017 1018 return ipmi::responseSuccess( 1019 *powerGood, powerOverload, chassisInterlock, powerFault, 1020 powerControlFault, *restorePolicy, 1021 false, // reserved 1022 1023 powerDownAcFailed, powerDownOverload, powerDownInterlock, 1024 powerDownPowerFault, powerStatusIPMI, 1025 uint3_t(0), // reserved 1026 1027 chassisIntrusionActive, frontPanelLockoutActive, driveFault, 1028 coolingFanFault, chassisIdentifyState, chassisIdentifySupport, 1029 false, // reserved 1030 1031 powerButtonDisabled, resetButtonDisabled, diagButtonDisabled, 1032 sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow, 1033 diagButtonDisableAllow, sleepButtonDisableAllow); 1034 } 1035 1036 //------------------------------------------------------------- 1037 // Send a command to SoftPowerOff application to stop any timer 1038 //------------------------------------------------------------- 1039 int stop_soft_off_timer() 1040 { 1041 constexpr auto iface = "org.freedesktop.DBus.Properties"; 1042 constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." 1043 "SoftPowerOff"; 1044 1045 constexpr auto property = "ResponseReceived"; 1046 constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." 1047 "SoftPowerOff.HostResponse.HostShutdown"; 1048 1049 // Get the system bus where most system services are provided. 1050 auto bus = ipmid_get_sd_bus_connection(); 1051 1052 // Get the service name 1053 // TODO openbmc/openbmc#1661 - Mapper refactor 1054 // 1055 // See openbmc/openbmc#1743 for some details but high level summary is that 1056 // for now the code will directly call the soft off interface due to a 1057 // race condition with mapper usage 1058 // 1059 // char *busname = nullptr; 1060 // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); 1061 // if (r < 0) 1062 //{ 1063 // fprintf(stderr, "Failed to get %s bus name: %s\n", 1064 // SOFTOFF_OBJPATH, -r); 1065 // return r; 1066 //} 1067 1068 // No error object or reply expected. 1069 int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, 1070 "Set", nullptr, nullptr, "ssv", soft_off_iface, 1071 property, "s", value); 1072 if (rc < 0) 1073 { 1074 log<level::ERR>("Failed to set property in SoftPowerOff object", 1075 entry("ERRNO=0x%X", -rc)); 1076 } 1077 1078 // TODO openbmc/openbmc#1661 - Mapper refactor 1079 // free(busname); 1080 return rc; 1081 } 1082 1083 //---------------------------------------------------------------------- 1084 // Create file to indicate there is no need for softoff notification to host 1085 //---------------------------------------------------------------------- 1086 void indicate_no_softoff_needed() 1087 { 1088 fs::path path{HOST_INBAND_REQUEST_DIR}; 1089 if (!fs::is_directory(path)) 1090 { 1091 fs::create_directory(path); 1092 } 1093 1094 // Add the host instance (default 0 for now) to the file name 1095 std::string file{HOST_INBAND_REQUEST_FILE}; 1096 auto size = std::snprintf(nullptr, 0, file.c_str(), 0); 1097 size++; // null 1098 std::unique_ptr<char[]> buf(new char[size]); 1099 std::snprintf(buf.get(), size, file.c_str(), 0); 1100 1101 // Append file name to directory and create it 1102 path /= buf.get(); 1103 std::ofstream(path.c_str()); 1104 } 1105 1106 /** @brief Implementation of chassis control command 1107 * 1108 * @param - chassisControl command byte 1109 * 1110 * @return Success or InvalidFieldRequest. 1111 */ 1112 ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl) 1113 { 1114 int rc = 0; 1115 switch (chassisControl) 1116 { 1117 case CMD_POWER_ON: 1118 rc = initiate_state_transition(State::Host::Transition::On); 1119 break; 1120 case CMD_POWER_OFF: 1121 // This path would be hit in 2 conditions. 1122 // 1: When user asks for power off using ipmi chassis command 0x04 1123 // 2: Host asking for power off post shutting down. 1124 1125 // If it's a host requested power off, then need to nudge Softoff 1126 // application that it needs to stop the watchdog timer if running. 1127 // If it is a user requested power off, then this is not really 1128 // needed. But then we need to differentiate between user and host 1129 // calling this same command 1130 1131 // For now, we are going ahead with trying to nudge the soft off and 1132 // interpret the failure to do so as a non softoff case 1133 rc = stop_soft_off_timer(); 1134 1135 // Only request the Off transition if the soft power off 1136 // application is not running 1137 if (rc < 0) 1138 { 1139 // First create a file to indicate to the soft off application 1140 // that it should not run. Not doing this will result in State 1141 // manager doing a default soft power off when asked for power 1142 // off. 1143 indicate_no_softoff_needed(); 1144 1145 // Now request the shutdown 1146 rc = initiate_state_transition(State::Host::Transition::Off); 1147 } 1148 else 1149 { 1150 log<level::INFO>("Soft off is running, so let shutdown target " 1151 "stop the host"); 1152 } 1153 break; 1154 1155 case CMD_HARD_RESET: 1156 case CMD_POWER_CYCLE: 1157 // SPEC has a section that says certain implementations can trigger 1158 // PowerOn if power is Off when a command to power cycle is 1159 // requested 1160 1161 // First create a file to indicate to the soft off application 1162 // that it should not run since this is a direct user initiated 1163 // power reboot request (i.e. a reboot request that is not 1164 // originating via a soft power off SMS request) 1165 indicate_no_softoff_needed(); 1166 1167 rc = initiate_state_transition(State::Host::Transition::Reboot); 1168 break; 1169 1170 case CMD_SOFT_OFF_VIA_OVER_TEMP: 1171 // Request Host State Manager to do a soft power off 1172 rc = initiate_state_transition(State::Host::Transition::Off); 1173 break; 1174 1175 default: 1176 { 1177 log<level::ERR>("Invalid Chassis Control command", 1178 entry("CMD=0x%X", chassisControl)); 1179 return ipmi::responseInvalidFieldRequest(); 1180 } 1181 } 1182 1183 return ((rc < 0) ? ipmi::responseUnspecifiedError() 1184 : ipmi::responseSuccess()); 1185 } 1186 1187 /** @brief Return D-Bus connection string to enclosure identify LED object 1188 * 1189 * @param[in, out] connection - connection to D-Bus object 1190 * @return a IPMI return code 1191 */ 1192 std::string getEnclosureIdentifyConnection() 1193 { 1194 // lookup enclosure_identify group owner(s) in mapper 1195 auto mapperCall = chassis::internal::dbus.new_method_call( 1196 ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF, 1197 "GetObject"); 1198 1199 mapperCall.append(identify_led_object_name); 1200 static const std::vector<std::string> interfaces = { 1201 "xyz.openbmc_project.Led.Group"}; 1202 mapperCall.append(interfaces); 1203 auto mapperReply = chassis::internal::dbus.call(mapperCall); 1204 if (mapperReply.is_method_error()) 1205 { 1206 log<level::ERR>("Chassis Identify: Error communicating to mapper."); 1207 elog<InternalFailure>(); 1208 } 1209 std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp; 1210 mapperReply.read(mapperResp); 1211 1212 if (mapperResp.size() != encIdentifyObjectsSize) 1213 { 1214 log<level::ERR>( 1215 "Invalid number of enclosure identify objects.", 1216 entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size())); 1217 elog<InternalFailure>(); 1218 } 1219 auto pair = mapperResp[encIdentifyObjectsSize - 1]; 1220 return pair.first; 1221 } 1222 1223 /** @brief Turn On/Off enclosure identify LED 1224 * 1225 * @param[in] flag - true to turn on LED, false to turn off 1226 * @return a IPMI return code 1227 */ 1228 void enclosureIdentifyLed(bool flag) 1229 { 1230 using namespace chassis::internal; 1231 std::string connection = std::move(getEnclosureIdentifyConnection()); 1232 auto msg = std::string("enclosureIdentifyLed(") + 1233 boost::lexical_cast<std::string>(flag) + ")"; 1234 log<level::DEBUG>(msg.c_str()); 1235 auto led = 1236 dbus.new_method_call(connection.c_str(), identify_led_object_name, 1237 "org.freedesktop.DBus.Properties", "Set"); 1238 led.append("xyz.openbmc_project.Led.Group", "Asserted", 1239 std::variant<bool>(flag)); 1240 auto ledReply = dbus.call(led); 1241 if (ledReply.is_method_error()) 1242 { 1243 log<level::ERR>("Chassis Identify: Error Setting State On/Off\n", 1244 entry("LED_STATE=%d", flag)); 1245 elog<InternalFailure>(); 1246 } 1247 } 1248 1249 /** @brief Callback method to turn off LED 1250 */ 1251 void enclosureIdentifyLedOff() 1252 { 1253 try 1254 { 1255 chassisIDState = ChassisIDState::off; 1256 enclosureIdentifyLed(false); 1257 } 1258 catch (const InternalFailure& e) 1259 { 1260 report<InternalFailure>(); 1261 } 1262 } 1263 1264 /** @brief Create timer to turn on and off the enclosure LED 1265 */ 1266 void createIdentifyTimer() 1267 { 1268 if (!identifyTimer) 1269 { 1270 identifyTimer = 1271 std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff); 1272 } 1273 } 1274 1275 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, 1276 std::optional<uint8_t> force) 1277 { 1278 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); 1279 bool forceIdentify = force.value_or(0) & 0x01; 1280 1281 if (identifyInterval || forceIdentify) 1282 { 1283 // stop the timer if already started; 1284 // for force identify we should not turn off LED 1285 identifyTimer->stop(); 1286 try 1287 { 1288 chassisIDState = ChassisIDState::temporaryOn; 1289 enclosureIdentifyLed(true); 1290 } 1291 catch (const InternalFailure& e) 1292 { 1293 report<InternalFailure>(); 1294 return ipmi::responseResponseError(); 1295 } 1296 1297 if (forceIdentify) 1298 { 1299 chassisIDState = ChassisIDState::indefiniteOn; 1300 return ipmi::responseSuccess(); 1301 } 1302 // start the timer 1303 auto time = std::chrono::duration_cast<std::chrono::microseconds>( 1304 std::chrono::seconds(identifyInterval)); 1305 identifyTimer->start(time); 1306 } 1307 else if (!identifyInterval) 1308 { 1309 identifyTimer->stop(); 1310 enclosureIdentifyLedOff(); 1311 } 1312 return ipmi::responseSuccess(); 1313 } 1314 1315 namespace boot_options 1316 { 1317 1318 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 1319 using IpmiValue = uint8_t; 1320 constexpr auto ipmiDefault = 0; 1321 1322 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 1323 {0x01, Source::Sources::Network}, 1324 {0x02, Source::Sources::Disk}, 1325 {0x05, Source::Sources::ExternalMedia}, 1326 {0x0f, Source::Sources::RemovableMedia}, 1327 {ipmiDefault, Source::Sources::Default}}; 1328 1329 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 1330 {0x03, Mode::Modes::Safe}, 1331 {0x06, Mode::Modes::Setup}, 1332 {ipmiDefault, Mode::Modes::Regular}}; 1333 1334 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 1335 {Source::Sources::Network, 0x01}, 1336 {Source::Sources::Disk, 0x02}, 1337 {Source::Sources::ExternalMedia, 0x05}, 1338 {Source::Sources::RemovableMedia, 0x0f}, 1339 {Source::Sources::Default, ipmiDefault}}; 1340 1341 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 1342 {Mode::Modes::Safe, 0x03}, 1343 {Mode::Modes::Setup, 0x06}, 1344 {Mode::Modes::Regular, ipmiDefault}}; 1345 1346 } // namespace boot_options 1347 1348 /** @brief Set the property value for boot source 1349 * @param[in] source - boot source value 1350 * @return On failure return IPMI error. 1351 */ 1352 static ipmi_ret_t setBootSource(const Source::Sources& source) 1353 { 1354 using namespace chassis::internal; 1355 using namespace chassis::internal::cache; 1356 std::variant<std::string> property = convertForMessage(source); 1357 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1358 const auto& bootSourceSetting = std::get<settings::Path>(bootSetting); 1359 auto method = dbus.new_method_call( 1360 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1361 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set"); 1362 method.append(bootSourceIntf, "BootSource", property); 1363 auto reply = dbus.call(method); 1364 if (reply.is_method_error()) 1365 { 1366 log<level::ERR>("Error in BootSource Set"); 1367 report<InternalFailure>(); 1368 return IPMI_CC_UNSPECIFIED_ERROR; 1369 } 1370 return IPMI_CC_OK; 1371 } 1372 1373 /** @brief Set the property value for boot mode 1374 * @param[in] mode - boot mode value 1375 * @return On failure return IPMI error. 1376 */ 1377 static ipmi_ret_t setBootMode(const Mode::Modes& mode) 1378 { 1379 using namespace chassis::internal; 1380 using namespace chassis::internal::cache; 1381 std::variant<std::string> property = convertForMessage(mode); 1382 auto bootSetting = settings::boot::setting(objects, bootModeIntf); 1383 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1384 auto method = dbus.new_method_call( 1385 objects.service(bootModeSetting, bootModeIntf).c_str(), 1386 bootModeSetting.c_str(), ipmi::PROP_INTF, "Set"); 1387 method.append(bootModeIntf, "BootMode", property); 1388 auto reply = dbus.call(method); 1389 if (reply.is_method_error()) 1390 { 1391 log<level::ERR>("Error in BootMode Set"); 1392 report<InternalFailure>(); 1393 return IPMI_CC_UNSPECIFIED_ERROR; 1394 } 1395 return IPMI_CC_OK; 1396 } 1397 1398 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1399 ipmi_request_t request, 1400 ipmi_response_t response, 1401 ipmi_data_len_t data_len, 1402 ipmi_context_t context) 1403 { 1404 using namespace boot_options; 1405 ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED; 1406 char* p = NULL; 1407 get_sys_boot_options_response_t* resp = 1408 (get_sys_boot_options_response_t*)response; 1409 get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request; 1410 IpmiValue bootOption = ipmiDefault; 1411 1412 std::memset(resp, 0, sizeof(*resp)); 1413 resp->version = SET_PARM_VERSION; 1414 resp->parm = 5; 1415 resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME; 1416 1417 /* 1418 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1419 * This is the only parameter used by petitboot. 1420 */ 1421 if (reqptr->parameter == 1422 static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS)) 1423 { 1424 1425 *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS); 1426 using namespace chassis::internal; 1427 using namespace chassis::internal::cache; 1428 1429 try 1430 { 1431 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1432 const auto& bootSourceSetting = 1433 std::get<settings::Path>(bootSetting); 1434 auto oneTimeEnabled = 1435 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1436 auto method = dbus.new_method_call( 1437 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1438 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get"); 1439 method.append(bootSourceIntf, "BootSource"); 1440 auto reply = dbus.call(method); 1441 if (reply.is_method_error()) 1442 { 1443 log<level::ERR>("Error in BootSource Get"); 1444 report<InternalFailure>(); 1445 *data_len = 0; 1446 return IPMI_CC_UNSPECIFIED_ERROR; 1447 } 1448 std::variant<std::string> result; 1449 reply.read(result); 1450 auto bootSource = 1451 Source::convertSourcesFromString(std::get<std::string>(result)); 1452 1453 bootSetting = settings::boot::setting(objects, bootModeIntf); 1454 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1455 method = dbus.new_method_call( 1456 objects.service(bootModeSetting, bootModeIntf).c_str(), 1457 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get"); 1458 method.append(bootModeIntf, "BootMode"); 1459 reply = dbus.call(method); 1460 if (reply.is_method_error()) 1461 { 1462 log<level::ERR>("Error in BootMode Get"); 1463 report<InternalFailure>(); 1464 *data_len = 0; 1465 return IPMI_CC_UNSPECIFIED_ERROR; 1466 } 1467 reply.read(result); 1468 auto bootMode = 1469 Mode::convertModesFromString(std::get<std::string>(result)); 1470 1471 bootOption = sourceDbusToIpmi.at(bootSource); 1472 if ((Mode::Modes::Regular == bootMode) && 1473 (Source::Sources::Default == bootSource)) 1474 { 1475 bootOption = ipmiDefault; 1476 } 1477 else if (Source::Sources::Default == bootSource) 1478 { 1479 bootOption = modeDbusToIpmi.at(bootMode); 1480 } 1481 resp->data[1] = (bootOption << 2); 1482 1483 resp->data[0] = oneTimeEnabled 1484 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 1485 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT; 1486 1487 rc = IPMI_CC_OK; 1488 } 1489 catch (InternalFailure& e) 1490 { 1491 report<InternalFailure>(); 1492 *data_len = 0; 1493 return IPMI_CC_UNSPECIFIED_ERROR; 1494 } 1495 } 1496 else if (reqptr->parameter == 1497 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS)) 1498 { 1499 1500 *data_len = 1501 static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS); 1502 1503 resp->parm = 1504 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS); 1505 1506 int ret = getHostNetworkData(resp); 1507 1508 if (ret < 0) 1509 { 1510 1511 log<level::ERR>( 1512 "getHostNetworkData failed for get_sys_boot_options."); 1513 rc = IPMI_CC_UNSPECIFIED_ERROR; 1514 } 1515 else 1516 rc = IPMI_CC_OK; 1517 } 1518 1519 else 1520 { 1521 log<level::ERR>("Unsupported parameter", 1522 entry("PARAM=0x%x", reqptr->parameter)); 1523 } 1524 1525 if (p) 1526 free(p); 1527 1528 if (rc == IPMI_CC_OK) 1529 { 1530 *data_len += 2; 1531 } 1532 1533 return rc; 1534 } 1535 1536 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1537 ipmi_request_t request, 1538 ipmi_response_t response, 1539 ipmi_data_len_t data_len, 1540 ipmi_context_t context) 1541 { 1542 using namespace boot_options; 1543 ipmi_ret_t rc = IPMI_CC_OK; 1544 set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request; 1545 1546 std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n", 1547 reqptr->parameter); 1548 1549 // This IPMI command does not have any resposne data 1550 *data_len = 0; 1551 1552 /* 000101 1553 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1554 * This is the only parameter used by petitboot. 1555 */ 1556 1557 if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) 1558 { 1559 IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2); 1560 using namespace chassis::internal; 1561 using namespace chassis::internal::cache; 1562 auto oneTimeEnabled = false; 1563 constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable"; 1564 constexpr auto oneTimePath = 1565 "/xyz/openbmc_project/control/host0/boot/one_time"; 1566 1567 try 1568 { 1569 bool permanent = 1570 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) == 1571 SET_PARM_BOOT_FLAGS_PERMANENT; 1572 1573 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1574 1575 oneTimeEnabled = 1576 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1577 1578 /* 1579 * Check if the current boot setting is onetime or permanent, if the 1580 * request in the command is otherwise, then set the "Enabled" 1581 * property in one_time object path to 'True' to indicate onetime 1582 * and 'False' to indicate permanent. 1583 * 1584 * Once the onetime/permanent setting is applied, then the bootMode 1585 * and bootSource is updated for the corresponding object. 1586 */ 1587 if ((permanent && oneTimeEnabled) || 1588 (!permanent && !oneTimeEnabled)) 1589 { 1590 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath); 1591 1592 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf, 1593 "Enabled", !permanent); 1594 } 1595 1596 auto modeItr = modeIpmiToDbus.find(bootOption); 1597 auto sourceItr = sourceIpmiToDbus.find(bootOption); 1598 if (sourceIpmiToDbus.end() != sourceItr) 1599 { 1600 rc = setBootSource(sourceItr->second); 1601 if (rc != IPMI_CC_OK) 1602 { 1603 *data_len = 0; 1604 return rc; 1605 } 1606 // If a set boot device is mapping to a boot source, then reset 1607 // the boot mode D-Bus property to default. 1608 // This way the ipmid code can determine which property is not 1609 // at the default value 1610 if (sourceItr->second != Source::Sources::Default) 1611 { 1612 setBootMode(Mode::Modes::Regular); 1613 } 1614 } 1615 if (modeIpmiToDbus.end() != modeItr) 1616 { 1617 rc = setBootMode(modeItr->second); 1618 if (rc != IPMI_CC_OK) 1619 { 1620 *data_len = 0; 1621 return rc; 1622 } 1623 // If a set boot device is mapping to a boot mode, then reset 1624 // the boot source D-Bus property to default. 1625 // This way the ipmid code can determine which property is not 1626 // at the default value 1627 if (modeItr->second != Mode::Modes::Regular) 1628 { 1629 setBootSource(Source::Sources::Default); 1630 } 1631 } 1632 if ((modeIpmiToDbus.end() == modeItr) && 1633 (sourceIpmiToDbus.end() == sourceItr)) 1634 { 1635 // return error if boot option is not supported 1636 *data_len = 0; 1637 return IPMI_CC_INVALID_FIELD_REQUEST; 1638 } 1639 } 1640 catch (InternalFailure& e) 1641 { 1642 report<InternalFailure>(); 1643 *data_len = 0; 1644 return IPMI_CC_UNSPECIFIED_ERROR; 1645 } 1646 } 1647 else if (reqptr->parameter == 1648 (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) 1649 { 1650 1651 int ret = setHostNetworkData(reqptr); 1652 if (ret < 0) 1653 { 1654 log<level::ERR>( 1655 "setHostNetworkData failed for set_sys_boot_options"); 1656 rc = IPMI_CC_UNSPECIFIED_ERROR; 1657 } 1658 } 1659 else if (reqptr->parameter == 1660 static_cast<uint8_t>(BootOptionParameter::BOOT_INFO)) 1661 { 1662 // Handle parameter #4 and return command completed normally 1663 // (IPMI_CC_OK). There is no implementation in OpenBMC for this 1664 // parameter. This is added to support the ipmitool command `chassis 1665 // bootdev` which sends set on parameter #4, before setting the boot 1666 // flags. 1667 rc = IPMI_CC_OK; 1668 } 1669 else 1670 { 1671 log<level::ERR>("Unsupported parameter", 1672 entry("PARAM=0x%x", reqptr->parameter)); 1673 rc = IPMI_CC_PARM_NOT_SUPPORTED; 1674 } 1675 1676 return rc; 1677 } 1678 1679 /** @brief implements Get POH counter command 1680 * @parameter 1681 * - none 1682 * @returns IPMI completion code plus response data 1683 * - minPerCount - Minutes per count 1684 * - counterReading - counter reading 1685 */ 1686 ipmi::RspType<uint8_t, // Minutes per count 1687 uint32_t // Counter reading 1688 > 1689 ipmiGetPOHCounter() 1690 { 1691 // sd_bus error 1692 try 1693 { 1694 return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), 1695 getPOHCounter()); 1696 } 1697 catch (std::exception& e) 1698 { 1699 log<level::ERR>(e.what()); 1700 return ipmi::responseUnspecifiedError(); 1701 } 1702 } 1703 1704 ipmi::RspType<uint3_t, // policy support 1705 uint5_t // reserved 1706 > 1707 ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield, 1708 uint3_t policy, uint5_t reserved) 1709 { 1710 power_policy::DbusValue value = 1711 power_policy::RestorePolicy::Policy::AlwaysOff; 1712 1713 if (reserved || (policy > power_policy::noChange)) 1714 { 1715 phosphor::logging::log<level::ERR>( 1716 "Reserved request parameter", 1717 entry("REQ=0x%x", static_cast<int>(policy))); 1718 return ipmi::responseInvalidFieldRequest(); 1719 } 1720 1721 if (policy == power_policy::noChange) 1722 { 1723 // just return the supported policy 1724 return ipmi::responseSuccess(power_policy::allSupport, reserved); 1725 } 1726 1727 for (auto const& it : power_policy::dbusToIpmi) 1728 { 1729 if (it.second == policy) 1730 { 1731 value = it.first; 1732 break; 1733 } 1734 } 1735 1736 try 1737 { 1738 const settings::Path& powerRestoreSetting = 1739 chassis::internal::cache::objects.map 1740 .at(chassis::internal::powerRestoreIntf) 1741 .front(); 1742 std::variant<std::string> property = convertForMessage(value); 1743 1744 auto sdbusp = getSdBus(); 1745 boost::system::error_code ec; 1746 sdbusp->yield_method_call<void>( 1747 yield, ec, 1748 chassis::internal::cache::objects 1749 .service(powerRestoreSetting, 1750 chassis::internal::powerRestoreIntf) 1751 .c_str(), 1752 powerRestoreSetting, ipmi::PROP_INTF, "Set", 1753 chassis::internal::powerRestoreIntf, "PowerRestorePolicy", 1754 property); 1755 if (ec) 1756 { 1757 phosphor::logging::log<level::ERR>("Unspecified Error"); 1758 return ipmi::responseUnspecifiedError(); 1759 } 1760 } 1761 catch (InternalFailure& e) 1762 { 1763 report<InternalFailure>(); 1764 return ipmi::responseUnspecifiedError(); 1765 } 1766 1767 return ipmi::responseSuccess(power_policy::allSupport, reserved); 1768 } 1769 1770 void register_netfn_chassis_functions() 1771 { 1772 createIdentifyTimer(); 1773 1774 // <Wildcard Command> 1775 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL, 1776 ipmi_chassis_wildcard, PRIVILEGE_USER); 1777 1778 // Get Chassis Capabilities 1779 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, 1780 ipmi_get_chassis_cap, PRIVILEGE_USER); 1781 1782 // Set Chassis Capabilities 1783 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1784 ipmi::chassis::cmdSetChassisCapabilities, 1785 ipmi::Privilege::User, ipmiSetChassisCap); 1786 1787 // <Get System Boot Options> 1788 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL, 1789 ipmi_chassis_get_sys_boot_options, 1790 PRIVILEGE_OPERATOR); 1791 1792 // <Get Chassis Status> 1793 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1794 ipmi::chassis::cmdGetChassisStatus, 1795 ipmi::Privilege::User, ipmiGetChassisStatus); 1796 1797 // <Chassis Control> 1798 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1799 ipmi::chassis::cmdChassisControl, 1800 ipmi::Privilege::Operator, ipmiChassisControl); 1801 1802 // <Chassis Identify> 1803 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1804 ipmi::chassis::cmdChassisIdentify, 1805 ipmi::Privilege::Operator, ipmiChassisIdentify); 1806 1807 // <Set System Boot Options> 1808 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL, 1809 ipmi_chassis_set_sys_boot_options, 1810 PRIVILEGE_OPERATOR); 1811 // <Get POH Counter> 1812 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1813 ipmi::chassis::cmdGetPohCounter, 1814 ipmi::Privilege::User, ipmiGetPOHCounter); 1815 1816 // <Set Power Restore Policy> 1817 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1818 ipmi::chassis::cmdSetPowerRestorePolicy, 1819 ipmi::Privilege::Operator, 1820 ipmiChassisSetPowerRestorePolicy); 1821 } 1822