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