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 28 extern sd_bus *bus; 29 30 constexpr auto app_obj = "/org/openbmc/NetworkManager/Interface"; 31 constexpr auto app_ifc = "org.openbmc.NetworkManager"; 32 constexpr auto app_nwinterface = "eth0"; 33 34 void register_netfn_app_functions() __attribute__((constructor)); 35 36 using namespace phosphor::logging; 37 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 38 namespace fs = std::experimental::filesystem; 39 40 // Offset in get device id command. 41 typedef struct 42 { 43 uint8_t id; 44 uint8_t revision; 45 uint8_t fw[2]; 46 uint8_t ipmi_ver; 47 uint8_t addn_dev_support; 48 uint8_t manuf_id[3]; 49 uint8_t prod_id[2]; 50 uint8_t aux[4]; 51 }__attribute__((packed)) ipmi_device_id_t; 52 53 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 54 ipmi_request_t request, ipmi_response_t response, 55 ipmi_data_len_t data_len, ipmi_context_t context) 56 { 57 ipmi_ret_t rc = IPMI_CC_OK; 58 *data_len = 0; 59 60 printf("IPMI SET ACPI STATE Ignoring for now\n"); 61 return rc; 62 } 63 64 typedef struct 65 { 66 char major; 67 char minor; 68 uint16_t d[2]; 69 } rev_t; 70 71 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */ 72 /* return -1 if not in those formats, this routine knows how to parse */ 73 /* version = v0.6-19-gf363f61-dirty */ 74 /* ^ ^ ^^ ^ */ 75 /* | | |----------|-- additional details */ 76 /* | |---------------- Minor */ 77 /* |------------------ Major */ 78 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */ 79 /* ^ ^ ^^ ^ */ 80 /* | | |--|---------- additional details */ 81 /* | |---------------- Minor */ 82 /* |------------------ Major */ 83 /* Additional details : If the option group exists it will force Auxiliary */ 84 /* Firmware Revision Information 4th byte to 1 indicating the build was */ 85 /* derived with additional edits */ 86 int convert_version(const char * p, rev_t *rev) 87 { 88 std::string s(p); 89 std::string token; 90 uint16_t commits; 91 92 auto location = s.find_first_of('v'); 93 if (location != std::string::npos) 94 { 95 s = s.substr(location+1); 96 } 97 98 if (!s.empty()) 99 { 100 location = s.find_first_of("."); 101 if (location != std::string::npos) 102 { 103 rev->major = static_cast<char>(std::stoi(s.substr(0, location), 0, 16)); 104 token = s.substr(location+1); 105 } 106 107 if (!token.empty()) 108 { 109 location = token.find_first_of(".-"); 110 if (location != std::string::npos) 111 { 112 rev->minor = static_cast<char>(std::stoi(token.substr(0, location), 0, 16)); 113 token = token.substr(location+1); 114 } 115 } 116 117 // Capture the number of commits on top of the minor tag. 118 // I'm using BE format like the ipmi spec asked for 119 location = token.find_first_of(".-"); 120 if (!token.empty()) 121 { 122 commits = std::stoi(token.substr(0, location), 0, 16); 123 rev->d[0] = (commits>>8) | (commits<<8); 124 125 // commit number we skip 126 location = token.find_first_of(".-"); 127 if (location != std::string::npos) 128 { 129 token = token.substr(location+1); 130 } 131 } 132 else { 133 rev->d[0] = 0; 134 } 135 136 if (location != std::string::npos) 137 { 138 token = token.substr(location+1); 139 } 140 141 // Any value of the optional parameter forces it to 1 142 location = token.find_first_of(".-"); 143 if (location != std::string::npos) 144 { 145 token = token.substr(location+1); 146 } 147 commits = (!token.empty()) ? 1 : 0; 148 149 //We do this operation to get this displayed in least significant bytes 150 //of ipmitool device id command. 151 rev->d[1] = (commits>>8) | (commits<<8); 152 } 153 154 return 0; 155 } 156 157 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 158 ipmi_request_t request, ipmi_response_t response, 159 ipmi_data_len_t data_len, ipmi_context_t context) 160 { 161 ipmi_ret_t rc = IPMI_CC_OK; 162 const char *objname = "/org/openbmc/inventory/system/chassis/motherboard/bmc"; 163 const char *iface = "org.openbmc.InventoryItem"; 164 char *ver = NULL; 165 char *busname = NULL; 166 int r; 167 rev_t rev = {0}; 168 static ipmi_device_id_t dev_id{}; 169 static bool dev_id_initialized = false; 170 const char* filename = "/usr/share/ipmi-providers/dev_id.json"; 171 172 // Data length 173 *data_len = sizeof(dev_id); 174 175 if (!dev_id_initialized) 176 { 177 // Firmware revision is already implemented, 178 // so get it from appropriate position. 179 r = mapper_get_service(bus, objname, &busname); 180 if (r < 0) { 181 fprintf(stderr, "Failed to get %s bus name: %s\n", 182 objname, strerror(-r)); 183 goto finish; 184 } 185 r = sd_bus_get_property_string(bus,busname,objname,iface,"version", 186 NULL, &ver); 187 if ( r < 0 ) { 188 fprintf(stderr, "Failed to obtain version property: %s\n", 189 strerror(-r)); 190 } else { 191 r = convert_version(ver, &rev); 192 if( r >= 0 ) { 193 // bit7 identifies if the device is available 194 // 0=normal operation 195 // 1=device firmware, SDR update, 196 // or self-initialization in progress. 197 // our SDR is normal working condition, so mask: 198 dev_id.fw[0] = 0x7F & rev.major; 199 200 rev.minor = (rev.minor > 99 ? 99 : rev.minor); 201 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; 202 memcpy(&dev_id.aux, rev.d, 4); 203 } 204 } 205 206 // IPMI Spec version 2.0 207 dev_id.ipmi_ver = 2; 208 209 std::ifstream dev_id_file(filename); 210 if (dev_id_file.is_open()) 211 { 212 auto data = nlohmann::json::parse(dev_id_file, nullptr, false); 213 if (!data.is_discarded()) 214 { 215 dev_id.id = data.value("id", 0); 216 dev_id.revision = data.value("revision", 0); 217 dev_id.addn_dev_support = data.value("addn_dev_support", 0); 218 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16; 219 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8; 220 dev_id.manuf_id[0] = data.value("manuf_id", 0); 221 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8; 222 dev_id.prod_id[0] = data.value("prod_id", 0); 223 dev_id.aux[3] = data.value("aux", 0) >> 24; 224 dev_id.aux[2] = data.value("aux", 0) >> 16; 225 dev_id.aux[1] = data.value("aux", 0) >> 8; 226 dev_id.aux[0] = data.value("aux", 0); 227 228 //Don't read the file every time if successful 229 dev_id_initialized = true; 230 } 231 else 232 { 233 log<level::ERR>("Device ID JSON parser failure"); 234 rc = IPMI_CC_UNSPECIFIED_ERROR; 235 } 236 } 237 else 238 { 239 log<level::ERR>("Device ID file not found"); 240 rc = IPMI_CC_UNSPECIFIED_ERROR; 241 } 242 } 243 244 // Pack the actual response 245 memcpy(response, &dev_id, *data_len); 246 finish: 247 free(busname); 248 free(ver); 249 return rc; 250 } 251 252 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 253 ipmi_request_t request, ipmi_response_t response, 254 ipmi_data_len_t data_len, ipmi_context_t context) 255 { 256 ipmi_ret_t rc = IPMI_CC_OK; 257 258 // Byte 2: 259 // 55h - No error. 260 // 56h - Self Test function not implemented in this controller. 261 // 57h - Corrupted or inaccesssible data or devices. 262 // 58h - Fatal hardware error. 263 // FFh - reserved. 264 // all other: Device-specific 'internal failure'. 265 // Byte 3: 266 // For byte 2 = 55h, 56h, FFh: 00h 267 // For byte 2 = 58h, all other: Device-specific 268 // For byte 2 = 57h: self-test error bitfield. 269 // Note: returning 57h does not imply that all test were run. 270 // [7] 1b = Cannot access SEL device. 271 // [6] 1b = Cannot access SDR Repository. 272 // [5] 1b = Cannot access BMC FRU device. 273 // [4] 1b = IPMB signal lines do not respond. 274 // [3] 1b = SDR Repository empty. 275 // [2] 1b = Internal Use Area of BMC FRU corrupted. 276 // [1] 1b = controller update 'boot block' firmware corrupted. 277 // [0] 1b = controller operational firmware corrupted. 278 279 char selftestresults[2] = {0}; 280 281 *data_len = 2; 282 283 selftestresults[0] = 0x56; 284 selftestresults[1] = 0; 285 286 memcpy(response, selftestresults, *data_len); 287 288 return rc; 289 } 290 291 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 292 ipmi_request_t request, ipmi_response_t response, 293 ipmi_data_len_t data_len, ipmi_context_t context) 294 { 295 const char *objname = "/org/openbmc/control/chassis0"; 296 const char *iface = "org.freedesktop.DBus.Properties"; 297 const char *chassis_iface = "org.openbmc.control.Chassis"; 298 sd_bus_message *reply = NULL; 299 sd_bus_error error = SD_BUS_ERROR_NULL; 300 int r = 0; 301 char *uuid = NULL; 302 char *busname = NULL; 303 304 // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 305 // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte order 306 // Ex: 0x2332fc2c40e66298e511f2782395a361 307 308 const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec 309 uint8_t resp_uuid[resp_size]; // Array to hold the formatted response 310 int resp_loc = resp_size-1; // Point resp end of array to save in reverse order 311 int i = 0; 312 char *tokptr = NULL; 313 char *id_octet = NULL; 314 315 // Status code. 316 ipmi_ret_t rc = IPMI_CC_OK; 317 *data_len = 0; 318 319 printf("IPMI GET DEVICE GUID\n"); 320 321 // Call Get properties method with the interface and property name 322 r = mapper_get_service(bus, objname, &busname); 323 if (r < 0) { 324 fprintf(stderr, "Failed to get %s bus name: %s\n", 325 objname, strerror(-r)); 326 goto finish; 327 } 328 r = sd_bus_call_method(bus,busname,objname,iface, 329 "Get",&error, &reply, "ss", 330 chassis_iface, "uuid"); 331 if (r < 0) 332 { 333 fprintf(stderr, "Failed to call Get Method: %s\n", strerror(-r)); 334 rc = IPMI_CC_UNSPECIFIED_ERROR; 335 goto finish; 336 } 337 338 r = sd_bus_message_read(reply, "v", "s", &uuid); 339 if (r < 0 || uuid == NULL) 340 { 341 fprintf(stderr, "Failed to get a response: %s", strerror(-r)); 342 rc = IPMI_CC_RESPONSE_ERROR; 343 goto finish; 344 } 345 346 // Traverse the UUID 347 id_octet = strtok_r(uuid, "-", &tokptr); // Get the UUID octects separated by dash 348 349 if (id_octet == NULL) 350 { 351 // Error 352 fprintf(stderr, "Unexpected UUID format: %s", uuid); 353 rc = IPMI_CC_RESPONSE_ERROR; 354 goto finish; 355 } 356 357 while (id_octet != NULL) 358 { 359 // Calculate the octet string size since it varies 360 // Divide it by 2 for the array size since 1 byte is built from 2 chars 361 int tmp_size = strlen(id_octet)/2; 362 363 for(i = 0; i < tmp_size; i++) 364 { 365 char tmp_array[3] = {0}; // Holder of the 2 chars that will become a byte 366 strncpy(tmp_array, id_octet, 2); // 2 chars at a time 367 368 int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte 369 memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1); // Copy end to first 370 resp_loc--; 371 id_octet+=2; // Finished with the 2 chars, advance 372 } 373 id_octet=strtok_r(NULL, "-", &tokptr); // Get next octet 374 } 375 376 // Data length 377 *data_len = resp_size; 378 379 // Pack the actual response 380 memcpy(response, &resp_uuid, *data_len); 381 382 finish: 383 sd_bus_error_free(&error); 384 reply = sd_bus_message_unref(reply); 385 free(busname); 386 387 return rc; 388 } 389 390 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 391 ipmi_request_t request, ipmi_response_t response, 392 ipmi_data_len_t data_len, ipmi_context_t context) 393 { 394 printf("Handling Netfn:[0x%X], Cmd:[0x%X]\n",netfn,cmd); 395 396 // Status code. 397 ipmi_ret_t rc = IPMI_CC_OK; 398 399 // Per IPMI 2.0 spec, the input and output buffer size must be the max 400 // buffer size minus one byte to allocate space for the length byte. 401 uint8_t str[] = {0x01, MAX_IPMI_BUFFER-1, MAX_IPMI_BUFFER-1, 0x0A, 0x01}; 402 403 // Data length 404 *data_len = sizeof(str); 405 406 // Pack the actual response 407 memcpy(response, &str, *data_len); 408 409 return rc; 410 } 411 412 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 413 ipmi_request_t request, ipmi_response_t response, 414 ipmi_data_len_t data_len, ipmi_context_t context) 415 { 416 printf("Handling WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd); 417 418 // Status code. 419 ipmi_ret_t rc = IPMI_CC_INVALID; 420 421 *data_len = strlen("THIS IS WILDCARD"); 422 423 // Now pack actual response 424 memcpy(response, "THIS IS WILDCARD", *data_len); 425 426 return rc; 427 } 428 429 void register_netfn_app_functions() 430 { 431 // <Get BT Interface Capabilities> 432 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CAP_BIT); 433 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, ipmi_app_get_bt_capabilities, 434 PRIVILEGE_USER); 435 436 // <Wildcard Command> 437 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_WILDCARD); 438 ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL, ipmi_app_wildcard_handler, 439 PRIVILEGE_USER); 440 441 // <Reset Watchdog Timer> 442 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_RESET_WD); 443 ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL, ipmi_app_reset_watchdog, 444 PRIVILEGE_OPERATOR); 445 446 // <Set Watchdog Timer> 447 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_SET_WD); 448 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL, ipmi_app_set_watchdog, 449 PRIVILEGE_OPERATOR); 450 451 // <Get Device ID> 452 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_DEVICE_ID); 453 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL, ipmi_app_get_device_id, 454 PRIVILEGE_USER); 455 456 // <Get Self Test Results> 457 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS); 458 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL, 459 ipmi_app_get_self_test_results, PRIVILEGE_USER); 460 461 // <Get Device GUID> 462 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID); 463 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL, ipmi_app_get_device_guid, 464 PRIVILEGE_USER); 465 466 // <Set ACPI Power State> 467 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_SET_ACPI); 468 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, ipmi_app_set_acpi_power_state, 469 PRIVILEGE_ADMIN); 470 471 // <Set Channel Access> 472 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, 473 IPMI_CMD_SET_CHAN_ACCESS); 474 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL, 475 ipmi_set_channel_access, PRIVILEGE_ADMIN); 476 477 // <Get Channel Access> 478 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS); 479 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, 480 ipmi_get_channel_access, PRIVILEGE_USER); 481 482 // <Get Channel Info Command> 483 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CHAN_INFO); 484 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, ipmi_app_channel_info, 485 PRIVILEGE_USER); 486 487 return; 488 } 489 490 491