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