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