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