1 #include "apphandler.h" 2 #include "app/channel.hpp" 3 #include "app/watchdog.hpp" 4 #include "host-ipmid/ipmid-api.h" 5 #include "ipmid.hpp" 6 #include "nlohmann/json.hpp" 7 #include "types.hpp" 8 #include "utils.hpp" 9 10 #include <fstream> 11 #include <stdio.h> 12 #include <stdint.h> 13 #include <systemd/sd-bus.h> 14 #include <mapper.h> 15 #include <array> 16 #include <vector> 17 #include <string> 18 #include <cstddef> 19 #include <experimental/filesystem> 20 21 #include <arpa/inet.h> 22 #include "transporthandler.hpp" 23 24 #include <phosphor-logging/log.hpp> 25 #include <phosphor-logging/elog-errors.hpp> 26 #include "xyz/openbmc_project/Common/error.hpp" 27 #include "xyz/openbmc_project/Software/Version/server.hpp" 28 #include "xyz/openbmc_project/Software/Activation/server.hpp" 29 30 extern sd_bus *bus; 31 32 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc"; 33 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID"; 34 constexpr auto bmc_guid_property = "UUID"; 35 constexpr auto bmc_guid_len = 16; 36 37 static constexpr auto redundancyIntf = 38 "xyz.openbmc_project.Software.RedundancyPriority"; 39 static constexpr auto versionIntf = 40 "xyz.openbmc_project.Software.Version"; 41 static constexpr auto activationIntf = 42 "xyz.openbmc_project.Software.Activation"; 43 static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; 44 45 void register_netfn_app_functions() __attribute__((constructor)); 46 47 using namespace phosphor::logging; 48 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 49 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; 50 using Activation = 51 sdbusplus::xyz::openbmc_project::Software::server::Activation; 52 namespace fs = std::experimental::filesystem; 53 54 // Offset in get device id command. 55 typedef struct 56 { 57 uint8_t id; 58 uint8_t revision; 59 uint8_t fw[2]; 60 uint8_t ipmi_ver; 61 uint8_t addn_dev_support; 62 uint8_t manuf_id[3]; 63 uint8_t prod_id[2]; 64 uint8_t aux[4]; 65 }__attribute__((packed)) ipmi_device_id_t; 66 67 /** 68 * @brief Returns the Version info from primary s/w object 69 * 70 * Get the Version info from the active s/w object which is having high 71 * "Priority" value(a smaller number is a higher priority) and "Purpose" 72 * is "BMC" from the list of all s/w objects those are implementing 73 * RedundancyPriority interface from the given softwareRoot path. 74 * 75 * @return On success returns the Version info from primary s/w object. 76 * 77 */ 78 std::string getActiveSoftwareVersionInfo() 79 { 80 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 81 82 std::string revision{}; 83 auto objectTree = ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, 84 ""); 85 if (objectTree.empty()) 86 { 87 log<level::ERR>("No Obj has implemented the s/w redundancy interface", 88 entry("INTERFACE=%s", redundancyIntf)); 89 elog<InternalFailure>(); 90 } 91 92 auto objectFound = false; 93 for (auto& softObject : objectTree) 94 { 95 auto service = ipmi::getService(bus, redundancyIntf, softObject.first); 96 auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot); 97 98 auto minPriority = 0xFF; 99 for (const auto& objIter : objValueTree) 100 { 101 try 102 { 103 auto& intfMap = objIter.second; 104 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); 105 auto& versionProps = intfMap.at(versionIntf); 106 auto& activationProps = intfMap.at(activationIntf); 107 auto priority = 108 redundancyPriorityProps.at("Priority").get<uint8_t>(); 109 auto purpose = versionProps.at("Purpose").get<std::string>(); 110 auto activation = 111 activationProps.at("Activation").get<std::string>(); 112 auto version = versionProps.at("Version").get<std::string>(); 113 if ((Version::convertVersionPurposeFromString(purpose) == 114 Version::VersionPurpose::BMC) && 115 (Activation::convertActivationsFromString(activation) == 116 Activation::Activations::Active)) 117 { 118 if (priority < minPriority) 119 { 120 minPriority = priority; 121 objectFound = true; 122 revision = std::move(version); 123 } 124 } 125 } 126 catch (const std::exception& e) 127 { 128 log<level::ERR>(e.what()); 129 } 130 } 131 } 132 133 if (!objectFound) 134 { 135 log<level::ERR>("Could not found an BMC software Object"); 136 elog<InternalFailure>(); 137 } 138 139 return revision; 140 } 141 142 143 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 144 ipmi_request_t request, ipmi_response_t response, 145 ipmi_data_len_t data_len, ipmi_context_t context) 146 { 147 ipmi_ret_t rc = IPMI_CC_OK; 148 *data_len = 0; 149 150 log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n"); 151 return rc; 152 } 153 154 typedef struct 155 { 156 char major; 157 char minor; 158 uint16_t d[2]; 159 } rev_t; 160 161 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */ 162 /* return -1 if not in those formats, this routine knows how to parse */ 163 /* version = v0.6-19-gf363f61-dirty */ 164 /* ^ ^ ^^ ^ */ 165 /* | | |----------|-- additional details */ 166 /* | |---------------- Minor */ 167 /* |------------------ Major */ 168 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */ 169 /* ^ ^ ^^ ^ */ 170 /* | | |--|---------- additional details */ 171 /* | |---------------- Minor */ 172 /* |------------------ Major */ 173 /* Additional details : If the option group exists it will force Auxiliary */ 174 /* Firmware Revision Information 4th byte to 1 indicating the build was */ 175 /* derived with additional edits */ 176 int convert_version(const char * p, rev_t *rev) 177 { 178 std::string s(p); 179 std::string token; 180 uint16_t commits; 181 182 auto location = s.find_first_of('v'); 183 if (location != std::string::npos) 184 { 185 s = s.substr(location+1); 186 } 187 188 if (!s.empty()) 189 { 190 location = s.find_first_of("."); 191 if (location != std::string::npos) 192 { 193 rev->major = static_cast<char>( 194 std::stoi(s.substr(0, location), 0, 16)); 195 token = s.substr(location+1); 196 } 197 198 if (!token.empty()) 199 { 200 location = token.find_first_of(".-"); 201 if (location != std::string::npos) 202 { 203 rev->minor = static_cast<char>( 204 std::stoi(token.substr(0, location), 0, 16)); 205 token = token.substr(location+1); 206 } 207 } 208 209 // Capture the number of commits on top of the minor tag. 210 // I'm using BE format like the ipmi spec asked for 211 location = token.find_first_of(".-"); 212 if (!token.empty()) 213 { 214 commits = std::stoi(token.substr(0, location), 0, 16); 215 rev->d[0] = (commits>>8) | (commits<<8); 216 217 // commit number we skip 218 location = token.find_first_of(".-"); 219 if (location != std::string::npos) 220 { 221 token = token.substr(location+1); 222 } 223 } 224 else { 225 rev->d[0] = 0; 226 } 227 228 if (location != std::string::npos) 229 { 230 token = token.substr(location+1); 231 } 232 233 // Any value of the optional parameter forces it to 1 234 location = token.find_first_of(".-"); 235 if (location != std::string::npos) 236 { 237 token = token.substr(location+1); 238 } 239 commits = (!token.empty()) ? 1 : 0; 240 241 //We do this operation to get this displayed in least significant bytes 242 //of ipmitool device id command. 243 rev->d[1] = (commits>>8) | (commits<<8); 244 } 245 246 return 0; 247 } 248 249 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 250 ipmi_request_t request, ipmi_response_t response, 251 ipmi_data_len_t data_len, ipmi_context_t context) 252 { 253 ipmi_ret_t rc = IPMI_CC_OK; 254 int r = -1; 255 rev_t rev = {0}; 256 static ipmi_device_id_t dev_id{}; 257 static bool dev_id_initialized = false; 258 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 259 260 // Data length 261 *data_len = sizeof(dev_id); 262 263 if (!dev_id_initialized) 264 { 265 try 266 { 267 auto version = getActiveSoftwareVersionInfo(); 268 r = convert_version(version.c_str(), &rev); 269 } 270 catch (const std::exception& e) 271 { 272 log<level::ERR>(e.what()); 273 } 274 275 if( r >= 0 ) { 276 // bit7 identifies if the device is available 277 // 0=normal operation 278 // 1=device firmware, SDR update, 279 // or self-initialization in progress. 280 // our SDR is normal working condition, so mask: 281 dev_id.fw[0] = 0x7F & rev.major; 282 283 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 284 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 285 memcpy(&dev_id.aux, rev.d, 4); 286 } 287 288 // IPMI Spec version 2.0 289 dev_id.ipmi_ver = 2; 290 291 std::ifstream dev_id_file(filename); 292 if (dev_id_file.is_open()) 293 { 294 auto data = nlohmann::json::parse(dev_id_file, nullptr, false); 295 if (!data.is_discarded()) 296 { 297 dev_id.id = data.value("id", 0); 298 dev_id.revision = data.value("revision", 0); 299 dev_id.addn_dev_support = data.value("addn_dev_support", 0); 300 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16; 301 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8; 302 dev_id.manuf_id[0] = data.value("manuf_id", 0); 303 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8; 304 dev_id.prod_id[0] = data.value("prod_id", 0); 305 dev_id.aux[3] = data.value("aux", 0); 306 dev_id.aux[2] = data.value("aux", 0) >> 8; 307 dev_id.aux[1] = data.value("aux", 0) >> 16; 308 dev_id.aux[0] = data.value("aux", 0) >> 24; 309 310 //Don't read the file every time if successful 311 dev_id_initialized = true; 312 } 313 else 314 { 315 log<level::ERR>("Device ID JSON parser failure"); 316 rc = IPMI_CC_UNSPECIFIED_ERROR; 317 } 318 } 319 else 320 { 321 log<level::ERR>("Device ID file not found"); 322 rc = IPMI_CC_UNSPECIFIED_ERROR; 323 } 324 } 325 326 // Pack the actual response 327 memcpy(response, &dev_id, *data_len); 328 329 return rc; 330 } 331 332 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 333 ipmi_request_t request, ipmi_response_t response, 334 ipmi_data_len_t data_len, ipmi_context_t context) 335 { 336 ipmi_ret_t rc = IPMI_CC_OK; 337 338 // Byte 2: 339 // 55h - No error. 340 // 56h - Self Test function not implemented in this controller. 341 // 57h - Corrupted or inaccesssible data or devices. 342 // 58h - Fatal hardware error. 343 // FFh - reserved. 344 // all other: Device-specific 'internal failure'. 345 // Byte 3: 346 // For byte 2 = 55h, 56h, FFh: 00h 347 // For byte 2 = 58h, all other: Device-specific 348 // For byte 2 = 57h: self-test error bitfield. 349 // Note: returning 57h does not imply that all test were run. 350 // [7] 1b = Cannot access SEL device. 351 // [6] 1b = Cannot access SDR Repository. 352 // [5] 1b = Cannot access BMC FRU device. 353 // [4] 1b = IPMB signal lines do not respond. 354 // [3] 1b = SDR Repository empty. 355 // [2] 1b = Internal Use Area of BMC FRU corrupted. 356 // [1] 1b = controller update 'boot block' firmware corrupted. 357 // [0] 1b = controller operational firmware corrupted. 358 359 char selftestresults[2] = {0}; 360 361 *data_len = 2; 362 363 selftestresults[0] = 0x56; 364 selftestresults[1] = 0; 365 366 memcpy(response, selftestresults, *data_len); 367 368 return rc; 369 } 370 371 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 372 ipmi_request_t request, ipmi_response_t response, 373 ipmi_data_len_t data_len, ipmi_context_t context) 374 { 375 const char *objname = "/org/openbmc/control/chassis0"; 376 const char *iface = "org.freedesktop.DBus.Properties"; 377 const char *chassis_iface = "org.openbmc.control.Chassis"; 378 sd_bus_message *reply = NULL; 379 sd_bus_error error = SD_BUS_ERROR_NULL; 380 int r = 0; 381 char *uuid = NULL; 382 char *busname = NULL; 383 384 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 385 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte 386 // order 387 // Ex: 0x2332fc2c40e66298e511f2782395a361 388 389 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec 390 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response 391 // Point resp end of array to save in reverse order 392 int resp_loc = resp_size-1; 393 int i = 0; 394 char *tokptr = NULL; 395 char *id_octet = NULL; 396 397 // Status code. 398 ipmi_ret_t rc = IPMI_CC_OK; 399 *data_len = 0; 400 401 // Call Get properties method with the interface and property name 402 r = mapper_get_service(bus, objname, &busname); 403 if (r < 0) { 404 log<level::ERR>("Failed to get bus name", 405 entry("BUS=%s", objname), 406 entry("ERRNO=0x%X", -r)); 407 goto finish; 408 } 409 r = sd_bus_call_method(bus,busname,objname,iface, 410 "Get",&error, &reply, "ss", 411 chassis_iface, "uuid"); 412 if (r < 0) 413 { 414 log<level::ERR>("Failed to call Get Method", 415 entry("ERRNO=0x%X", -r)); 416 rc = IPMI_CC_UNSPECIFIED_ERROR; 417 goto finish; 418 } 419 420 r = sd_bus_message_read(reply, "v", "s", &uuid); 421 if (r < 0 || uuid == NULL) 422 { 423 log<level::ERR>("Failed to get a response", 424 entry("ERRNO=0x%X", -r)); 425 rc = IPMI_CC_RESPONSE_ERROR; 426 goto finish; 427 } 428 429 // Traverse the UUID 430 // Get the UUID octects separated by dash 431 id_octet = strtok_r(uuid, "-", &tokptr); 432 433 if (id_octet == NULL) 434 { 435 // Error 436 log<level::ERR>("Unexpected UUID format", 437 entry("UUID=%s", uuid)); 438 rc = IPMI_CC_RESPONSE_ERROR; 439 goto finish; 440 } 441 442 while (id_octet != NULL) 443 { 444 // Calculate the octet string size since it varies 445 // Divide it by 2 for the array size since 1 byte is built from 2 chars 446 int tmp_size = strlen(id_octet)/2; 447 448 for(i = 0; i < tmp_size; i++) 449 { 450 // Holder of the 2 chars that will become a byte 451 char tmp_array[3] = {0}; 452 strncpy(tmp_array, id_octet, 2); // 2 chars at a time 453 454 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte 455 // Copy end to first 456 memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1); 457 resp_loc--; 458 id_octet+=2; // Finished with the 2 chars, advance 459 } 460 id_octet=strtok_r(NULL, "-", &tokptr); // Get next octet 461 } 462 463 // Data length 464 *data_len = resp_size; 465 466 // Pack the actual response 467 memcpy(response, &resp_uuid, *data_len); 468 469 finish: 470 sd_bus_error_free(&error); 471 reply = sd_bus_message_unref(reply); 472 free(busname); 473 474 return rc; 475 } 476 477 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 478 ipmi_request_t request, ipmi_response_t response, 479 ipmi_data_len_t data_len, ipmi_context_t context) 480 { 481 482 // Status code. 483 ipmi_ret_t rc = IPMI_CC_OK; 484 485 // Per IPMI 2.0 spec, the input and output buffer size must be the max 486 // buffer size minus one byte to allocate space for the length byte. 487 uint8_t str[] = {0x01, MAX_IPMI_BUFFER-1, MAX_IPMI_BUFFER-1, 0x0A, 0x01}; 488 489 // Data length 490 *data_len = sizeof(str); 491 492 // Pack the actual response 493 memcpy(response, &str, *data_len); 494 495 return rc; 496 } 497 498 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 499 ipmi_request_t request, ipmi_response_t response, 500 ipmi_data_len_t data_len, ipmi_context_t context) 501 { 502 // Status code. 503 ipmi_ret_t rc = IPMI_CC_INVALID; 504 505 *data_len = strlen("THIS IS WILDCARD"); 506 507 // Now pack actual response 508 memcpy(response, "THIS IS WILDCARD", *data_len); 509 510 return rc; 511 } 512 513 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 514 ipmi_request_t request, ipmi_response_t response, 515 ipmi_data_len_t data_len, ipmi_context_t context) 516 517 { 518 ipmi_ret_t rc = IPMI_CC_OK; 519 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 520 521 try 522 { 523 // Get the Inventory object implementing BMC interface 524 ipmi::DbusObjectInfo bmcObject = 525 ipmi::getDbusObject(bus, bmc_interface); 526 527 // Read UUID property value from bmcObject 528 // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 529 auto variant = ipmi::getDbusProperty( 530 bus, bmcObject.second, bmcObject.first, bmc_guid_interface, 531 bmc_guid_property); 532 std::string guidProp = variant.get<std::string>(); 533 534 // Erase "-" characters from the property value 535 guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'), 536 guidProp.end()); 537 538 auto guidPropLen = guidProp.length(); 539 // Validate UUID data 540 // Divide by 2 as 1 byte is built from 2 chars 541 if ( (guidPropLen <=0) || ((guidPropLen/2) != bmc_guid_len) ) 542 543 { 544 log<level::ERR>("Invalid UUID property value", 545 entry("UUID_LENGTH=%d", guidPropLen)); 546 return IPMI_CC_RESPONSE_ERROR; 547 } 548 549 // Convert data in RFC4122(MSB) format to LSB format 550 // Get 2 characters at a time as 1 byte is built from 2 chars and 551 // convert to hex byte 552 // TODO: Data printed for GUID command is not as per the 553 // GUID format defined in IPMI specification 2.0 section 20.8 554 // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/ 555 uint8_t respGuid[bmc_guid_len]; 556 for (size_t i = 0, respLoc = (bmc_guid_len - 1); 557 i < guidPropLen && respLoc >= 0; i += 2, respLoc--) 558 { 559 auto value = static_cast<uint8_t>( 560 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16)); 561 respGuid[respLoc] = value; 562 } 563 564 *data_len = bmc_guid_len; 565 memcpy(response, &respGuid, bmc_guid_len); 566 } 567 catch (const InternalFailure& e) 568 { 569 log<level::ERR>("Failed in reading BMC UUID property", 570 entry("INTERFACE=%s", bmc_interface), 571 entry("PROPERTY_INTERFACE=%s", bmc_guid_interface), 572 entry("PROPERTY=%s", bmc_guid_property)); 573 return IPMI_CC_UNSPECIFIED_ERROR; 574 } 575 return rc; 576 } 577 578 void register_netfn_app_functions() 579 { 580 // <Get BT Interface Capabilities> 581 ipmi_register_callback(NETFUN_APP, 582 IPMI_CMD_GET_CAP_BIT, 583 NULL, 584 ipmi_app_get_bt_capabilities, 585 PRIVILEGE_USER); 586 587 // <Wildcard Command> 588 ipmi_register_callback(NETFUN_APP, 589 IPMI_CMD_WILDCARD, 590 NULL, 591 ipmi_app_wildcard_handler, 592 PRIVILEGE_USER); 593 594 // <Reset Watchdog Timer> 595 ipmi_register_callback(NETFUN_APP, 596 IPMI_CMD_RESET_WD, 597 NULL, 598 ipmi_app_watchdog_reset, 599 PRIVILEGE_OPERATOR); 600 601 // <Set Watchdog Timer> 602 ipmi_register_callback(NETFUN_APP, 603 IPMI_CMD_SET_WD, 604 NULL, 605 ipmi_app_watchdog_set, 606 PRIVILEGE_OPERATOR); 607 608 // <Get Watchdog Timer> 609 ipmi_register_callback(NETFUN_APP, 610 IPMI_CMD_GET_WD, 611 NULL, 612 ipmi_app_watchdog_get, 613 PRIVILEGE_OPERATOR); 614 615 // <Get Device ID> 616 ipmi_register_callback(NETFUN_APP, 617 IPMI_CMD_GET_DEVICE_ID, 618 NULL, 619 ipmi_app_get_device_id, 620 PRIVILEGE_USER); 621 622 // <Get Self Test Results> 623 ipmi_register_callback(NETFUN_APP, 624 IPMI_CMD_GET_SELF_TEST_RESULTS, 625 NULL, 626 ipmi_app_get_self_test_results, 627 PRIVILEGE_USER); 628 629 // <Get Device GUID> 630 ipmi_register_callback(NETFUN_APP, 631 IPMI_CMD_GET_DEVICE_GUID, 632 NULL, 633 ipmi_app_get_device_guid, 634 PRIVILEGE_USER); 635 636 // <Set ACPI Power State> 637 ipmi_register_callback(NETFUN_APP, 638 IPMI_CMD_SET_ACPI, 639 NULL, 640 ipmi_app_set_acpi_power_state, 641 PRIVILEGE_ADMIN); 642 643 // <Get Channel Access> 644 ipmi_register_callback(NETFUN_APP, 645 IPMI_CMD_GET_CHANNEL_ACCESS, 646 NULL, 647 ipmi_get_channel_access, 648 PRIVILEGE_USER); 649 650 // <Get Channel Info Command> 651 ipmi_register_callback(NETFUN_APP, 652 IPMI_CMD_GET_CHAN_INFO, 653 NULL, 654 ipmi_app_channel_info, 655 PRIVILEGE_USER); 656 657 // <Get System GUID Command> 658 ipmi_register_callback(NETFUN_APP, 659 IPMI_CMD_GET_SYS_GUID, 660 NULL, 661 ipmi_app_get_sys_guid, 662 PRIVILEGE_USER); 663 664 // <Get Channel Cipher Suites Command> 665 ipmi_register_callback(NETFUN_APP, 666 IPMI_CMD_GET_CHAN_CIPHER_SUITES, 667 NULL, 668 getChannelCipherSuites, 669 PRIVILEGE_CALLBACK); 670 return; 671 } 672 673