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