1 #include "chassishandler.h" 2 #include "host-ipmid/ipmid-api.h" 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <stdint.h> 6 #include <mapper.h> 7 #include <arpa/inet.h> 8 #include <netinet/in.h> 9 #include <limits.h> 10 #include <string.h> 11 #include <endian.h> 12 #include <sstream> 13 #include <array> 14 #include <fstream> 15 #include <experimental/filesystem> 16 #include <phosphor-logging/log.hpp> 17 #include <xyz/openbmc_project/State/Host/server.hpp> 18 #include "config.h" 19 20 //Defines 21 #define SET_PARM_VERSION 0x01 22 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 //boot flags data1 7th bit on 23 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80 //boot flags data1 8th bit on 24 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 //boot flags data1 7 & 8 bit on 25 26 constexpr size_t SIZE_MAC = 18; 27 constexpr size_t SIZE_BOOT_OPTION = (uint8_t)BootOptionResponseSize:: 28 OPAL_NETWORK_SETTINGS;//Maximum size of the boot option parametrs 29 constexpr size_t SIZE_PREFIX = 7; 30 constexpr size_t MAX_PREFIX_VALUE = 32; 31 constexpr size_t SIZE_COOKIE = 4; 32 constexpr size_t SIZE_VERSION = 2; 33 constexpr auto MAC_ADDRESS_FORMAT = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"; 34 constexpr auto IP_ADDRESS_FORMAT = "%d.%d.%d.%d"; 35 constexpr auto PREFIX_FORMAT = "%hhd"; 36 constexpr auto ADDR_TYPE_FORMAT = "%hhx"; 37 //PetiBoot-Specific 38 static constexpr uint8_t net_conf_initial_bytes[] = {0x80,0x21, 0x70 ,0x62 ,0x21, 39 0x00 ,0x01 ,0x06 ,0x04}; 40 41 static constexpr size_t COOKIE_OFFSET = 1; 42 static constexpr size_t VERSION_OFFSET = 5; 43 static constexpr size_t MAC_OFFSET = 9; 44 static constexpr size_t ADDRTYPE_OFFSET = 16; 45 static constexpr size_t IPADDR_OFFSET = 17; 46 static constexpr size_t PREFIX_OFFSET = 21; 47 static constexpr size_t GATEWAY_OFFSET = 22; 48 49 using namespace phosphor::logging; 50 51 52 void register_netfn_chassis_functions() __attribute__((constructor)); 53 54 // Host settings in dbus 55 // Service name should be referenced by connection name got via object mapper 56 const char *settings_object_name = "/org/openbmc/settings/host0"; 57 const char *settings_intf_name = "org.freedesktop.DBus.Properties"; 58 const char *host_intf_name = "org.openbmc.settings.Host"; 59 60 typedef struct 61 { 62 uint8_t cap_flags; 63 uint8_t fru_info_dev_addr; 64 uint8_t sdr_dev_addr; 65 uint8_t sel_dev_addr; 66 uint8_t system_management_dev_addr; 67 uint8_t bridge_dev_addr; 68 }__attribute__((packed)) ipmi_chassis_cap_t; 69 70 typedef struct 71 { 72 uint8_t cur_power_state; 73 uint8_t last_power_event; 74 uint8_t misc_power_state; 75 uint8_t front_panel_button_cap_status; 76 }__attribute__((packed)) ipmi_get_chassis_status_t; 77 78 // Phosphor Host State manager 79 namespace State = sdbusplus::xyz::openbmc_project::State::server; 80 81 namespace fs = std::experimental::filesystem; 82 83 int dbus_get_property(const char *name, char **buf) 84 { 85 sd_bus_error error = SD_BUS_ERROR_NULL; 86 sd_bus_message *m = NULL; 87 sd_bus *bus = NULL; 88 char *temp_buf = NULL; 89 char *connection = NULL; 90 int r; 91 92 // Get the system bus where most system services are provided. 93 bus = ipmid_get_sd_bus_connection(); 94 95 r = mapper_get_service(bus, settings_object_name, &connection); 96 if (r < 0) { 97 fprintf(stderr, "Failed to get %s connection: %s\n", 98 settings_object_name, strerror(-r)); 99 goto finish; 100 } 101 102 /* 103 * Bus, service, object path, interface and method are provided to call 104 * the method. 105 * Signatures and input arguments are provided by the arguments at the 106 * end. 107 */ 108 r = sd_bus_call_method(bus, 109 connection, /* service to contact */ 110 settings_object_name, /* object path */ 111 settings_intf_name, /* interface name */ 112 "Get", /* method name */ 113 &error, /* object to return error in */ 114 &m, /* return message on success */ 115 "ss", /* input signature */ 116 host_intf_name, /* first argument */ 117 name); /* second argument */ 118 119 if (r < 0) { 120 fprintf(stderr, "Failed to issue method call: %s\n", error.message); 121 goto finish; 122 } 123 124 /* 125 * The output should be parsed exactly the same as the output formatting 126 * specified. 127 */ 128 r = sd_bus_message_read(m, "v", "s", &temp_buf); 129 if (r < 0) { 130 fprintf(stderr, "Failed to parse response message: %s\n", strerror(-r)); 131 goto finish; 132 } 133 134 *buf = strdup(temp_buf); 135 /* *buf = (char*) malloc(strlen(temp_buf)); 136 if (*buf) { 137 strcpy(*buf, temp_buf); 138 } 139 */ 140 printf("IPMID boot option property get: {%s}.\n", (char *) temp_buf); 141 142 finish: 143 sd_bus_error_free(&error); 144 sd_bus_message_unref(m); 145 free(connection); 146 147 return r; 148 } 149 150 int dbus_set_property(const char * name, const char *value) 151 { 152 sd_bus_error error = SD_BUS_ERROR_NULL; 153 sd_bus_message *m = NULL; 154 sd_bus *bus = NULL; 155 char *connection = NULL; 156 int r; 157 158 // Get the system bus where most system services are provided. 159 bus = ipmid_get_sd_bus_connection(); 160 161 r = mapper_get_service(bus, settings_object_name, &connection); 162 if (r < 0) { 163 fprintf(stderr, "Failed to get %s connection: %s\n", 164 settings_object_name, strerror(-r)); 165 goto finish; 166 } 167 168 /* 169 * Bus, service, object path, interface and method are provided to call 170 * the method. 171 * Signatures and input arguments are provided by the arguments at the 172 * end. 173 */ 174 r = sd_bus_call_method(bus, 175 connection, /* service to contact */ 176 settings_object_name, /* object path */ 177 settings_intf_name, /* interface name */ 178 "Set", /* method name */ 179 &error, /* object to return error in */ 180 &m, /* return message on success */ 181 "ssv", /* input signature */ 182 host_intf_name, /* first argument */ 183 name, /* second argument */ 184 "s", /* third argument */ 185 value); /* fourth argument */ 186 187 if (r < 0) { 188 fprintf(stderr, "Failed to issue method call: %s\n", error.message); 189 goto finish; 190 } 191 192 printf("IPMID boot option property set: {%s}.\n", value); 193 194 finish: 195 sd_bus_error_free(&error); 196 sd_bus_message_unref(m); 197 free(connection); 198 199 return r; 200 } 201 202 struct get_sys_boot_options_t { 203 uint8_t parameter; 204 uint8_t set; 205 uint8_t block; 206 } __attribute__ ((packed)); 207 208 struct get_sys_boot_options_response_t { 209 uint8_t version; 210 uint8_t parm; 211 uint8_t data[SIZE_BOOT_OPTION]; 212 } __attribute__ ((packed)); 213 214 struct set_sys_boot_options_t { 215 uint8_t parameter; 216 uint8_t data[SIZE_BOOT_OPTION]; 217 } __attribute__ ((packed)); 218 219 struct host_network_config_t { 220 std::string ipaddress; 221 std::string prefix; 222 std::string gateway; 223 std::string macaddress; 224 std::string addrType; 225 226 host_network_config_t()=default; 227 }; 228 229 void fillNetworkConfig( host_network_config_t & host_config , 230 const std::string& conf_str ) { 231 232 constexpr auto COMMA_DELIMITER = ","; 233 constexpr auto EQUAL_DELIMITER = "="; 234 size_t commaDelimtrPos = 0; 235 size_t equalDelimtrPos = 0,commaDelimtrPrevPos = 0; 236 std::string value; 237 while ( commaDelimtrPos < conf_str.length() ) { 238 239 commaDelimtrPos = conf_str.find(COMMA_DELIMITER,commaDelimtrPos); 240 //This condition is to extract the last 241 //Substring as we will not be having the delimeter 242 //at end. std::string::npos is -1 243 244 if ( commaDelimtrPos == std::string::npos ) { 245 commaDelimtrPos = conf_str.length(); 246 } 247 248 equalDelimtrPos = conf_str.find (EQUAL_DELIMITER,commaDelimtrPrevPos); 249 250 //foo,ipaddress=1234 251 if ( equalDelimtrPos == std::string::npos ) { 252 253 commaDelimtrPos++; 254 commaDelimtrPrevPos= commaDelimtrPos; 255 continue; 256 } 257 258 value = conf_str.substr((equalDelimtrPos+1), 259 commaDelimtrPos-(equalDelimtrPos+1)); 260 261 #ifdef _IPMI_DEBUG_ 262 printf ("Name=[%s],Value=[%s],commaDelimtrPos=[%d],\ 263 commaDelimtrPrevPos=[%d],equalDelimtrPos=[%d]\n", 264 name.c_str(),value.c_str(),commaDelimtrPos, 265 commaDelimtrPrevPos,equalDelimtrPos); 266 #endif 267 268 if ( 0 == conf_str.compare(commaDelimtrPrevPos, 269 equalDelimtrPos-commaDelimtrPrevPos, 270 "ipaddress" )) { 271 host_config.ipaddress = std::move(value); 272 } 273 else if ( 0 == conf_str.compare(commaDelimtrPrevPos, 274 equalDelimtrPos-commaDelimtrPrevPos, 275 "prefix" )) { 276 host_config.prefix = std::move(value); 277 } 278 else if ( 0 == conf_str.compare(commaDelimtrPrevPos, 279 equalDelimtrPos-commaDelimtrPrevPos, 280 "gateway" )) { 281 host_config.gateway = std::move(value); 282 } 283 else if ( 0 == conf_str.compare(commaDelimtrPrevPos, 284 equalDelimtrPos-commaDelimtrPrevPos, 285 "mac" )) { 286 host_config.macaddress = std::move(value); 287 } 288 else if ( 0 == conf_str.compare(commaDelimtrPrevPos, 289 equalDelimtrPos-commaDelimtrPrevPos, 290 "addr_type" )) { 291 host_config.addrType = std::move(value); 292 } 293 294 commaDelimtrPos++; 295 commaDelimtrPrevPos= commaDelimtrPos; 296 } 297 } 298 299 int getHostNetworkData(get_sys_boot_options_response_t* respptr) 300 { 301 302 char *prop = nullptr; 303 int rc = dbus_get_property("network_config",&prop); 304 305 if ( rc < 0 ) { 306 fprintf(stderr, "Dbus get property(boot_flags) failed\ 307 for get_sys_boot_options.\n"); 308 return rc; 309 } 310 311 std::string conf_str(prop); 312 313 if ( prop ) { 314 315 free(prop); 316 prop = nullptr; 317 } 318 319 /* network_config property Value would be in the form of 320 * ipaddress=1.1.1.1,prefix=16,gateway=2.2.2.2,mac=11:22:33:44:55:66,dhcp=0 321 */ 322 323 /* Parsing the string and fill the hostconfig structure with the 324 * values */ 325 326 printf ("Configuration String[%s]\n ",conf_str.c_str()); 327 328 host_network_config_t host_config; 329 330 // Fill the host_config from the configuration string 331 fillNetworkConfig(host_config,conf_str); 332 333 //Assigning the index as intialByteLength as it is fixed and prefilled. 334 printf ("host_config.macaddress.c_str()=[%s]\n",host_config.macaddress.c_str()); 335 do{ 336 337 rc = sscanf(host_config.macaddress.c_str(),MAC_ADDRESS_FORMAT, 338 (respptr->data+MAC_OFFSET), (respptr->data+MAC_OFFSET+1), 339 (respptr->data+MAC_OFFSET+2),(respptr->data+MAC_OFFSET+3), 340 (respptr->data+MAC_OFFSET+4), (respptr->data+MAC_OFFSET+5)); 341 342 343 if ( rc < 6 ){ 344 fprintf(stderr, "sscanf Failed in extracting mac address.\n"); 345 rc = -1; 346 break; 347 } 348 349 //Conevrt the dhcp,ipaddress,mask and gateway as hex number 350 respptr->data[MAC_OFFSET+6]=0x00; 351 352 rc = sscanf(host_config.addrType.c_str(),ADDR_TYPE_FORMAT, 353 (respptr->data+ADDRTYPE_OFFSET)); 354 355 if ( rc <= 0 ) { 356 fprintf(stderr, "sscanf Failed in extracting address type.\n"); 357 rc = -1; 358 break; 359 } 360 361 //ipaddress and gateway would be in IPv4 format 362 rc = inet_pton(AF_INET,host_config.ipaddress.c_str(), 363 (respptr->data+IPADDR_OFFSET)); 364 365 if ( rc <= 0 ) { 366 fprintf(stderr, "inet_pton failed during ipaddress coneversion\n"); 367 rc = -1; 368 break; 369 } 370 371 rc = sscanf(host_config.prefix.c_str(),PREFIX_FORMAT, 372 (respptr->data+PREFIX_OFFSET)); 373 374 if ( rc <= 0 ) { 375 fprintf(stderr, "sscanf failed during prefix extraction.\n"); 376 rc = -1; 377 break; 378 } 379 380 rc = inet_pton(AF_INET,host_config.gateway.c_str(), 381 (respptr->data+GATEWAY_OFFSET)); 382 383 if ( rc <= 0 ) { 384 fprintf(stderr, "inet_pton failed during gateway conversion.\n"); 385 rc = -1; 386 break; 387 } 388 389 }while (0); 390 391 if ( rc ) { 392 393 //PetiBoot-Specific 394 //If sucess then copy the first 9 bytes to the data 395 //else set the respptr to 0 396 397 memcpy(respptr->data,net_conf_initial_bytes, 398 sizeof(net_conf_initial_bytes)); 399 400 #ifdef _IPMI_DEBUG_ 401 printf ("\n===Printing the IPMI Formatted Data========\n"); 402 403 for ( uint8_t pos = 0; pos<index; pos++ ) 404 printf("%02x ", respptr->data[pos]); 405 #endif 406 407 }else { 408 409 memset(respptr->data,0,SIZE_BOOT_OPTION); 410 } 411 412 return rc; 413 } 414 415 int setHostNetworkData(set_sys_boot_options_t * reqptr) 416 { 417 std::string host_network_config; 418 char mac[SIZE_MAC] = {0}; 419 char ipAddress[INET_ADDRSTRLEN] = {0}; 420 char gateway[INET_ADDRSTRLEN] = {0}; 421 char dhcp[SIZE_PREFIX] = {0}; 422 char prefix[SIZE_PREFIX] = {0}; 423 int rc = 0; 424 uint32_t zeroCookie=0; 425 426 //cookie starts from second byte 427 // version starts from sixth byte 428 429 do { 430 431 // cookie == 0x21 0x70 0x62 0x21 432 if ( memcmp(&(reqptr->data[COOKIE_OFFSET]), 433 (net_conf_initial_bytes+COOKIE_OFFSET), 434 SIZE_COOKIE) != 0 ) { 435 //cookie == 0 436 if ( memcmp(&(reqptr->data[COOKIE_OFFSET]), 437 &zeroCookie, 438 SIZE_COOKIE) == 0 ) { 439 rc = 0; 440 break; 441 } 442 //Invalid cookie 443 fprintf(stderr, "Invalid Cookie\n"); 444 rc = -1; 445 break; 446 } 447 // vesion == 0x00 0x01 448 if ( memcmp(&(reqptr->data[VERSION_OFFSET]), 449 (net_conf_initial_bytes+VERSION_OFFSET), 450 SIZE_VERSION) != 0 ) { 451 452 fprintf(stderr, "Invalid Version\n"); 453 rc = -1; 454 break; 455 } 456 457 snprintf(mac, SIZE_MAC, MAC_ADDRESS_FORMAT, 458 reqptr->data[MAC_OFFSET], 459 reqptr->data[MAC_OFFSET+1], 460 reqptr->data[MAC_OFFSET+2], 461 reqptr->data[MAC_OFFSET+3], 462 reqptr->data[MAC_OFFSET+4], 463 reqptr->data[MAC_OFFSET+5]); 464 465 snprintf(dhcp,SIZE_PREFIX, ADDR_TYPE_FORMAT, reqptr->data[ADDRTYPE_OFFSET]); 466 //Validating the address type which could be 467 //either static or dynamic 468 if( *(reqptr->data+ADDRTYPE_OFFSET) > 1 ) { 469 470 fprintf(stderr, "Invalid Address Type\n"); 471 rc = -1; 472 break; 473 474 } 475 476 snprintf(ipAddress, INET_ADDRSTRLEN, IP_ADDRESS_FORMAT, 477 reqptr->data[IPADDR_OFFSET], reqptr->data[IPADDR_OFFSET+1], 478 reqptr->data[IPADDR_OFFSET+2], reqptr->data[IPADDR_OFFSET+3]); 479 480 //validating prefix 481 if ( *(reqptr->data+PREFIX_OFFSET) > (uint8_t)MAX_PREFIX_VALUE ) { 482 483 fprintf(stderr, "Invalid Prefix\n"); 484 rc = -1; 485 break; 486 } 487 488 snprintf(prefix,SIZE_PREFIX,PREFIX_FORMAT, reqptr->data[PREFIX_OFFSET]); 489 490 snprintf(gateway, INET_ADDRSTRLEN,IP_ADDRESS_FORMAT, 491 reqptr->data[GATEWAY_OFFSET], 492 reqptr->data[GATEWAY_OFFSET+1], 493 reqptr->data[GATEWAY_OFFSET+2], 494 reqptr->data[GATEWAY_OFFSET+3]); 495 496 497 }while(0); 498 499 if( !rc ) 500 { 501 //Cookie == 0 or it is a valid cookie 502 host_network_config += "ipaddress="+std::string(ipAddress)+",prefix="+ 503 std::string(prefix)+",gateway="+std::string(gateway)+ 504 ",mac="+std::string(mac)+",addr_type="+std::string(dhcp); 505 506 printf ("Network configuration changed: %s\n",host_network_config.c_str()); 507 508 rc = dbus_set_property("network_config",host_network_config.c_str()); 509 510 if ( rc < 0 ) { 511 fprintf(stderr, "Dbus set property(network_config)\ 512 failed for set_sys_boot_options.\n"); 513 rc = -1; 514 } 515 516 } 517 return rc; 518 } 519 520 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 521 ipmi_request_t request, 522 ipmi_response_t response, 523 ipmi_data_len_t data_len, 524 ipmi_context_t context) 525 { 526 printf("Handling CHASSIS WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd); 527 // Status code. 528 ipmi_ret_t rc = IPMI_CC_INVALID; 529 *data_len = 0; 530 return rc; 531 } 532 533 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 534 ipmi_request_t request, ipmi_response_t response, 535 ipmi_data_len_t data_len, ipmi_context_t context) 536 { 537 // sd_bus error 538 ipmi_ret_t rc = IPMI_CC_OK; 539 540 ipmi_chassis_cap_t chassis_cap{}; 541 542 *data_len = sizeof(ipmi_chassis_cap_t); 543 544 // TODO: need future work. Get those flag from MRW. 545 546 // capabilities flags 547 // [7..4] - reserved 548 // [3] – 1b = provides power interlock (IPM 1.5) 549 // [2] – 1b = provides Diagnostic Interrupt (FP NMI) 550 // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis has capabilities 551 // to lock out external power control and reset button or front panel interfaces 552 // and/or detect tampering with those interfaces). 553 // [0] -1b = Chassis provides intrusion (physical security) sensor. 554 // set to default value 0x0. 555 chassis_cap.cap_flags = 0x0; 556 557 // Since we do not have a separate SDR Device/SEL Device/ FRU repository. 558 // The 20h was given as those 5 device addresses. 559 // Chassis FRU info Device Address 560 chassis_cap.fru_info_dev_addr = 0x20; 561 562 // Chassis SDR Device Address 563 chassis_cap.sdr_dev_addr = 0x20; 564 565 // Chassis SEL Device Address 566 chassis_cap.sel_dev_addr = 0x20; 567 568 // Chassis System Management Device Address 569 chassis_cap.system_management_dev_addr = 0x20; 570 571 // Chassis Bridge Device Address. 572 chassis_cap.bridge_dev_addr = 0x20; 573 574 memcpy(response, &chassis_cap, *data_len); 575 576 return rc; 577 } 578 579 //------------------------------------------ 580 // Calls into Host State Manager Dbus object 581 //------------------------------------------ 582 int initiate_state_transition(State::Host::Transition transition) 583 { 584 // OpenBMC Host State Manager dbus framework 585 constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0"; 586 constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host"; 587 constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; 588 constexpr auto PROPERTY = "RequestedHostTransition"; 589 590 // sd_bus error 591 int rc = 0; 592 char *busname = NULL; 593 594 // SD Bus error report mechanism. 595 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 596 597 // Gets a hook onto either a SYSTEM or SESSION bus 598 sd_bus *bus_type = ipmid_get_sd_bus_connection(); 599 rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname); 600 if (rc < 0) 601 { 602 log<level::ERR>("Failed to get bus name", 603 entry("ERROR=%s, OBJPATH=%s", 604 strerror(-rc), HOST_STATE_MANAGER_ROOT)); 605 return rc; 606 } 607 608 // Convert to string equivalent of the passed in transition enum. 609 auto request = State::convertForMessage(transition); 610 611 rc = sd_bus_call_method(bus_type, // On the system bus 612 busname, // Service to contact 613 HOST_STATE_MANAGER_ROOT, // Object path 614 DBUS_PROPERTY_IFACE, // Interface name 615 "Set", // Method to be called 616 &bus_error, // object to return error 617 nullptr, // Response buffer if any 618 "ssv", // Takes 3 arguments 619 HOST_STATE_MANAGER_IFACE, 620 PROPERTY, 621 "s", request.c_str()); 622 if(rc < 0) 623 { 624 log<level::ERR>("Failed to initiate transition", 625 entry("ERROR=%s, REQUEST=%s", 626 bus_error.message, request.c_str())); 627 } 628 else 629 { 630 log<level::INFO>("Transition request initiated successfully"); 631 } 632 633 sd_bus_error_free(&bus_error); 634 free(busname); 635 636 return rc; 637 } 638 639 struct hostPowerPolicyTypeMap_t 640 { 641 uint8_t policyNum; 642 char policyName[19]; 643 }; 644 645 hostPowerPolicyTypeMap_t g_hostPowerPolicyTypeMap_t[] = { 646 647 {0x00, "LEAVE_OFF"}, 648 {0x01, "RESTORE_LAST_STATE"}, 649 {0x02, "ALWAYS_POWER_ON"}, 650 {0x03, "UNKNOWN"} 651 }; 652 653 uint8_t get_host_power_policy(char *p) 654 { 655 656 hostPowerPolicyTypeMap_t *s = g_hostPowerPolicyTypeMap_t; 657 658 while (s->policyNum != 0x03) { 659 if (!strcmp(s->policyName,p)) 660 break; 661 s++; 662 } 663 664 return s->policyNum; 665 } 666 667 //---------------------------------------------------------------------- 668 // Get Chassis Status commands 669 //---------------------------------------------------------------------- 670 ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 671 ipmi_request_t request, 672 ipmi_response_t response, 673 ipmi_data_len_t data_len, 674 ipmi_context_t context) 675 { 676 const char *objname = "/org/openbmc/control/power0"; 677 const char *intf = "org.openbmc.control.Power"; 678 679 sd_bus *bus = NULL; 680 sd_bus_message *reply = NULL; 681 int r = 0; 682 int pgood = 0; 683 char *busname = NULL; 684 ipmi_ret_t rc = IPMI_CC_OK; 685 ipmi_get_chassis_status_t chassis_status{}; 686 687 char *p = NULL; 688 uint8_t s = 0; 689 690 // Get the system bus where most system services are provided. 691 bus = ipmid_get_sd_bus_connection(); 692 693 *data_len = 4; 694 695 r = mapper_get_service(bus, objname, &busname); 696 if (r < 0) { 697 fprintf(stderr, "Failed to get bus name, return value: %s.\n", strerror(-r)); 698 rc = IPMI_CC_UNSPECIFIED_ERROR; 699 goto finish; 700 } 701 702 r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply, "i"); 703 if (r < 0) { 704 fprintf(stderr, "Failed to call sd_bus_get_property:%d, %s\n", r, strerror(-r)); 705 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n", 706 busname, objname, intf); 707 rc = IPMI_CC_UNSPECIFIED_ERROR; 708 goto finish; 709 } 710 711 r = sd_bus_message_read(reply, "i", &pgood); 712 if (r < 0) { 713 fprintf(stderr, "Failed to read sensor: %s\n", strerror(-r)); 714 rc = IPMI_CC_UNSPECIFIED_ERROR; 715 goto finish; 716 } 717 718 printf("pgood is 0x%02x\n", pgood); 719 720 // Get Power Policy 721 r = dbus_get_property("power_policy",&p); 722 723 if (r < 0) { 724 fprintf(stderr, "Dbus get property(power_policy) failed for get_sys_boot_options.\n"); 725 rc = IPMI_CC_UNSPECIFIED_ERROR; 726 } else { 727 s = get_host_power_policy(p); 728 } 729 730 if (p) 731 { 732 free(p); 733 p = NULL; 734 } 735 736 // Current Power State 737 // [7] reserved 738 // [6..5] power restore policy 739 // 00b = chassis stays powered off after AC/mains returns 740 // 01b = after AC returns, power is restored to the state that was 741 // in effect when AC/mains was lost. 742 // 10b = chassis always powers up after AC/mains returns 743 // 11b = unknow 744 // Set to 00b, by observing the hardware behavior. 745 // Do we need to define a dbus property to identify the restore policy? 746 747 // [4] power control fault 748 // 1b = controller attempted to turn system power on or off, but 749 // system did not enter desired state. 750 // Set to 0b, since We don't support it.. 751 752 // [3] power fault 753 // 1b = fault detected in main power subsystem. 754 // set to 0b. for we don't support it. 755 756 // [2] 1b = interlock (chassis is presently shut down because a chassis 757 // panel interlock switch is active). (IPMI 1.5) 758 // set to 0b, for we don't support it. 759 760 // [1] power overload 761 // 1b = system shutdown because of power overload condition. 762 // set to 0b, for we don't support it. 763 764 // [0] power is on 765 // 1b = system power is on 766 // 0b = system power is off(soft-off S4/S5, or mechanical off) 767 768 chassis_status.cur_power_state = ((s & 0x3)<<5) | (pgood & 0x1); 769 770 // Last Power Event 771 // [7..5] – reserved 772 // [4] – 1b = last ‘Power is on’ state was entered via IPMI command 773 // [3] – 1b = last power down caused by power fault 774 // [2] – 1b = last power down caused by a power interlock being activated 775 // [1] – 1b = last power down caused by a Power overload 776 // [0] – 1b = AC failed 777 // set to 0x0, for we don't support these fields. 778 779 chassis_status.last_power_event = 0; 780 781 // Misc. Chassis State 782 // [7] – reserved 783 // [6] – 1b = Chassis Identify command and state info supported (Optional) 784 // 0b = Chassis Identify command support unspecified via this command. 785 // (The Get Command Support command , if implemented, would still 786 // indicate support for the Chassis Identify command) 787 // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved (return 788 // as 00b) otherwise. Returns the present chassis identify state. 789 // Refer to the Chassis Identify command for more info. 790 // 00b = chassis identify state = Off 791 // 01b = chassis identify state = Temporary(timed) On 792 // 10b = chassis identify state = Indefinite On 793 // 11b = reserved 794 // [3] – 1b = Cooling/fan fault detected 795 // [2] – 1b = Drive Fault 796 // [1] – 1b = Front Panel Lockout active (power off and reset via chassis 797 // push-buttons disabled.) 798 // [0] – 1b = Chassis Intrusion active 799 // set to 0, for we don't support them. 800 chassis_status.misc_power_state = 0; 801 802 // Front Panel Button Capabilities and disable/enable status(Optional) 803 // set to 0, for we don't support them. 804 chassis_status.front_panel_button_cap_status = 0; 805 806 // Pack the actual response 807 memcpy(response, &chassis_status, *data_len); 808 809 finish: 810 free(busname); 811 reply = sd_bus_message_unref(reply); 812 813 return rc; 814 } 815 816 //------------------------------------------------------------- 817 // Send a command to SoftPowerOff application to stop any timer 818 //------------------------------------------------------------- 819 int stop_soft_off_timer() 820 { 821 constexpr auto iface = "org.freedesktop.DBus.Properties"; 822 constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." 823 "SoftPowerOff"; 824 825 constexpr auto property = "ResponseReceived"; 826 constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." 827 "SoftPowerOff.HostResponse.HostShutdown"; 828 829 // Get the system bus where most system services are provided. 830 auto bus = ipmid_get_sd_bus_connection(); 831 832 // Get the service name 833 // TODO openbmc/openbmc#1661 - Mapper refactor 834 // 835 // See openbmc/openbmc#1743 for some details but high level summary is that 836 // for now the code will directly call the soft off interface due to a 837 // race condition with mapper usage 838 // 839 //char *busname = nullptr; 840 //auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); 841 //if (r < 0) 842 //{ 843 // fprintf(stderr, "Failed to get %s bus name: %s\n", 844 // SOFTOFF_OBJPATH, strerror(-r)); 845 // return r; 846 //} 847 848 // No error object or reply expected. 849 int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, 850 "Set", nullptr, nullptr, "ssv", 851 soft_off_iface, property, "s", value); 852 if (rc < 0) 853 { 854 fprintf(stderr, "Failed to set property in SoftPowerOff object: %s\n", 855 strerror(-rc)); 856 } 857 858 //TODO openbmc/openbmc#1661 - Mapper refactor 859 //free(busname); 860 return rc; 861 } 862 863 //---------------------------------------------------------------------- 864 // Create file to indicate there is no need for softoff notification to host 865 //---------------------------------------------------------------------- 866 void indicate_no_softoff_needed() 867 { 868 fs::path path{HOST_INBAND_REQUEST_DIR}; 869 if (!fs::is_directory(path)) 870 { 871 fs::create_directory(path); 872 } 873 874 // Add the host instance (default 0 for now) to the file name 875 std::string file{HOST_INBAND_REQUEST_FILE}; 876 auto size = std::snprintf(nullptr,0,file.c_str(),0); 877 size++; // null 878 std::unique_ptr<char[]> buf(new char[size]); 879 std::snprintf(buf.get(),size,file.c_str(),0); 880 881 // Append file name to directory and create it 882 path /= buf.get(); 883 std::ofstream(path.c_str()); 884 } 885 886 //---------------------------------------------------------------------- 887 // Chassis Control commands 888 //---------------------------------------------------------------------- 889 ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 890 ipmi_request_t request, 891 ipmi_response_t response, 892 ipmi_data_len_t data_len, 893 ipmi_context_t context) 894 { 895 // Error from power off. 896 int rc = 0; 897 898 // No response for this command. 899 *data_len = 0; 900 901 // Catch the actual operaton by peeking into request buffer 902 uint8_t chassis_ctrl_cmd = *(uint8_t *)request; 903 printf("Chassis Control Command: Operation:[0x%X]\n",chassis_ctrl_cmd); 904 905 switch(chassis_ctrl_cmd) 906 { 907 case CMD_POWER_ON: 908 rc = initiate_state_transition(State::Host::Transition::On); 909 break; 910 case CMD_POWER_OFF: 911 // Need to Nudge SoftPowerOff application that it needs to stop the 912 // watchdog timer if running. 913 rc = stop_soft_off_timer(); 914 // Only request the Off transition if the soft power off 915 // application is not running 916 if (rc < 0) 917 { 918 log<level::INFO>("Did not find soft off service so request " 919 "Host:Transition:Off"); 920 921 // First create a file to indicate to the soft off application 922 // that it should not run since this is a direct user initiated 923 // power off request (i.e. a power off request that is not 924 // originating via a soft power off SMS request) 925 indicate_no_softoff_needed(); 926 927 // Now request the shutdown 928 rc = initiate_state_transition(State::Host::Transition::Off); 929 } 930 else 931 { 932 log<level::INFO>("Soft off is running, so let that stop " 933 "the host"); 934 } 935 936 break; 937 938 case CMD_HARD_RESET: 939 case CMD_POWER_CYCLE: 940 // SPEC has a section that says certain implementations can trigger 941 // PowerOn if power is Off when a command to power cycle is 942 // requested 943 944 // First create a file to indicate to the soft off application 945 // that it should not run since this is a direct user initiated 946 // power reboot request (i.e. a reboot request that is not 947 // originating via a soft power off SMS request) 948 indicate_no_softoff_needed(); 949 950 rc = initiate_state_transition(State::Host::Transition::Reboot); 951 break; 952 default: 953 { 954 fprintf(stderr, "Invalid Chassis Control command:[0x%X] received\n",chassis_ctrl_cmd); 955 rc = -1; 956 } 957 } 958 959 return ( (rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK); 960 } 961 962 struct bootOptionTypeMap_t { 963 uint8_t ipmibootflag; 964 char dbusname[8]; 965 }; 966 967 #define INVALID_STRING "Invalid" 968 // dbus supports this list of boot devices. 969 bootOptionTypeMap_t g_bootOptionTypeMap_t[] = { 970 971 {0x01, "Network"}, 972 {0x02, "Disk"}, 973 {0x03, "Safe"}, 974 {0x05, "CDROM"}, 975 {0x06, "Setup"}, 976 {0x00, "Default"}, 977 {0xFF, INVALID_STRING} 978 }; 979 980 uint8_t get_ipmi_boot_option(char *p) { 981 982 bootOptionTypeMap_t *s = g_bootOptionTypeMap_t; 983 984 while (s->ipmibootflag != 0xFF) { 985 if (!strcmp(s->dbusname,p)) 986 break; 987 s++; 988 } 989 990 if (!s->ipmibootflag) 991 printf("Failed to find Sensor Type %s\n", p); 992 993 return s->ipmibootflag; 994 } 995 996 char* get_boot_option_by_ipmi(uint8_t p) { 997 998 bootOptionTypeMap_t *s = g_bootOptionTypeMap_t; 999 1000 while (s->ipmibootflag != 0xFF) { 1001 1002 if (s->ipmibootflag == p) 1003 break; 1004 1005 s++; 1006 } 1007 1008 1009 if (!s->ipmibootflag) 1010 printf("Failed to find Sensor Type 0x%x\n", p); 1011 1012 return s->dbusname; 1013 } 1014 1015 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1016 ipmi_request_t request, 1017 ipmi_response_t response, 1018 ipmi_data_len_t data_len, 1019 ipmi_context_t context) 1020 { 1021 ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED; 1022 char *p = NULL; 1023 get_sys_boot_options_response_t *resp = (get_sys_boot_options_response_t *) response; 1024 get_sys_boot_options_t *reqptr = (get_sys_boot_options_t*) request; 1025 uint8_t s; 1026 1027 printf("IPMI GET_SYS_BOOT_OPTIONS\n"); 1028 1029 memset(resp,0,sizeof(*resp)); 1030 resp->version = SET_PARM_VERSION; 1031 resp->parm = 5; 1032 resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME; 1033 1034 1035 /* 1036 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1037 * This is the only parameter used by petitboot. 1038 */ 1039 if ( reqptr->parameter == static_cast<uint8_t> 1040 ( BootOptionParameter::BOOT_FLAGS )) { 1041 1042 *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS); 1043 /* Get the boot device */ 1044 int r = dbus_get_property("boot_flags",&p); 1045 1046 if (r < 0) { 1047 fprintf(stderr, "Dbus get property(boot_flags) failed for get_sys_boot_options.\n"); 1048 rc = IPMI_CC_UNSPECIFIED_ERROR; 1049 1050 } else { 1051 1052 s = get_ipmi_boot_option(p); 1053 resp->data[1] = (s << 2); 1054 rc = IPMI_CC_OK; 1055 1056 } 1057 1058 if (p) 1059 { 1060 free(p); 1061 p = NULL; 1062 } 1063 1064 /* Get the boot policy */ 1065 r = dbus_get_property("boot_policy",&p); 1066 1067 if (r < 0) { 1068 fprintf(stderr, "Dbus get property(boot_policy) failed for get_sys_boot_options.\n"); 1069 rc = IPMI_CC_UNSPECIFIED_ERROR; 1070 1071 } else { 1072 1073 printf("BootPolicy is[%s]", p); 1074 resp->data[0] = (strncmp(p,"ONETIME",strlen("ONETIME"))==0) ? 1075 SET_PARM_BOOT_FLAGS_VALID_ONE_TIME: 1076 SET_PARM_BOOT_FLAGS_VALID_PERMANENT; 1077 rc = IPMI_CC_OK; 1078 1079 } 1080 1081 1082 } else if ( reqptr->parameter == static_cast<uint8_t> 1083 ( BootOptionParameter::OPAL_NETWORK_SETTINGS )) { 1084 1085 *data_len = static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS); 1086 1087 resp->parm = static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS); 1088 1089 int ret = getHostNetworkData(resp); 1090 1091 if (ret < 0) { 1092 1093 fprintf(stderr, "getHostNetworkData failed for get_sys_boot_options.\n"); 1094 rc = IPMI_CC_UNSPECIFIED_ERROR; 1095 1096 }else 1097 rc = IPMI_CC_OK; 1098 } 1099 1100 else { 1101 fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter); 1102 } 1103 1104 if (p) 1105 free(p); 1106 1107 if (rc == IPMI_CC_OK) 1108 { 1109 *data_len += 2; 1110 } 1111 1112 return rc; 1113 } 1114 1115 1116 1117 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 1118 ipmi_request_t request, 1119 ipmi_response_t response, 1120 ipmi_data_len_t data_len, 1121 ipmi_context_t context) 1122 { 1123 ipmi_ret_t rc = IPMI_CC_OK; 1124 char *s; 1125 set_sys_boot_options_t *reqptr = (set_sys_boot_options_t *) request; 1126 1127 printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",reqptr->parameter); 1128 1129 // This IPMI command does not have any resposne data 1130 *data_len = 0; 1131 1132 /* 000101 1133 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. 1134 * This is the only parameter used by petitboot. 1135 */ 1136 1137 if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) { 1138 1139 s = get_boot_option_by_ipmi(((reqptr->data[1] & 0x3C) >> 2)); 1140 1141 printf("%d: %s\n", __LINE__, s); 1142 if (!strcmp(s,INVALID_STRING)) { 1143 1144 rc = IPMI_CC_PARM_NOT_SUPPORTED; 1145 1146 } else { 1147 1148 int r = dbus_set_property("boot_flags",s); 1149 1150 if (r < 0) { 1151 fprintf(stderr, "Dbus set property(boot_flags) failed for set_sys_boot_options.\n"); 1152 rc = IPMI_CC_UNSPECIFIED_ERROR; 1153 } 1154 } 1155 1156 /* setting the boot policy */ 1157 s = (char *)(((reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) == 1158 SET_PARM_BOOT_FLAGS_PERMANENT) ?"PERMANENT":"ONETIME"); 1159 1160 printf ( "\nBoot Policy is %s",s); 1161 int r = dbus_set_property("boot_policy",s); 1162 1163 if (r < 0) { 1164 fprintf(stderr, "Dbus set property(boot_policy) failed for set_sys_boot_options.\n"); 1165 rc = IPMI_CC_UNSPECIFIED_ERROR; 1166 } 1167 1168 } else if (reqptr->parameter == 1169 (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) { 1170 1171 int ret = setHostNetworkData(reqptr); 1172 if (ret < 0) { 1173 fprintf(stderr, "setHostNetworkData failed for set_sys_boot_options.\n"); 1174 rc = IPMI_CC_UNSPECIFIED_ERROR; 1175 } 1176 } 1177 else { 1178 fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter); 1179 rc = IPMI_CC_PARM_NOT_SUPPORTED; 1180 } 1181 1182 return rc; 1183 } 1184 1185 void register_netfn_chassis_functions() 1186 { 1187 // <Wildcard Command> 1188 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_WILDCARD); 1189 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL, ipmi_chassis_wildcard, 1190 PRIVILEGE_USER); 1191 1192 // Get Chassis Capabilities 1193 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP); 1194 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, ipmi_get_chassis_cap, 1195 PRIVILEGE_USER); 1196 1197 // <Get System Boot Options> 1198 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS); 1199 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL, 1200 ipmi_chassis_get_sys_boot_options, PRIVILEGE_OPERATOR); 1201 1202 // <Get Chassis Status> 1203 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS); 1204 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL, ipmi_get_chassis_status, 1205 PRIVILEGE_USER); 1206 1207 // <Chassis Control> 1208 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL); 1209 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL, ipmi_chassis_control, 1210 PRIVILEGE_OPERATOR); 1211 1212 // <Set System Boot Options> 1213 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS); 1214 ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL, 1215 ipmi_chassis_set_sys_boot_options, PRIVILEGE_OPERATOR); 1216 } 1217