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