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