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