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