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