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