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