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