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