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