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