1 #include <cstdio> 2 #include <string> 3 #include <arpa/inet.h> 4 #include <systemd/sd-bus.h> 5 #include <mapper.h> 6 #include <chrono> 7 #include "selutility.hpp" 8 #include <algorithm> 9 #include "storagehandler.h" 10 #include "storageaddsel.h" 11 #include "utils.hpp" 12 #include "host-ipmid/ipmid-api.h" 13 #include <experimental/filesystem> 14 #include <phosphor-logging/log.hpp> 15 #include <sdbusplus/server.hpp> 16 #include "xyz/openbmc_project/Common/error.hpp" 17 #include "read_fru_data.hpp" 18 #include <phosphor-logging/elog-errors.hpp> 19 20 void register_netfn_storage_functions() __attribute__((constructor)); 21 22 unsigned int g_sel_time = 0xFFFFFFFF; 23 extern unsigned short g_sel_reserve; 24 25 constexpr auto time_manager_intf = "org.openbmc.TimeManager"; 26 constexpr auto time_manager_obj = "/org/openbmc/TimeManager"; 27 28 namespace cache 29 { 30 /* 31 * This cache contains the object paths of the logging entries sorted in the 32 * order of the filename(numeric order). The cache is initialized by 33 * invoking readLoggingObjectPaths with the cache as the parameter. The 34 * cache is invoked in the execution of the Get SEL info and Delete SEL 35 * entry command. The Get SEL Info command is typically invoked before the 36 * Get SEL entry command, so the cache is utilized for responding to Get SEL 37 * entry command. The cache is invalidated by clearing after Delete SEL 38 * entry and Clear SEL command. 39 */ 40 ipmi::sel::ObjectPaths paths; 41 42 } // namespace objectPathsCache 43 44 using InternalFailure = 45 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 46 using namespace phosphor::logging; 47 using namespace ipmi::fru; 48 49 /** 50 * @enum Device access mode 51 */ 52 enum class AccessMode 53 { 54 bytes, ///< Device is accessed by bytes 55 words ///< Device is accessed by words 56 }; 57 58 59 ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 60 ipmi_request_t request, ipmi_response_t response, 61 ipmi_data_len_t data_len, ipmi_context_t context) 62 { 63 printf("Handling STORAGE WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd); 64 // Status code. 65 ipmi_ret_t rc = IPMI_CC_INVALID; 66 *data_len = 0; 67 return rc; 68 } 69 70 ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 71 ipmi_request_t request, ipmi_response_t response, 72 ipmi_data_len_t data_len, ipmi_context_t context) 73 { 74 std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse)); 75 auto responseData = reinterpret_cast<ipmi::sel::GetSELInfoResponse*> 76 (outPayload.data()); 77 78 responseData->selVersion = ipmi::sel::selVersion; 79 // Last erase timestamp is not available from log manager. 80 responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp; 81 responseData->operationSupport = ipmi::sel::operationSupport; 82 83 ipmi::sel::readLoggingObjectPaths(cache::paths); 84 responseData->entries = 0; 85 responseData->addTimeStamp = ipmi::sel::invalidTimeStamp; 86 87 if (!cache::paths.empty()) 88 { 89 responseData->entries = static_cast<uint16_t>(cache::paths.size()); 90 91 try 92 { 93 responseData->addTimeStamp = static_cast<uint32_t>( 94 (ipmi::sel::getEntryTimeStamp(cache::paths.back()) 95 .count())); 96 } 97 catch (InternalFailure& e) 98 { 99 } 100 catch (const std::runtime_error& e) 101 { 102 log<level::ERR>(e.what()); 103 } 104 } 105 106 memcpy(response, outPayload.data(), outPayload.size()); 107 *data_len = outPayload.size(); 108 109 return IPMI_CC_OK; 110 } 111 112 ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 113 ipmi_request_t request, ipmi_response_t response, 114 ipmi_data_len_t data_len, ipmi_context_t context) 115 { 116 auto requestData = reinterpret_cast<const ipmi::sel::GetSELEntryRequest*> 117 (request); 118 119 if (requestData->reservationID != 0) 120 { 121 if (g_sel_reserve != requestData->reservationID) 122 { 123 *data_len = 0; 124 return IPMI_CC_INVALID_RESERVATION_ID; 125 } 126 } 127 128 if (cache::paths.empty()) 129 { 130 *data_len = 0; 131 return IPMI_CC_SENSOR_INVALID; 132 } 133 134 ipmi::sel::ObjectPaths::const_iterator iter; 135 136 // Check for the requested SEL Entry. 137 if (requestData->selRecordID == ipmi::sel::firstEntry) 138 { 139 iter = cache::paths.begin(); 140 } 141 else if (requestData->selRecordID == ipmi::sel::lastEntry) 142 { 143 iter = cache::paths.end(); 144 } 145 else 146 { 147 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" + 148 std::to_string(requestData->selRecordID); 149 150 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath); 151 if (iter == cache::paths.end()) 152 { 153 *data_len = 0; 154 return IPMI_CC_SENSOR_INVALID; 155 } 156 } 157 158 ipmi::sel::GetSELEntryResponse record {}; 159 160 // Convert the log entry into SEL record. 161 try 162 { 163 record = ipmi::sel::convertLogEntrytoSEL(*iter); 164 } 165 catch (InternalFailure& e) 166 { 167 *data_len = 0; 168 return IPMI_CC_UNSPECIFIED_ERROR; 169 } 170 catch (const std::runtime_error& e) 171 { 172 log<level::ERR>(e.what()); 173 *data_len = 0; 174 return IPMI_CC_UNSPECIFIED_ERROR; 175 } 176 177 178 // Identify the next SEL record ID 179 if(iter != cache::paths.end()) 180 { 181 ++iter; 182 if (iter == cache::paths.end()) 183 { 184 record.nextRecordID = ipmi::sel::lastEntry; 185 } 186 else 187 { 188 namespace fs = std::experimental::filesystem; 189 fs::path path(*iter); 190 record.nextRecordID = static_cast<uint16_t> 191 (std::stoul(std::string(path.filename().c_str()))); 192 } 193 } 194 else 195 { 196 record.nextRecordID = ipmi::sel::lastEntry; 197 } 198 199 if (requestData->readLength == ipmi::sel::entireRecord) 200 { 201 memcpy(response, &record, sizeof(record)); 202 *data_len = sizeof(record); 203 } 204 else 205 { 206 if (requestData->offset >= ipmi::sel::selRecordSize || 207 requestData->readLength > ipmi::sel::selRecordSize) 208 { 209 *data_len = 0; 210 return IPMI_CC_INVALID_FIELD_REQUEST; 211 } 212 213 auto diff = ipmi::sel::selRecordSize - requestData->offset; 214 auto readLength = std::min(diff, 215 static_cast<int>(requestData->readLength)); 216 217 memcpy(response, &record.nextRecordID, sizeof(record.nextRecordID)); 218 memcpy(static_cast<uint8_t*>(response) + sizeof(record.nextRecordID), 219 &record.recordID + requestData->offset, readLength); 220 *data_len = sizeof(record.nextRecordID) + readLength; 221 } 222 223 return IPMI_CC_OK; 224 } 225 226 ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 227 ipmi_request_t request, ipmi_response_t response, 228 ipmi_data_len_t data_len, ipmi_context_t context) 229 { 230 namespace fs = std::experimental::filesystem; 231 auto requestData = reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*> 232 (request); 233 234 if (g_sel_reserve != requestData->reservationID) 235 { 236 *data_len = 0; 237 return IPMI_CC_INVALID_RESERVATION_ID; 238 } 239 240 ipmi::sel::readLoggingObjectPaths(cache::paths); 241 242 if (cache::paths.empty()) 243 { 244 *data_len = 0; 245 return IPMI_CC_SENSOR_INVALID; 246 } 247 248 ipmi::sel::ObjectPaths::const_iterator iter; 249 uint16_t delRecordID = 0; 250 251 if (requestData->selRecordID == ipmi::sel::firstEntry) 252 { 253 iter = cache::paths.begin(); 254 fs::path path(*iter); 255 delRecordID = static_cast<uint16_t> 256 (std::stoul(std::string(path.filename().c_str()))); 257 } 258 else if (requestData->selRecordID == ipmi::sel::lastEntry) 259 { 260 iter = cache::paths.end(); 261 fs::path path(*iter); 262 delRecordID = static_cast<uint16_t> 263 (std::stoul(std::string(path.filename().c_str()))); 264 } 265 else 266 { 267 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" + 268 std::to_string(requestData->selRecordID); 269 270 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath); 271 if (iter == cache::paths.end()) 272 { 273 *data_len = 0; 274 return IPMI_CC_SENSOR_INVALID; 275 } 276 delRecordID = requestData->selRecordID; 277 } 278 279 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 280 std::string service; 281 282 try 283 { 284 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter); 285 } 286 catch (const std::runtime_error& e) 287 { 288 log<level::ERR>(e.what()); 289 *data_len = 0; 290 return IPMI_CC_UNSPECIFIED_ERROR; 291 } 292 293 auto methodCall = bus.new_method_call(service.c_str(), 294 (*iter).c_str(), 295 ipmi::sel::logDeleteIntf, 296 "Delete"); 297 auto reply = bus.call(methodCall); 298 if (reply.is_method_error()) 299 { 300 *data_len = 0; 301 return IPMI_CC_UNSPECIFIED_ERROR; 302 } 303 304 // Invalidate the cache of dbus entry objects. 305 cache::paths.clear(); 306 memcpy(response, &delRecordID, sizeof(delRecordID)); 307 *data_len = sizeof(delRecordID); 308 309 return IPMI_CC_OK; 310 } 311 312 ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 313 ipmi_response_t response, ipmi_data_len_t data_len, 314 ipmi_context_t context) 315 { 316 auto requestData = reinterpret_cast<const ipmi::sel::ClearSELRequest*> 317 (request); 318 319 if (g_sel_reserve != requestData->reservationID) 320 { 321 *data_len = 0; 322 return IPMI_CC_INVALID_RESERVATION_ID; 323 } 324 325 if (requestData->charC != 'C' || 326 requestData->charL != 'L' || 327 requestData->charR != 'R') 328 { 329 *data_len = 0; 330 return IPMI_CC_INVALID_FIELD_REQUEST; 331 } 332 333 uint8_t eraseProgress = ipmi::sel::eraseComplete; 334 335 /* 336 * Erasure status cannot be fetched from DBUS, so always return erasure 337 * status as `erase completed`. 338 */ 339 if (requestData->eraseOperation == ipmi::sel::getEraseStatus) 340 { 341 memcpy(response, &eraseProgress, sizeof(eraseProgress)); 342 *data_len = sizeof(eraseProgress); 343 return IPMI_CC_OK; 344 } 345 346 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 347 auto depth = 0; 348 349 auto mapperCall = bus.new_method_call(ipmi::sel::mapperBusName, 350 ipmi::sel::mapperObjPath, 351 ipmi::sel::mapperIntf, 352 "GetSubTreePaths"); 353 mapperCall.append(ipmi::sel::logBasePath); 354 mapperCall.append(depth); 355 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf})); 356 357 auto reply = bus.call(mapperCall); 358 if (reply.is_method_error()) 359 { 360 memcpy(response, &eraseProgress, sizeof(eraseProgress)); 361 *data_len = sizeof(eraseProgress); 362 return IPMI_CC_OK; 363 } 364 365 ipmi::sel::ObjectPaths objectPaths; 366 reply.read(objectPaths); 367 if (objectPaths.empty()) 368 { 369 memcpy(response, &eraseProgress, sizeof(eraseProgress)); 370 *data_len = sizeof(eraseProgress); 371 return IPMI_CC_OK; 372 } 373 374 std::string service; 375 376 try 377 { 378 service = ipmi::getService(bus, 379 ipmi::sel::logDeleteIntf, 380 objectPaths.front()); 381 } 382 catch (const std::runtime_error& e) 383 { 384 log<level::ERR>(e.what()); 385 *data_len = 0; 386 return IPMI_CC_UNSPECIFIED_ERROR; 387 } 388 389 for (const auto& iter : objectPaths) 390 { 391 auto methodCall = bus.new_method_call(service.c_str(), 392 iter.c_str(), 393 ipmi::sel::logDeleteIntf, 394 "Delete"); 395 396 auto reply = bus.call(methodCall); 397 if (reply.is_method_error()) 398 { 399 *data_len = 0; 400 return IPMI_CC_UNSPECIFIED_ERROR; 401 } 402 } 403 404 // Invalidate the cache of dbus entry objects. 405 cache::paths.clear(); 406 memcpy(response, &eraseProgress, sizeof(eraseProgress)); 407 *data_len = sizeof(eraseProgress); 408 return IPMI_CC_OK; 409 } 410 411 ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 412 ipmi_request_t request, ipmi_response_t response, 413 ipmi_data_len_t data_len, ipmi_context_t context) 414 { 415 using namespace std::chrono; 416 417 char *time_provider = nullptr; 418 const char* time_in_str = nullptr; 419 uint64_t host_time_usec = 0; 420 uint32_t resp = 0; 421 ipmi_ret_t rc = IPMI_CC_OK; 422 423 sd_bus_message *reply = nullptr; 424 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 425 426 printf("IPMI Handling GET-SEL-TIME\n"); 427 428 auto bus = ipmid_get_sd_bus_connection(); 429 430 auto rct = mapper_get_service(bus, time_manager_obj, &time_provider); 431 if (rct < 0) { 432 printf("Error [%s] getting bus name for time provider\n", 433 strerror(-rct)); 434 rc = IPMI_CC_UNSPECIFIED_ERROR; 435 goto finish; 436 } 437 438 rct = sd_bus_call_method(bus, 439 time_provider, 440 time_manager_obj, 441 time_manager_intf, 442 "GetTime", 443 &bus_error, 444 &reply, 445 "s", 446 "host"); 447 if (rct < 0) { 448 printf("Error [%s] getting time\n", strerror(-rct)); 449 rc = IPMI_CC_UNSPECIFIED_ERROR; 450 goto finish; 451 } 452 453 rct = sd_bus_message_read(reply, "sx", &time_in_str, &host_time_usec); 454 if (rct < 0) { 455 fprintf(stderr, "Error [%s] parsing get-time response\n", 456 strerror(-rct)); 457 rc = IPMI_CC_UNSPECIFIED_ERROR; 458 goto finish; 459 } 460 461 // Time is really long int but IPMI wants just uint32. This works okay until 462 // the number of seconds since 1970 overflows uint32 size.. Still a whole 463 // lot of time here to even think about that. 464 resp = duration_cast<seconds>(microseconds(host_time_usec)).count(); 465 resp = htole32(resp); 466 printf("Host Time read:[%s] :: [%d]\n", time_in_str, resp); 467 468 // From the IPMI Spec 2.0, response should be a 32-bit value 469 *data_len = sizeof(resp); 470 471 // Pack the actual response 472 memcpy(response, &resp, *data_len); 473 474 finish: 475 sd_bus_error_free(&bus_error); 476 reply = sd_bus_message_unref(reply); 477 free(time_provider); 478 return rc; 479 } 480 481 ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 482 ipmi_request_t request, ipmi_response_t response, 483 ipmi_data_len_t data_len, ipmi_context_t context) 484 { 485 char *time_provider = nullptr; 486 int time_rc = 0; 487 ipmi_ret_t rc = IPMI_CC_OK; 488 489 sd_bus_message *reply = nullptr; 490 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 491 492 uint32_t* secs = (uint32_t*)request; 493 *data_len = 0; 494 495 printf("Handling Set-SEL-Time:[0x%X], Cmd:[0x%X]\n",netfn, cmd); 496 printf("Data: 0x%X]\n",*secs); 497 498 auto bus = ipmid_get_sd_bus_connection(); 499 500 auto rct = mapper_get_service(bus, time_manager_obj, &time_provider); 501 if (rct < 0) { 502 printf("Error [%s] getting bus name for time provider\n", 503 strerror(-rct)); 504 rc = IPMI_CC_UNSPECIFIED_ERROR; 505 goto finish; 506 } 507 508 rct = sd_bus_call_method(bus, 509 time_provider, 510 time_manager_obj, 511 time_manager_intf, 512 "SetTime", 513 &bus_error, 514 &reply, 515 "ss", 516 "host", 517 std::to_string(le32toh(*secs)).c_str()); 518 519 if (rct < 0) { 520 printf("Error [%s] setting time\n", strerror(-rct)); 521 rc = IPMI_CC_UNSPECIFIED_ERROR; 522 goto finish; 523 } 524 525 rct = sd_bus_message_read(reply, "i", &time_rc); 526 if (rct < 0) { 527 fprintf(stderr, "Error [%s] parsing set-time response\n", 528 strerror(-rct)); 529 rc = IPMI_CC_UNSPECIFIED_ERROR; 530 goto finish; 531 } 532 533 if (time_rc < 0) { 534 printf("Error setting time."); 535 rc = IPMI_CC_UNSPECIFIED_ERROR; 536 } 537 538 finish: 539 sd_bus_error_free(&bus_error); 540 reply = sd_bus_message_unref(reply); 541 free(time_provider); 542 return rc; 543 } 544 545 ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 546 ipmi_request_t request, ipmi_response_t response, 547 ipmi_data_len_t data_len, ipmi_context_t context) 548 { 549 ipmi_ret_t rc = IPMI_CC_OK; 550 551 // IPMI spec, Reservation ID, the value simply increases against each execution of reserve_sel command. 552 if( ++g_sel_reserve == 0) 553 g_sel_reserve = 1; 554 555 printf("IPMI Handling RESERVE-SEL 0x%04x\n", g_sel_reserve); 556 557 *data_len = sizeof(g_sel_reserve); 558 559 // Pack the actual response 560 memcpy(response, &g_sel_reserve, *data_len); 561 562 return rc; 563 } 564 565 ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 566 ipmi_request_t request, ipmi_response_t response, 567 ipmi_data_len_t data_len, ipmi_context_t context) 568 { 569 570 ipmi_ret_t rc = IPMI_CC_OK; 571 ipmi_add_sel_request_t *p = (ipmi_add_sel_request_t*) request; 572 uint16_t recordid; 573 574 recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2]; 575 576 printf("IPMI Handling ADD-SEL for record 0x%04x\n", recordid); 577 578 *data_len = sizeof(g_sel_reserve); 579 580 // Pack the actual response 581 memcpy(response, &p->eventdata[1], 2); 582 583 send_esel(recordid); 584 585 return rc; 586 } 587 588 //Read FRU info area 589 ipmi_ret_t ipmi_storage_get_fru_inv_area_info( 590 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 591 ipmi_response_t response, ipmi_data_len_t data_len, 592 ipmi_context_t context) 593 { 594 ipmi_ret_t rc = IPMI_CC_OK; 595 const FruInvenAreaInfoRequest* reqptr = 596 reinterpret_cast<const FruInvenAreaInfoRequest*>(request); 597 try 598 { 599 const auto& fruArea = getFruAreaData(reqptr->fruID); 600 auto size = static_cast<uint16_t>(fruArea.size()); 601 FruInvenAreaInfoResponse resp; 602 resp.sizems = size >> 8; 603 resp.sizels = size; 604 resp.access = static_cast<uint8_t>(AccessMode::bytes); 605 606 *data_len = sizeof(resp); 607 608 // Pack the actual response 609 memcpy(response, &resp, *data_len); 610 } 611 catch(const InternalFailure& e) 612 { 613 rc = IPMI_CC_UNSPECIFIED_ERROR; 614 *data_len = 0; 615 log<level::ERR>(e.what()); 616 report<InternalFailure>(); 617 } 618 return rc; 619 } 620 621 //Read FRU data 622 ipmi_ret_t ipmi_storage_read_fru_data( 623 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 624 ipmi_response_t response, ipmi_data_len_t data_len, 625 ipmi_context_t context) 626 { 627 ipmi_ret_t rc = IPMI_CC_OK; 628 const ReadFruDataRequest* reqptr = 629 reinterpret_cast<const ReadFruDataRequest*>(request); 630 auto offset = 631 static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS); 632 try 633 { 634 const auto& fruArea = getFruAreaData(reqptr->fruID); 635 auto size = fruArea.size(); 636 if ((offset + reqptr->count) > size) 637 { 638 log<level::ERR>("Invalid offset and count", 639 entry("Offset=%d Count=%d SizeOfFruArea=%d", 640 offset, reqptr->count, size)); 641 return IPMI_CC_INVALID; 642 } 643 std::copy((fruArea.begin() + offset), (fruArea.begin() + reqptr->count), 644 (static_cast<uint8_t*>(response))); 645 *data_len = reqptr->count; 646 } 647 catch (const InternalFailure& e) 648 { 649 rc = IPMI_CC_UNSPECIFIED_ERROR; 650 *data_len = 0; 651 log<level::ERR>(e.what()); 652 report<InternalFailure>(); 653 } 654 return rc; 655 } 656 657 658 void register_netfn_storage_functions() 659 { 660 // <Wildcard Command> 661 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_WILDCARD); 662 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL, ipmi_storage_wildcard, 663 PRIVILEGE_USER); 664 665 // <Get SEL Info> 666 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO); 667 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL, getSELInfo, 668 PRIVILEGE_USER); 669 670 // <Get SEL Time> 671 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME); 672 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL, ipmi_storage_get_sel_time, 673 PRIVILEGE_USER); 674 675 // <Set SEL Time> 676 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME); 677 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL, ipmi_storage_set_sel_time, 678 PRIVILEGE_OPERATOR); 679 680 // <Reserve SEL> 681 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL); 682 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL, ipmi_storage_reserve_sel, 683 PRIVILEGE_USER); 684 685 // <Get SEL Entry> 686 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY); 687 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL, getSELEntry, 688 PRIVILEGE_USER); 689 690 // <Delete SEL Entry> 691 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_DELETE_SEL); 692 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL, deleteSELEntry, 693 PRIVILEGE_OPERATOR); 694 695 // <Add SEL Entry> 696 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_ADD_SEL); 697 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL, ipmi_storage_add_sel, 698 PRIVILEGE_OPERATOR); 699 // <Clear SEL> 700 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL); 701 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL, 702 PRIVILEGE_OPERATOR); 703 // <Get FRU Inventory Area Info> 704 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_STORAGE, 705 IPMI_CMD_GET_FRU_INV_AREA_INFO); 706 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL, 707 ipmi_storage_get_fru_inv_area_info, PRIVILEGE_OPERATOR); 708 709 // <Add READ FRU Data 710 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_STORAGE, 711 IPMI_CMD_READ_FRU_DATA); 712 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL, 713 ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR); 714 715 ipmi::fru::registerCallbackHandler(); 716 return; 717 } 718 719