1 #include "config.h" 2 3 #include "chassishandler.hpp" 4 5 #include "settings.hpp" 6 7 #include <arpa/inet.h> 8 #include <endian.h> 9 #include <limits.h> 10 #include <mapper.h> 11 #include <netinet/in.h> 12 13 #include <array> 14 #include <chrono> 15 #include <cstring> 16 #include <filesystem> 17 #include <fstream> 18 #include <future> 19 #include <ipmid/api.hpp> 20 #include <ipmid/types.hpp> 21 #include <ipmid/utils.hpp> 22 #include <map> 23 #include <phosphor-logging/elog-errors.hpp> 24 #include <phosphor-logging/log.hpp> 25 #include <sdbusplus/bus.hpp> 26 #include <sdbusplus/message/types.hpp> 27 #include <sdbusplus/server/object.hpp> 28 #include <sdbusplus/timer.hpp> 29 #include <sstream> 30 #include <string> 31 #include <xyz/openbmc_project/Common/error.hpp> 32 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> 33 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> 34 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> 35 #include <xyz/openbmc_project/State/Host/server.hpp> 36 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> 37 38 // Defines 39 #define SET_PARM_VERSION 0x01 40 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 41 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80 42 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 43 44 std::unique_ptr<phosphor::Timer> identifyTimer 45 __attribute__((init_priority(101))); 46 47 constexpr size_t SIZE_MAC = 18; 48 constexpr size_t SIZE_BOOT_OPTION = (uint8_t) 49 BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot 50 // option parametrs 51 constexpr size_t SIZE_PREFIX = 7; 52 constexpr size_t MAX_PREFIX_VALUE = 32; 53 constexpr size_t SIZE_COOKIE = 4; 54 constexpr size_t SIZE_VERSION = 2; 55 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15; 56 57 // PetiBoot-Specific 58 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62, 59 0x21, 0x00, 0x01, 0x06}; 60 61 static constexpr size_t COOKIE_OFFSET = 1; 62 static constexpr size_t VERSION_OFFSET = 5; 63 static constexpr size_t ADDR_SIZE_OFFSET = 8; 64 static constexpr size_t MAC_OFFSET = 9; 65 static constexpr size_t ADDRTYPE_OFFSET = 16; 66 static constexpr size_t IPADDR_OFFSET = 17; 67 68 static constexpr size_t encIdentifyObjectsSize = 1; 69 static constexpr size_t chassisIdentifyReqLength = 2; 70 static constexpr size_t identifyIntervalPos = 0; 71 static constexpr size_t forceIdentifyPos = 1; 72 73 void register_netfn_chassis_functions() __attribute__((constructor)); 74 75 // Host settings in dbus 76 // Service name should be referenced by connection name got via object mapper 77 const char* settings_object_name = "/org/openbmc/settings/host0"; 78 const char* settings_intf_name = "org.freedesktop.DBus.Properties"; 79 const char* identify_led_object_name = 80 "/xyz/openbmc_project/led/groups/enclosure_identify"; 81 82 constexpr auto SETTINGS_ROOT = "/"; 83 constexpr auto SETTINGS_MATCH = "host0"; 84 85 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; 86 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; 87 88 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state"; 89 static constexpr auto chassisPOHStateIntf = 90 "xyz.openbmc_project.State.PowerOnHours"; 91 static constexpr auto pOHCounterProperty = "POHCounter"; 92 static constexpr auto match = "chassis0"; 93 const static constexpr char chassisCapIntf[] = 94 "xyz.openbmc_project.Control.ChassisCapabilities"; 95 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags"; 96 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress"; 97 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress"; 98 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress"; 99 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; 100 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; 101 static constexpr uint8_t chassisCapFlagMask = 0x0f; 102 static constexpr uint8_t chassisCapAddrMask = 0xfe; 103 104 typedef struct 105 { 106 uint8_t cap_flags; 107 uint8_t fru_info_dev_addr; 108 uint8_t sdr_dev_addr; 109 uint8_t sel_dev_addr; 110 uint8_t system_management_dev_addr; 111 uint8_t bridge_dev_addr; 112 } __attribute__((packed)) ipmi_chassis_cap_t; 113 114 typedef struct 115 { 116 uint8_t cur_power_state; 117 uint8_t last_power_event; 118 uint8_t misc_power_state; 119 uint8_t front_panel_button_cap_status; 120 } __attribute__((packed)) ipmi_get_chassis_status_t; 121 122 /** 123 * @struct Get POH counter command response data 124 */ 125 struct GetPOHCountResponse 126 { 127 uint8_t minPerCount; ///< Minutes per count 128 uint8_t counterReading[4]; ///< Counter reading 129 } __attribute__((packed)); 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 ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 606 ipmi_request_t request, 607 ipmi_response_t response, 608 ipmi_data_len_t data_len, 609 ipmi_context_t context) 610 { 611 ipmi_ret_t rc = IPMI_CC_OK; 612 613 if (*data_len != sizeof(ipmi_chassis_cap_t)) 614 { 615 log<level::ERR>("Unsupported request length", 616 entry("LEN=0x%x", *data_len)); 617 *data_len = 0; 618 return IPMI_CC_REQ_DATA_LEN_INVALID; 619 } 620 621 ipmi_chassis_cap_t* chassisCap = static_cast<ipmi_chassis_cap_t*>(request); 622 623 *data_len = 0; 624 625 // check input data 626 if (0 != (chassisCap->cap_flags & ~chassisCapFlagMask)) 627 { 628 log<level::ERR>("Unsupported request parameter(CAP Flags)", 629 entry("REQ=0x%x", chassisCap->cap_flags)); 630 return IPMI_CC_INVALID_FIELD_REQUEST; 631 } 632 633 if (0 != (chassisCap->fru_info_dev_addr & ~chassisCapAddrMask)) 634 { 635 log<level::ERR>("Unsupported request parameter(FRU Addr)", 636 entry("REQ=0x%x", chassisCap->fru_info_dev_addr)); 637 return IPMI_CC_INVALID_FIELD_REQUEST; 638 } 639 640 if (0 != (chassisCap->sdr_dev_addr & ~chassisCapAddrMask)) 641 { 642 log<level::ERR>("Unsupported request parameter(SDR Addr)", 643 entry("REQ=0x%x", chassisCap->sdr_dev_addr)); 644 return IPMI_CC_INVALID_FIELD_REQUEST; 645 } 646 647 if (0 != (chassisCap->sel_dev_addr & ~chassisCapAddrMask)) 648 { 649 log<level::ERR>("Unsupported request parameter(SEL Addr)", 650 entry("REQ=0x%x", chassisCap->sel_dev_addr)); 651 return IPMI_CC_INVALID_FIELD_REQUEST; 652 } 653 654 if (0 != (chassisCap->system_management_dev_addr & ~chassisCapAddrMask)) 655 { 656 log<level::ERR>( 657 "Unsupported request parameter(SM Addr)", 658 entry("REQ=0x%x", chassisCap->system_management_dev_addr)); 659 return IPMI_CC_INVALID_FIELD_REQUEST; 660 } 661 662 if (0 != (chassisCap->bridge_dev_addr & ~chassisCapAddrMask)) 663 { 664 log<level::ERR>("Unsupported request parameter(Bridge Addr)", 665 entry("REQ=0x%x", chassisCap->bridge_dev_addr)); 666 return IPMI_CC_INVALID_FIELD_REQUEST; 667 } 668 669 try 670 { 671 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 672 ipmi::DbusObjectInfo chassisCapObject = 673 ipmi::getDbusObject(bus, chassisCapIntf); 674 675 ipmi::setDbusProperty(bus, chassisCapObject.second, 676 chassisCapObject.first, chassisCapIntf, 677 chassisCapFlagsProp, chassisCap->cap_flags); 678 679 ipmi::setDbusProperty(bus, chassisCapObject.second, 680 chassisCapObject.first, chassisCapIntf, 681 chassisFRUDevAddrProp, 682 chassisCap->fru_info_dev_addr); 683 684 ipmi::setDbusProperty(bus, chassisCapObject.second, 685 chassisCapObject.first, chassisCapIntf, 686 chassisSDRDevAddrProp, chassisCap->sdr_dev_addr); 687 688 ipmi::setDbusProperty(bus, chassisCapObject.second, 689 chassisCapObject.first, chassisCapIntf, 690 chassisSELDevAddrProp, chassisCap->sel_dev_addr); 691 692 ipmi::setDbusProperty(bus, chassisCapObject.second, 693 chassisCapObject.first, chassisCapIntf, 694 chassisSMDevAddrProp, 695 chassisCap->system_management_dev_addr); 696 697 ipmi::setDbusProperty(bus, chassisCapObject.second, 698 chassisCapObject.first, chassisCapIntf, 699 chassisBridgeDevAddrProp, 700 chassisCap->bridge_dev_addr); 701 } 702 catch (std::exception& e) 703 { 704 log<level::ERR>(e.what()); 705 rc = IPMI_CC_UNSPECIFIED_ERROR; 706 return rc; 707 } 708 709 return rc; 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 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 static constexpr uint8_t policyBitMask = 0x07; 785 static constexpr uint8_t setPolicyReqLen = 1; 786 } // namespace power_policy 787 788 //---------------------------------------------------------------------- 789 // Get Chassis Status commands 790 //---------------------------------------------------------------------- 791 ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 792 ipmi_request_t request, 793 ipmi_response_t response, 794 ipmi_data_len_t data_len, 795 ipmi_context_t context) 796 { 797 const char* objname = "/org/openbmc/control/power0"; 798 const char* intf = "org.openbmc.control.Power"; 799 800 sd_bus* bus = NULL; 801 sd_bus_message* reply = NULL; 802 int r = 0; 803 int pgood = 0; 804 char* busname = NULL; 805 ipmi_ret_t rc = IPMI_CC_OK; 806 ipmi_get_chassis_status_t chassis_status{}; 807 808 uint8_t s = 0; 809 810 using namespace chassis::internal; 811 using namespace chassis::internal::cache; 812 using namespace power_policy; 813 814 const auto& powerRestoreSetting = objects.map.at(powerRestoreIntf).front(); 815 auto method = dbus.new_method_call( 816 objects.service(powerRestoreSetting, powerRestoreIntf).c_str(), 817 powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Get"); 818 method.append(powerRestoreIntf, "PowerRestorePolicy"); 819 auto resp = dbus.call(method); 820 if (resp.is_method_error()) 821 { 822 log<level::ERR>("Error in PowerRestorePolicy Get"); 823 report<InternalFailure>(); 824 *data_len = 0; 825 return IPMI_CC_UNSPECIFIED_ERROR; 826 } 827 sdbusplus::message::variant<std::string> result; 828 resp.read(result); 829 auto powerRestore = 830 RestorePolicy::convertPolicyFromString(std::get<std::string>(result)); 831 832 *data_len = 4; 833 834 bus = ipmid_get_sd_bus_connection(); 835 836 r = mapper_get_service(bus, objname, &busname); 837 if (r < 0) 838 { 839 log<level::ERR>("Failed to get bus name", entry("ERRNO=0x%X", -r)); 840 rc = IPMI_CC_UNSPECIFIED_ERROR; 841 goto finish; 842 } 843 844 r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply, 845 "i"); 846 if (r < 0) 847 { 848 log<level::ERR>("Failed to call sd_bus_get_property", 849 entry("PROPERTY=%s", "pgood"), entry("ERRNO=0x%X", -r), 850 entry("BUS=%s", busname), entry("PATH=%s", objname), 851 entry("INTERFACE=%s", intf)); 852 rc = IPMI_CC_UNSPECIFIED_ERROR; 853 goto finish; 854 } 855 856 r = sd_bus_message_read(reply, "i", &pgood); 857 if (r < 0) 858 { 859 log<level::ERR>("Failed to read sensor:", entry("ERRNO=0x%X", -r)); 860 rc = IPMI_CC_UNSPECIFIED_ERROR; 861 goto finish; 862 } 863 864 s = dbusToIpmi.at(powerRestore); 865 866 // Current Power State 867 // [7] reserved 868 // [6..5] power restore policy 869 // 00b = chassis stays powered off after AC/mains returns 870 // 01b = after AC returns, power is restored to the state that was 871 // in effect when AC/mains was lost. 872 // 10b = chassis always powers up after AC/mains returns 873 // 11b = unknow 874 // Set to 00b, by observing the hardware behavior. 875 // Do we need to define a dbus property to identify the restore 876 // policy? 877 878 // [4] power control fault 879 // 1b = controller attempted to turn system power on or off, but 880 // system did not enter desired state. 881 // Set to 0b, since We don't support it.. 882 883 // [3] power fault 884 // 1b = fault detected in main power subsystem. 885 // set to 0b. for we don't support it. 886 887 // [2] 1b = interlock (chassis is presently shut down because a chassis 888 // panel interlock switch is active). (IPMI 1.5) 889 // set to 0b, for we don't support it. 890 891 // [1] power overload 892 // 1b = system shutdown because of power overload condition. 893 // set to 0b, for we don't support it. 894 895 // [0] power is on 896 // 1b = system power is on 897 // 0b = system power is off(soft-off S4/S5, or mechanical off) 898 899 chassis_status.cur_power_state = ((s & 0x3) << 5) | (pgood & 0x1); 900 901 // Last Power Event 902 // [7..5] – reserved 903 // [4] – 1b = last ‘Power is on’ state was entered via IPMI command 904 // [3] – 1b = last power down caused by power fault 905 // [2] – 1b = last power down caused by a power interlock being activated 906 // [1] – 1b = last power down caused by a Power overload 907 // [0] – 1b = AC failed 908 // set to 0x0, for we don't support these fields. 909 910 chassis_status.last_power_event = 0; 911 912 // Misc. Chassis State 913 // [7] – reserved 914 // [6] – 1b = Chassis Identify command and state info supported (Optional) 915 // 0b = Chassis Identify command support unspecified via this command. 916 // (The Get Command Support command , if implemented, would still 917 // indicate support for the Chassis Identify command) 918 // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved 919 // (return 920 // as 00b) otherwise. Returns the present chassis identify state. 921 // Refer to the Chassis Identify command for more info. 922 // 00b = chassis identify state = Off 923 // 01b = chassis identify state = Temporary(timed) On 924 // 10b = chassis identify state = Indefinite On 925 // 11b = reserved 926 // [3] – 1b = Cooling/fan fault detected 927 // [2] – 1b = Drive Fault 928 // [1] – 1b = Front Panel Lockout active (power off and reset via chassis 929 // push-buttons disabled.) 930 // [0] – 1b = Chassis Intrusion active 931 // set to 0, for we don't support them. 932 chassis_status.misc_power_state = 0; 933 934 // Front Panel Button Capabilities and disable/enable status(Optional) 935 // set to 0, for we don't support them. 936 chassis_status.front_panel_button_cap_status = 0; 937 938 // Pack the actual response 939 std::memcpy(response, &chassis_status, *data_len); 940 941 finish: 942 free(busname); 943 reply = sd_bus_message_unref(reply); 944 945 return rc; 946 } 947 948 //------------------------------------------------------------- 949 // Send a command to SoftPowerOff application to stop any timer 950 //------------------------------------------------------------- 951 int stop_soft_off_timer() 952 { 953 constexpr auto iface = "org.freedesktop.DBus.Properties"; 954 constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." 955 "SoftPowerOff"; 956 957 constexpr auto property = "ResponseReceived"; 958 constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." 959 "SoftPowerOff.HostResponse.HostShutdown"; 960 961 // Get the system bus where most system services are provided. 962 auto bus = ipmid_get_sd_bus_connection(); 963 964 // Get the service name 965 // TODO openbmc/openbmc#1661 - Mapper refactor 966 // 967 // See openbmc/openbmc#1743 for some details but high level summary is that 968 // for now the code will directly call the soft off interface due to a 969 // race condition with mapper usage 970 // 971 // char *busname = nullptr; 972 // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); 973 // if (r < 0) 974 //{ 975 // fprintf(stderr, "Failed to get %s bus name: %s\n", 976 // SOFTOFF_OBJPATH, -r); 977 // return r; 978 //} 979 980 // No error object or reply expected. 981 int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, 982 "Set", nullptr, nullptr, "ssv", soft_off_iface, 983 property, "s", value); 984 if (rc < 0) 985 { 986 log<level::ERR>("Failed to set property in SoftPowerOff object", 987 entry("ERRNO=0x%X", -rc)); 988 } 989 990 // TODO openbmc/openbmc#1661 - Mapper refactor 991 // free(busname); 992 return rc; 993 } 994 995 //---------------------------------------------------------------------- 996 // Create file to indicate there is no need for softoff notification to host 997 //---------------------------------------------------------------------- 998 void indicate_no_softoff_needed() 999 { 1000 fs::path path{HOST_INBAND_REQUEST_DIR}; 1001 if (!fs::is_directory(path)) 1002 { 1003 fs::create_directory(path); 1004 } 1005 1006 // Add the host instance (default 0 for now) to the file name 1007 std::string file{HOST_INBAND_REQUEST_FILE}; 1008 auto size = std::snprintf(nullptr, 0, file.c_str(), 0); 1009 size++; // null 1010 std::unique_ptr<char[]> buf(new char[size]); 1011 std::snprintf(buf.get(), size, file.c_str(), 0); 1012 1013 // Append file name to directory and create it 1014 path /= buf.get(); 1015 std::ofstream(path.c_str()); 1016 } 1017 1018 //---------------------------------------------------------------------- 1019 // Chassis Control commands 1020 //---------------------------------------------------------------------- 1021 ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1022 ipmi_request_t request, 1023 ipmi_response_t response, 1024 ipmi_data_len_t data_len, 1025 ipmi_context_t context) 1026 { 1027 // Error from power off. 1028 int rc = 0; 1029 1030 // No response for this command. 1031 *data_len = 0; 1032 1033 // Catch the actual operaton by peeking into request buffer 1034 uint8_t chassis_ctrl_cmd = *(uint8_t*)request; 1035 1036 switch (chassis_ctrl_cmd) 1037 { 1038 case CMD_POWER_ON: 1039 rc = initiate_state_transition(State::Host::Transition::On); 1040 break; 1041 case CMD_POWER_OFF: 1042 // This path would be hit in 2 conditions. 1043 // 1: When user asks for power off using ipmi chassis command 0x04 1044 // 2: Host asking for power off post shutting down. 1045 1046 // If it's a host requested power off, then need to nudge Softoff 1047 // application that it needs to stop the watchdog timer if running. 1048 // If it is a user requested power off, then this is not really 1049 // needed. But then we need to differentiate between user and host 1050 // calling this same command 1051 1052 // For now, we are going ahead with trying to nudge the soft off and 1053 // interpret the failure to do so as a non softoff case 1054 rc = stop_soft_off_timer(); 1055 1056 // Only request the Off transition if the soft power off 1057 // application is not running 1058 if (rc < 0) 1059 { 1060 // First create a file to indicate to the soft off application 1061 // that it should not run. Not doing this will result in State 1062 // manager doing a default soft power off when asked for power 1063 // off. 1064 indicate_no_softoff_needed(); 1065 1066 // Now request the shutdown 1067 rc = initiate_state_transition(State::Host::Transition::Off); 1068 } 1069 else 1070 { 1071 log<level::INFO>("Soft off is running, so let shutdown target " 1072 "stop the host"); 1073 } 1074 break; 1075 1076 case CMD_HARD_RESET: 1077 case CMD_POWER_CYCLE: 1078 // SPEC has a section that says certain implementations can trigger 1079 // PowerOn if power is Off when a command to power cycle is 1080 // requested 1081 1082 // First create a file to indicate to the soft off application 1083 // that it should not run since this is a direct user initiated 1084 // power reboot request (i.e. a reboot request that is not 1085 // originating via a soft power off SMS request) 1086 indicate_no_softoff_needed(); 1087 1088 rc = initiate_state_transition(State::Host::Transition::Reboot); 1089 break; 1090 1091 case CMD_SOFT_OFF_VIA_OVER_TEMP: 1092 // Request Host State Manager to do a soft power off 1093 rc = initiate_state_transition(State::Host::Transition::Off); 1094 break; 1095 1096 default: 1097 { 1098 log<level::ERR>("Invalid Chassis Control command", 1099 entry("CMD=0x%X", chassis_ctrl_cmd)); 1100 rc = -1; 1101 } 1102 } 1103 1104 return ((rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK); 1105 } 1106 1107 /** @brief Return D-Bus connection string to enclosure identify LED object 1108 * 1109 * @param[in, out] connection - connection to D-Bus object 1110 * @return a IPMI return code 1111 */ 1112 std::string getEnclosureIdentifyConnection() 1113 { 1114 // lookup enclosure_identify group owner(s) in mapper 1115 auto mapperCall = chassis::internal::dbus.new_method_call( 1116 ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF, 1117 "GetObject"); 1118 1119 mapperCall.append(identify_led_object_name); 1120 static const std::vector<std::string> interfaces = { 1121 "xyz.openbmc_project.Led.Group"}; 1122 mapperCall.append(interfaces); 1123 auto mapperReply = chassis::internal::dbus.call(mapperCall); 1124 if (mapperReply.is_method_error()) 1125 { 1126 log<level::ERR>("Chassis Identify: Error communicating to mapper."); 1127 elog<InternalFailure>(); 1128 } 1129 std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp; 1130 mapperReply.read(mapperResp); 1131 1132 if (mapperResp.size() != encIdentifyObjectsSize) 1133 { 1134 log<level::ERR>( 1135 "Invalid number of enclosure identify objects.", 1136 entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size())); 1137 elog<InternalFailure>(); 1138 } 1139 auto pair = mapperResp[encIdentifyObjectsSize - 1]; 1140 return pair.first; 1141 } 1142 1143 /** @brief Turn On/Off enclosure identify LED 1144 * 1145 * @param[in] flag - true to turn on LED, false to turn off 1146 * @return a IPMI return code 1147 */ 1148 void enclosureIdentifyLed(bool flag) 1149 { 1150 using namespace chassis::internal; 1151 std::string connection = std::move(getEnclosureIdentifyConnection()); 1152 auto msg = std::string("enclosureIdentifyLed(") + 1153 boost::lexical_cast<std::string>(flag) + ")"; 1154 log<level::DEBUG>(msg.c_str()); 1155 auto led = 1156 dbus.new_method_call(connection.c_str(), identify_led_object_name, 1157 "org.freedesktop.DBus.Properties", "Set"); 1158 led.append("xyz.openbmc_project.Led.Group", "Asserted", 1159 sdbusplus::message::variant<bool>(flag)); 1160 auto ledReply = dbus.call(led); 1161 if (ledReply.is_method_error()) 1162 { 1163 log<level::ERR>("Chassis Identify: Error Setting State On/Off\n", 1164 entry("LED_STATE=%d", flag)); 1165 elog<InternalFailure>(); 1166 } 1167 } 1168 1169 /** @brief Callback method to turn off LED 1170 */ 1171 void enclosureIdentifyLedOff() 1172 { 1173 try 1174 { 1175 enclosureIdentifyLed(false); 1176 } 1177 catch (const InternalFailure& e) 1178 { 1179 report<InternalFailure>(); 1180 } 1181 } 1182 1183 /** @brief Create timer to turn on and off the enclosure LED 1184 */ 1185 void createIdentifyTimer() 1186 { 1187 if (!identifyTimer) 1188 { 1189 identifyTimer = 1190 std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff); 1191 } 1192 } 1193 1194 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, 1195 std::optional<uint8_t> force) 1196 { 1197 uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); 1198 bool forceIdentify = force.value_or(0) & 0x01; 1199 1200 if (identifyInterval || forceIdentify) 1201 { 1202 // stop the timer if already started; 1203 // for force identify we should not turn off LED 1204 identifyTimer->stop(); 1205 try 1206 { 1207 enclosureIdentifyLed(true); 1208 } 1209 catch (const InternalFailure& e) 1210 { 1211 report<InternalFailure>(); 1212 return ipmi::responseResponseError(); 1213 } 1214 1215 if (forceIdentify) 1216 { 1217 return ipmi::responseSuccess(); 1218 } 1219 // start the timer 1220 auto time = std::chrono::duration_cast<std::chrono::microseconds>( 1221 std::chrono::seconds(identifyInterval)); 1222 identifyTimer->start(time); 1223 } 1224 else if (!identifyInterval) 1225 { 1226 identifyTimer->stop(); 1227 enclosureIdentifyLedOff(); 1228 } 1229 return ipmi::responseSuccess(); 1230 } 1231 1232 namespace boot_options 1233 { 1234 1235 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; 1236 using IpmiValue = uint8_t; 1237 constexpr auto ipmiDefault = 0; 1238 1239 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { 1240 {0x01, Source::Sources::Network}, 1241 {0x02, Source::Sources::Disk}, 1242 {0x05, Source::Sources::ExternalMedia}, 1243 {ipmiDefault, Source::Sources::Default}}; 1244 1245 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { 1246 {0x03, Mode::Modes::Safe}, 1247 {0x06, Mode::Modes::Setup}, 1248 {ipmiDefault, Mode::Modes::Regular}}; 1249 1250 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { 1251 {Source::Sources::Network, 0x01}, 1252 {Source::Sources::Disk, 0x02}, 1253 {Source::Sources::ExternalMedia, 0x05}, 1254 {Source::Sources::Default, ipmiDefault}}; 1255 1256 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { 1257 {Mode::Modes::Safe, 0x03}, 1258 {Mode::Modes::Setup, 0x06}, 1259 {Mode::Modes::Regular, ipmiDefault}}; 1260 1261 } // namespace boot_options 1262 1263 /** @brief Set the property value for boot source 1264 * @param[in] source - boot source value 1265 * @return On failure return IPMI error. 1266 */ 1267 static ipmi_ret_t setBootSource(const Source::Sources& source) 1268 { 1269 using namespace chassis::internal; 1270 using namespace chassis::internal::cache; 1271 sdbusplus::message::variant<std::string> property = 1272 convertForMessage(source); 1273 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1274 const auto& bootSourceSetting = std::get<settings::Path>(bootSetting); 1275 auto method = dbus.new_method_call( 1276 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1277 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set"); 1278 method.append(bootSourceIntf, "BootSource", property); 1279 auto reply = dbus.call(method); 1280 if (reply.is_method_error()) 1281 { 1282 log<level::ERR>("Error in BootSource Set"); 1283 report<InternalFailure>(); 1284 return IPMI_CC_UNSPECIFIED_ERROR; 1285 } 1286 return IPMI_CC_OK; 1287 } 1288 1289 /** @brief Set the property value for boot mode 1290 * @param[in] mode - boot mode value 1291 * @return On failure return IPMI error. 1292 */ 1293 static ipmi_ret_t setBootMode(const Mode::Modes& mode) 1294 { 1295 using namespace chassis::internal; 1296 using namespace chassis::internal::cache; 1297 sdbusplus::message::variant<std::string> property = convertForMessage(mode); 1298 auto bootSetting = settings::boot::setting(objects, bootModeIntf); 1299 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1300 auto method = dbus.new_method_call( 1301 objects.service(bootModeSetting, bootModeIntf).c_str(), 1302 bootModeSetting.c_str(), ipmi::PROP_INTF, "Set"); 1303 method.append(bootModeIntf, "BootMode", property); 1304 auto reply = dbus.call(method); 1305 if (reply.is_method_error()) 1306 { 1307 log<level::ERR>("Error in BootMode Set"); 1308 report<InternalFailure>(); 1309 return IPMI_CC_UNSPECIFIED_ERROR; 1310 } 1311 return IPMI_CC_OK; 1312 } 1313 1314 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1315 ipmi_request_t request, 1316 ipmi_response_t response, 1317 ipmi_data_len_t data_len, 1318 ipmi_context_t context) 1319 { 1320 using namespace boot_options; 1321 ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED; 1322 char* p = NULL; 1323 get_sys_boot_options_response_t* resp = 1324 (get_sys_boot_options_response_t*)response; 1325 get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request; 1326 IpmiValue bootOption = ipmiDefault; 1327 1328 std::memset(resp, 0, sizeof(*resp)); 1329 resp->version = SET_PARM_VERSION; 1330 resp->parm = 5; 1331 resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME; 1332 1333 /* 1334 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1335 * This is the only parameter used by petitboot. 1336 */ 1337 if (reqptr->parameter == 1338 static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS)) 1339 { 1340 1341 *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS); 1342 using namespace chassis::internal; 1343 using namespace chassis::internal::cache; 1344 1345 try 1346 { 1347 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1348 const auto& bootSourceSetting = 1349 std::get<settings::Path>(bootSetting); 1350 auto oneTimeEnabled = 1351 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1352 auto method = dbus.new_method_call( 1353 objects.service(bootSourceSetting, bootSourceIntf).c_str(), 1354 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get"); 1355 method.append(bootSourceIntf, "BootSource"); 1356 auto reply = dbus.call(method); 1357 if (reply.is_method_error()) 1358 { 1359 log<level::ERR>("Error in BootSource Get"); 1360 report<InternalFailure>(); 1361 *data_len = 0; 1362 return IPMI_CC_UNSPECIFIED_ERROR; 1363 } 1364 sdbusplus::message::variant<std::string> result; 1365 reply.read(result); 1366 auto bootSource = 1367 Source::convertSourcesFromString(std::get<std::string>(result)); 1368 1369 bootSetting = settings::boot::setting(objects, bootModeIntf); 1370 const auto& bootModeSetting = std::get<settings::Path>(bootSetting); 1371 method = dbus.new_method_call( 1372 objects.service(bootModeSetting, bootModeIntf).c_str(), 1373 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get"); 1374 method.append(bootModeIntf, "BootMode"); 1375 reply = dbus.call(method); 1376 if (reply.is_method_error()) 1377 { 1378 log<level::ERR>("Error in BootMode Get"); 1379 report<InternalFailure>(); 1380 *data_len = 0; 1381 return IPMI_CC_UNSPECIFIED_ERROR; 1382 } 1383 reply.read(result); 1384 auto bootMode = 1385 Mode::convertModesFromString(std::get<std::string>(result)); 1386 1387 bootOption = sourceDbusToIpmi.at(bootSource); 1388 if ((Mode::Modes::Regular == bootMode) && 1389 (Source::Sources::Default == bootSource)) 1390 { 1391 bootOption = ipmiDefault; 1392 } 1393 else if (Source::Sources::Default == bootSource) 1394 { 1395 bootOption = modeDbusToIpmi.at(bootMode); 1396 } 1397 resp->data[1] = (bootOption << 2); 1398 1399 resp->data[0] = oneTimeEnabled 1400 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 1401 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT; 1402 1403 rc = IPMI_CC_OK; 1404 } 1405 catch (InternalFailure& e) 1406 { 1407 report<InternalFailure>(); 1408 *data_len = 0; 1409 return IPMI_CC_UNSPECIFIED_ERROR; 1410 } 1411 } 1412 else if (reqptr->parameter == 1413 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS)) 1414 { 1415 1416 *data_len = 1417 static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS); 1418 1419 resp->parm = 1420 static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS); 1421 1422 int ret = getHostNetworkData(resp); 1423 1424 if (ret < 0) 1425 { 1426 1427 log<level::ERR>( 1428 "getHostNetworkData failed for get_sys_boot_options."); 1429 rc = IPMI_CC_UNSPECIFIED_ERROR; 1430 } 1431 else 1432 rc = IPMI_CC_OK; 1433 } 1434 1435 else 1436 { 1437 log<level::ERR>("Unsupported parameter", 1438 entry("PARAM=0x%x", reqptr->parameter)); 1439 } 1440 1441 if (p) 1442 free(p); 1443 1444 if (rc == IPMI_CC_OK) 1445 { 1446 *data_len += 2; 1447 } 1448 1449 return rc; 1450 } 1451 1452 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1453 ipmi_request_t request, 1454 ipmi_response_t response, 1455 ipmi_data_len_t data_len, 1456 ipmi_context_t context) 1457 { 1458 using namespace boot_options; 1459 ipmi_ret_t rc = IPMI_CC_OK; 1460 set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request; 1461 1462 std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n", 1463 reqptr->parameter); 1464 1465 // This IPMI command does not have any resposne data 1466 *data_len = 0; 1467 1468 /* 000101 1469 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1470 * This is the only parameter used by petitboot. 1471 */ 1472 1473 if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) 1474 { 1475 IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2); 1476 using namespace chassis::internal; 1477 using namespace chassis::internal::cache; 1478 auto oneTimeEnabled = false; 1479 constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable"; 1480 constexpr auto oneTimePath = 1481 "/xyz/openbmc_project/control/host0/boot/one_time"; 1482 1483 try 1484 { 1485 bool permanent = 1486 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) == 1487 SET_PARM_BOOT_FLAGS_PERMANENT; 1488 1489 auto bootSetting = settings::boot::setting(objects, bootSourceIntf); 1490 1491 oneTimeEnabled = 1492 std::get<settings::boot::OneTimeEnabled>(bootSetting); 1493 1494 /* 1495 * Check if the current boot setting is onetime or permanent, if the 1496 * request in the command is otherwise, then set the "Enabled" 1497 * property in one_time object path to 'True' to indicate onetime 1498 * and 'False' to indicate permanent. 1499 * 1500 * Once the onetime/permanent setting is applied, then the bootMode 1501 * and bootSource is updated for the corresponding object. 1502 */ 1503 if ((permanent && oneTimeEnabled) || 1504 (!permanent && !oneTimeEnabled)) 1505 { 1506 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath); 1507 1508 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf, 1509 "Enabled", !permanent); 1510 } 1511 1512 auto modeItr = modeIpmiToDbus.find(bootOption); 1513 auto sourceItr = sourceIpmiToDbus.find(bootOption); 1514 if (sourceIpmiToDbus.end() != sourceItr) 1515 { 1516 rc = setBootSource(sourceItr->second); 1517 if (rc != IPMI_CC_OK) 1518 { 1519 *data_len = 0; 1520 return rc; 1521 } 1522 // If a set boot device is mapping to a boot source, then reset 1523 // the boot mode D-Bus property to default. 1524 // This way the ipmid code can determine which property is not 1525 // at the default value 1526 if (sourceItr->second != Source::Sources::Default) 1527 { 1528 setBootMode(Mode::Modes::Regular); 1529 } 1530 } 1531 if (modeIpmiToDbus.end() != modeItr) 1532 { 1533 rc = setBootMode(modeItr->second); 1534 if (rc != IPMI_CC_OK) 1535 { 1536 *data_len = 0; 1537 return rc; 1538 } 1539 // If a set boot device is mapping to a boot mode, then reset 1540 // the boot source D-Bus property to default. 1541 // This way the ipmid code can determine which property is not 1542 // at the default value 1543 if (modeItr->second != Mode::Modes::Regular) 1544 { 1545 setBootSource(Source::Sources::Default); 1546 } 1547 } 1548 } 1549 catch (InternalFailure& e) 1550 { 1551 report<InternalFailure>(); 1552 *data_len = 0; 1553 return IPMI_CC_UNSPECIFIED_ERROR; 1554 } 1555 } 1556 else if (reqptr->parameter == 1557 (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) 1558 { 1559 1560 int ret = setHostNetworkData(reqptr); 1561 if (ret < 0) 1562 { 1563 log<level::ERR>( 1564 "setHostNetworkData failed for set_sys_boot_options"); 1565 rc = IPMI_CC_UNSPECIFIED_ERROR; 1566 } 1567 } 1568 else if (reqptr->parameter == 1569 static_cast<uint8_t>(BootOptionParameter::BOOT_INFO)) 1570 { 1571 // Handle parameter #4 and return command completed normally 1572 // (IPMI_CC_OK). There is no implementation in OpenBMC for this 1573 // parameter. This is added to support the ipmitool command `chassis 1574 // bootdev` which sends set on parameter #4, before setting the boot 1575 // flags. 1576 rc = IPMI_CC_OK; 1577 } 1578 else 1579 { 1580 log<level::ERR>("Unsupported parameter", 1581 entry("PARAM=0x%x", reqptr->parameter)); 1582 rc = IPMI_CC_PARM_NOT_SUPPORTED; 1583 } 1584 1585 return rc; 1586 } 1587 1588 ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1589 ipmi_request_t request, ipmi_response_t response, 1590 ipmi_data_len_t data_len, ipmi_context_t context) 1591 { 1592 // sd_bus error 1593 ipmi_ret_t rc = IPMI_CC_OK; 1594 1595 auto resptr = reinterpret_cast<GetPOHCountResponse*>(response); 1596 1597 try 1598 { 1599 auto pohCounter = getPOHCounter(); 1600 resptr->counterReading[0] = pohCounter; 1601 resptr->counterReading[1] = pohCounter >> 8; 1602 resptr->counterReading[2] = pohCounter >> 16; 1603 resptr->counterReading[3] = pohCounter >> 24; 1604 } 1605 catch (std::exception& e) 1606 { 1607 log<level::ERR>(e.what()); 1608 return IPMI_CC_UNSPECIFIED_ERROR; 1609 } 1610 1611 resptr->minPerCount = poh::minutesPerCount; 1612 *data_len = sizeof(GetPOHCountResponse); 1613 1614 return rc; 1615 } 1616 1617 ipmi_ret_t ipmi_chassis_set_power_restore_policy( 1618 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 1619 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 1620 { 1621 auto* reqptr = reinterpret_cast<uint8_t*>(request); 1622 auto* resptr = reinterpret_cast<uint8_t*>(response); 1623 uint8_t reqPolicy = 0; 1624 1625 power_policy::DbusValue value = 1626 power_policy::RestorePolicy::Policy::AlwaysOff; 1627 1628 if (*data_len != power_policy::setPolicyReqLen) 1629 { 1630 phosphor::logging::log<level::ERR>("Unsupported request length", 1631 entry("LEN=0x%x", *data_len)); 1632 *data_len = 0; 1633 return IPMI_CC_REQ_DATA_LEN_INVALID; 1634 } 1635 1636 if (*reqptr > power_policy::noChange) 1637 { 1638 phosphor::logging::log<level::ERR>("Reserved request parameter", 1639 entry("REQ=0x%x", *reqptr)); 1640 *data_len = 0; 1641 return IPMI_CC_PARM_OUT_OF_RANGE; 1642 } 1643 1644 reqPolicy = *reqptr & power_policy::policyBitMask; 1645 if (reqPolicy == power_policy::noChange) 1646 { 1647 // just return the supported policy 1648 *resptr = power_policy::allSupport; 1649 *data_len = power_policy::setPolicyReqLen; 1650 return IPMI_CC_OK; 1651 } 1652 1653 for (auto const& it : power_policy::dbusToIpmi) 1654 { 1655 if (it.second == reqPolicy) 1656 { 1657 value = it.first; 1658 break; 1659 } 1660 } 1661 1662 try 1663 { 1664 const settings::Path& powerRestoreSetting = 1665 chassis::internal::cache::objects.map 1666 .at(chassis::internal::powerRestoreIntf) 1667 .front(); 1668 sdbusplus::message::variant<std::string> property = 1669 convertForMessage(value); 1670 1671 auto method = chassis::internal::dbus.new_method_call( 1672 chassis::internal::cache::objects 1673 .service(powerRestoreSetting, 1674 chassis::internal::powerRestoreIntf) 1675 .c_str(), 1676 powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Set"); 1677 1678 method.append(chassis::internal::powerRestoreIntf, "PowerRestorePolicy", 1679 property); 1680 auto reply = chassis::internal::dbus.call(method); 1681 if (reply.is_method_error()) 1682 { 1683 phosphor::logging::log<level::ERR>("Unspecified Error"); 1684 *data_len = 0; 1685 return IPMI_CC_UNSPECIFIED_ERROR; 1686 } 1687 } 1688 catch (InternalFailure& e) 1689 { 1690 report<InternalFailure>(); 1691 *data_len = 0; 1692 return IPMI_CC_UNSPECIFIED_ERROR; 1693 } 1694 1695 *resptr = power_policy::allSupport; 1696 *data_len = power_policy::setPolicyReqLen; 1697 return IPMI_CC_OK; 1698 } 1699 1700 void register_netfn_chassis_functions() 1701 { 1702 createIdentifyTimer(); 1703 1704 // <Wildcard Command> 1705 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL, 1706 ipmi_chassis_wildcard, PRIVILEGE_USER); 1707 1708 // Get Chassis Capabilities 1709 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, 1710 ipmi_get_chassis_cap, PRIVILEGE_USER); 1711 1712 // Set Chassis Capabilities 1713 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL, 1714 ipmi_set_chassis_cap, PRIVILEGE_USER); 1715 1716 // <Get System Boot Options> 1717 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL, 1718 ipmi_chassis_get_sys_boot_options, 1719 PRIVILEGE_OPERATOR); 1720 1721 // <Get Chassis Status> 1722 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL, 1723 ipmi_get_chassis_status, PRIVILEGE_USER); 1724 1725 // <Chassis Control> 1726 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL, 1727 ipmi_chassis_control, PRIVILEGE_OPERATOR); 1728 1729 // <Chassis Identify> 1730 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, 1731 ipmi::chassis::cmdChassisIdentify, 1732 ipmi::Privilege::Operator, ipmiChassisIdentify); 1733 1734 // <Set System Boot Options> 1735 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL, 1736 ipmi_chassis_set_sys_boot_options, 1737 PRIVILEGE_OPERATOR); 1738 // <Get POH Counter> 1739 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_POH_COUNTER, NULL, 1740 ipmiGetPOHCounter, PRIVILEGE_USER); 1741 1742 // <Set Power Restore Policy> 1743 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL, 1744 ipmi_chassis_set_power_restore_policy, 1745 PRIVILEGE_OPERATOR); 1746 } 1747