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/message/types.hpp> 23 #include <sdbusplus/server.hpp> 24 #include <string> 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 ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 271 ipmi_request_t request, ipmi_response_t response, 272 ipmi_data_len_t data_len, ipmi_context_t context) 273 { 274 if (*data_len != sizeof(ipmi::sel::DeleteSELEntryRequest)) 275 { 276 *data_len = 0; 277 return IPMI_CC_REQ_DATA_LEN_INVALID; 278 } 279 280 namespace fs = std::filesystem; 281 auto requestData = 282 reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*>(request); 283 284 if (!checkSELReservation(requestData->reservationID)) 285 { 286 *data_len = 0; 287 return IPMI_CC_INVALID_RESERVATION_ID; 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 *data_len = 0; 303 return IPMI_CC_SENSOR_INVALID; 304 } 305 306 if (cache::paths.empty()) 307 { 308 *data_len = 0; 309 return IPMI_CC_SENSOR_INVALID; 310 } 311 312 ipmi::sel::ObjectPaths::const_iterator iter; 313 uint16_t delRecordID = 0; 314 315 if (requestData->selRecordID == ipmi::sel::firstEntry) 316 { 317 iter = cache::paths.begin(); 318 fs::path path(*iter); 319 delRecordID = static_cast<uint16_t>( 320 std::stoul(std::string(path.filename().c_str()))); 321 } 322 else if (requestData->selRecordID == ipmi::sel::lastEntry) 323 { 324 iter = cache::paths.end(); 325 fs::path path(*iter); 326 delRecordID = static_cast<uint16_t>( 327 std::stoul(std::string(path.filename().c_str()))); 328 } 329 else 330 { 331 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" + 332 std::to_string(requestData->selRecordID); 333 334 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath); 335 if (iter == cache::paths.end()) 336 { 337 *data_len = 0; 338 return IPMI_CC_SENSOR_INVALID; 339 } 340 delRecordID = requestData->selRecordID; 341 } 342 343 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 344 std::string service; 345 346 try 347 { 348 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter); 349 } 350 catch (const std::runtime_error& e) 351 { 352 log<level::ERR>(e.what()); 353 *data_len = 0; 354 return IPMI_CC_UNSPECIFIED_ERROR; 355 } 356 357 auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(), 358 ipmi::sel::logDeleteIntf, "Delete"); 359 auto reply = bus.call(methodCall); 360 if (reply.is_method_error()) 361 { 362 *data_len = 0; 363 return IPMI_CC_UNSPECIFIED_ERROR; 364 } 365 366 // Invalidate the cache of dbus entry objects. 367 cache::paths.clear(); 368 std::memcpy(response, &delRecordID, sizeof(delRecordID)); 369 *data_len = sizeof(delRecordID); 370 371 return IPMI_CC_OK; 372 } 373 374 ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 375 ipmi_response_t response, ipmi_data_len_t data_len, 376 ipmi_context_t context) 377 { 378 if (*data_len != sizeof(ipmi::sel::ClearSELRequest)) 379 { 380 *data_len = 0; 381 return IPMI_CC_REQ_DATA_LEN_INVALID; 382 } 383 384 auto requestData = 385 reinterpret_cast<const ipmi::sel::ClearSELRequest*>(request); 386 387 if (!checkSELReservation(requestData->reservationID)) 388 { 389 *data_len = 0; 390 return IPMI_CC_INVALID_RESERVATION_ID; 391 } 392 393 if (requestData->charC != 'C' || requestData->charL != 'L' || 394 requestData->charR != 'R') 395 { 396 *data_len = 0; 397 return IPMI_CC_INVALID_FIELD_REQUEST; 398 } 399 400 uint8_t eraseProgress = ipmi::sel::eraseComplete; 401 402 /* 403 * Erasure status cannot be fetched from DBUS, so always return erasure 404 * status as `erase completed`. 405 */ 406 if (requestData->eraseOperation == ipmi::sel::getEraseStatus) 407 { 408 std::memcpy(response, &eraseProgress, sizeof(eraseProgress)); 409 *data_len = sizeof(eraseProgress); 410 return IPMI_CC_OK; 411 } 412 413 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared 414 cancelSELReservation(); 415 416 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 417 ipmi::sel::ObjectPaths objectPaths; 418 auto depth = 0; 419 420 auto mapperCall = 421 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath, 422 ipmi::sel::mapperIntf, "GetSubTreePaths"); 423 mapperCall.append(ipmi::sel::logBasePath); 424 mapperCall.append(depth); 425 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf})); 426 427 try 428 { 429 auto reply = bus.call(mapperCall); 430 if (reply.is_method_error()) 431 { 432 std::memcpy(response, &eraseProgress, sizeof(eraseProgress)); 433 *data_len = sizeof(eraseProgress); 434 return IPMI_CC_OK; 435 } 436 437 reply.read(objectPaths); 438 if (objectPaths.empty()) 439 { 440 std::memcpy(response, &eraseProgress, sizeof(eraseProgress)); 441 *data_len = sizeof(eraseProgress); 442 return IPMI_CC_OK; 443 } 444 } 445 catch (const sdbusplus::exception::SdBusError& e) 446 { 447 std::memcpy(response, &eraseProgress, sizeof(eraseProgress)); 448 *data_len = sizeof(eraseProgress); 449 return IPMI_CC_OK; 450 } 451 452 std::string service; 453 454 try 455 { 456 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, 457 objectPaths.front()); 458 } 459 catch (const std::runtime_error& e) 460 { 461 log<level::ERR>(e.what()); 462 *data_len = 0; 463 return IPMI_CC_UNSPECIFIED_ERROR; 464 } 465 466 for (const auto& iter : objectPaths) 467 { 468 auto methodCall = bus.new_method_call( 469 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete"); 470 471 auto reply = bus.call(methodCall); 472 if (reply.is_method_error()) 473 { 474 *data_len = 0; 475 return IPMI_CC_UNSPECIFIED_ERROR; 476 } 477 } 478 479 // Invalidate the cache of dbus entry objects. 480 cache::paths.clear(); 481 std::memcpy(response, &eraseProgress, sizeof(eraseProgress)); 482 *data_len = sizeof(eraseProgress); 483 return IPMI_CC_OK; 484 } 485 486 ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 487 ipmi_request_t request, 488 ipmi_response_t response, 489 ipmi_data_len_t data_len, 490 ipmi_context_t context) 491 { 492 if (*data_len != 0) 493 { 494 *data_len = 0; 495 return IPMI_CC_REQ_DATA_LEN_INVALID; 496 } 497 498 using namespace std::chrono; 499 uint64_t host_time_usec = 0; 500 uint32_t resp = 0; 501 std::stringstream hostTime; 502 503 try 504 { 505 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 506 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH); 507 sdbusplus::message::variant<uint64_t> value; 508 509 // Get host time 510 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH, 511 DBUS_PROPERTIES, "Get"); 512 513 method.append(TIME_INTERFACE, PROPERTY_ELAPSED); 514 auto reply = bus.call(method); 515 if (reply.is_method_error()) 516 { 517 log<level::ERR>("Error getting time", 518 entry("SERVICE=%s", service.c_str()), 519 entry("PATH=%s", HOST_TIME_PATH)); 520 return IPMI_CC_UNSPECIFIED_ERROR; 521 } 522 reply.read(value); 523 host_time_usec = std::get<uint64_t>(value); 524 } 525 catch (InternalFailure& e) 526 { 527 log<level::ERR>(e.what()); 528 return IPMI_CC_UNSPECIFIED_ERROR; 529 } 530 catch (const std::runtime_error& e) 531 { 532 log<level::ERR>(e.what()); 533 return IPMI_CC_UNSPECIFIED_ERROR; 534 } 535 536 hostTime << "Host time:" << getTimeString(host_time_usec); 537 log<level::DEBUG>(hostTime.str().c_str()); 538 539 // Time is really long int but IPMI wants just uint32. This works okay until 540 // the number of seconds since 1970 overflows uint32 size.. Still a whole 541 // lot of time here to even think about that. 542 resp = duration_cast<seconds>(microseconds(host_time_usec)).count(); 543 resp = htole32(resp); 544 545 // From the IPMI Spec 2.0, response should be a 32-bit value 546 *data_len = sizeof(resp); 547 548 // Pack the actual response 549 std::memcpy(response, &resp, *data_len); 550 551 return IPMI_CC_OK; 552 } 553 554 ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 555 ipmi_request_t request, 556 ipmi_response_t response, 557 ipmi_data_len_t data_len, 558 ipmi_context_t context) 559 { 560 if (*data_len != sizeof(uint32_t)) 561 { 562 *data_len = 0; 563 return IPMI_CC_REQ_DATA_LEN_INVALID; 564 } 565 using namespace std::chrono; 566 ipmi_ret_t rc = IPMI_CC_OK; 567 uint32_t secs = *static_cast<uint32_t*>(request); 568 *data_len = 0; 569 570 secs = le32toh(secs); 571 microseconds usec{seconds(secs)}; 572 573 try 574 { 575 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 576 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH); 577 sdbusplus::message::variant<uint64_t> value{usec.count()}; 578 579 // Set host time 580 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH, 581 DBUS_PROPERTIES, "Set"); 582 583 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value); 584 auto reply = bus.call(method); 585 if (reply.is_method_error()) 586 { 587 log<level::ERR>("Error setting time", 588 entry("SERVICE=%s", service.c_str()), 589 entry("PATH=%s", HOST_TIME_PATH)); 590 rc = IPMI_CC_UNSPECIFIED_ERROR; 591 } 592 } 593 catch (InternalFailure& e) 594 { 595 log<level::ERR>(e.what()); 596 rc = IPMI_CC_UNSPECIFIED_ERROR; 597 } 598 catch (const std::runtime_error& e) 599 { 600 log<level::ERR>(e.what()); 601 rc = IPMI_CC_UNSPECIFIED_ERROR; 602 } 603 604 return rc; 605 } 606 607 ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 608 ipmi_request_t request, 609 ipmi_response_t response, 610 ipmi_data_len_t data_len, 611 ipmi_context_t context) 612 { 613 if (*data_len != 0) 614 { 615 *data_len = 0; 616 return IPMI_CC_REQ_DATA_LEN_INVALID; 617 } 618 619 ipmi_ret_t rc = IPMI_CC_OK; 620 unsigned short selResID = reserveSel(); 621 622 *data_len = sizeof(selResID); 623 624 // Pack the actual response 625 std::memcpy(response, &selResID, *data_len); 626 627 return rc; 628 } 629 630 ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 631 ipmi_request_t request, 632 ipmi_response_t response, 633 ipmi_data_len_t data_len, 634 ipmi_context_t context) 635 { 636 if (*data_len != sizeof(ipmi_add_sel_request_t)) 637 { 638 *data_len = 0; 639 return IPMI_CC_REQ_DATA_LEN_INVALID; 640 } 641 642 ipmi_ret_t rc = IPMI_CC_OK; 643 ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request; 644 uint16_t recordid; 645 646 // Per the IPMI spec, need to cancel the reservation when a SEL entry is 647 // added 648 cancelSELReservation(); 649 650 recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2]; 651 652 *data_len = sizeof(recordid); 653 654 // Pack the actual response 655 std::memcpy(response, &p->eventdata[1], 2); 656 657 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is 658 // a maintenance procedure associated with eSEL record. 659 static constexpr auto procedureType = 0xDE; 660 if (p->recordtype == procedureType) 661 { 662 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the 663 // procedure number. 664 createProcedureLogEntry(p->sensortype); 665 } 666 667 return rc; 668 } 669 670 // Read FRU info area 671 ipmi_ret_t ipmi_storage_get_fru_inv_area_info( 672 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, 673 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) 674 { 675 ipmi_ret_t rc = IPMI_CC_OK; 676 const FruInvenAreaInfoRequest* reqptr = 677 reinterpret_cast<const FruInvenAreaInfoRequest*>(request); 678 679 auto iter = frus.find(reqptr->fruID); 680 if (iter == frus.end()) 681 { 682 *data_len = 0; 683 return IPMI_CC_SENSOR_INVALID; 684 } 685 686 try 687 { 688 const auto& fruArea = getFruAreaData(reqptr->fruID); 689 auto size = static_cast<uint16_t>(fruArea.size()); 690 FruInvenAreaInfoResponse resp; 691 resp.sizems = size >> 8; 692 resp.sizels = size; 693 resp.access = static_cast<uint8_t>(AccessMode::bytes); 694 695 *data_len = sizeof(resp); 696 697 // Pack the actual response 698 std::memcpy(response, &resp, *data_len); 699 } 700 catch (const InternalFailure& e) 701 { 702 rc = IPMI_CC_UNSPECIFIED_ERROR; 703 *data_len = 0; 704 log<level::ERR>(e.what()); 705 } 706 return rc; 707 } 708 709 // Read FRU data 710 ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 711 ipmi_request_t request, 712 ipmi_response_t response, 713 ipmi_data_len_t data_len, 714 ipmi_context_t context) 715 { 716 ipmi_ret_t rc = IPMI_CC_OK; 717 const ReadFruDataRequest* reqptr = 718 reinterpret_cast<const ReadFruDataRequest*>(request); 719 auto resptr = reinterpret_cast<ReadFruDataResponse*>(response); 720 721 auto iter = frus.find(reqptr->fruID); 722 if (iter == frus.end()) 723 { 724 *data_len = 0; 725 return IPMI_CC_SENSOR_INVALID; 726 } 727 728 auto offset = 729 static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS); 730 try 731 { 732 const auto& fruArea = getFruAreaData(reqptr->fruID); 733 auto size = fruArea.size(); 734 735 if (offset >= size) 736 { 737 return IPMI_CC_PARM_OUT_OF_RANGE; 738 } 739 740 // Write the count of response data. 741 if ((offset + reqptr->count) <= size) 742 { 743 resptr->count = reqptr->count; 744 } 745 else 746 { 747 resptr->count = size - offset; 748 } 749 750 std::copy((fruArea.begin() + offset), 751 (fruArea.begin() + offset + resptr->count), resptr->data); 752 753 *data_len = resptr->count + 1; // additional one byte for count 754 } 755 catch (const InternalFailure& e) 756 { 757 rc = IPMI_CC_UNSPECIFIED_ERROR; 758 *data_len = 0; 759 log<level::ERR>(e.what()); 760 } 761 return rc; 762 } 763 764 ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 765 ipmi_request_t request, 766 ipmi_response_t response, 767 ipmi_data_len_t data_len, 768 ipmi_context_t context) 769 { 770 constexpr auto sdrVersion = 0x51; 771 auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response); 772 773 std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse)); 774 775 responseData->sdrVersion = sdrVersion; 776 777 uint16_t records = frus.size() + sensors.size(); 778 responseData->recordCountMs = records >> 8; 779 responseData->recordCountLs = records; 780 781 responseData->freeSpace[0] = 0xFF; 782 responseData->freeSpace[1] = 0xFF; 783 784 *data_len = sizeof(GetRepositoryInfoResponse); 785 786 return IPMI_CC_OK; 787 } 788 789 void register_netfn_storage_functions() 790 { 791 // <Wildcard Command> 792 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL, 793 ipmi_storage_wildcard, PRIVILEGE_USER); 794 795 // <Get SEL Info> 796 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL, 797 getSELInfo, PRIVILEGE_USER); 798 799 // <Get SEL Time> 800 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL, 801 ipmi_storage_get_sel_time, PRIVILEGE_USER); 802 803 // <Set SEL Time> 804 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL, 805 ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR); 806 807 // <Reserve SEL> 808 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL, 809 ipmi_storage_reserve_sel, PRIVILEGE_USER); 810 811 // <Get SEL Entry> 812 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL, 813 getSELEntry, PRIVILEGE_USER); 814 815 // <Delete SEL Entry> 816 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL, 817 deleteSELEntry, PRIVILEGE_OPERATOR); 818 819 // <Add SEL Entry> 820 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL, 821 ipmi_storage_add_sel, PRIVILEGE_OPERATOR); 822 // <Clear SEL> 823 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL, 824 PRIVILEGE_OPERATOR); 825 // <Get FRU Inventory Area Info> 826 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL, 827 ipmi_storage_get_fru_inv_area_info, 828 PRIVILEGE_OPERATOR); 829 830 // <Add READ FRU Data 831 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL, 832 ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR); 833 834 // <Get Repository Info> 835 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO, 836 nullptr, ipmi_get_repository_info, PRIVILEGE_USER); 837 838 // <Reserve SDR Repository> 839 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr, 840 ipmi_sen_reserve_sdr, PRIVILEGE_USER); 841 842 // <Get SDR> 843 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr, 844 ipmi_sen_get_sdr, PRIVILEGE_USER); 845 846 ipmi::fru::registerCallbackHandler(); 847 return; 848 } 849