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 <optional> 21 #include <phosphor-logging/elog-errors.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <sdbusplus/server.hpp> 24 #include <string> 25 #include <variant> 26 #include <xyz/openbmc_project/Common/error.hpp> 27 28 void register_netfn_storage_functions() __attribute__((constructor)); 29 30 unsigned int g_sel_time = 0xFFFFFFFF; 31 namespace ipmi 32 { 33 namespace sensor 34 { 35 extern const IdInfoMap sensors; 36 } // namespace sensor 37 } // namespace ipmi 38 39 extern const FruMap frus; 40 constexpr uint8_t eventDataSize = 3; 41 namespace 42 { 43 constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime"; 44 constexpr auto BMC_TIME_PATH = "/xyz/openbmc_project/time/bmc"; 45 constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties"; 46 constexpr auto PROPERTY_ELAPSED = "Elapsed"; 47 48 constexpr auto logWatchPath = "/xyz/openbmc_project/logging"; 49 constexpr auto logBasePath = "/xyz/openbmc_project/logging/entry"; 50 constexpr auto logEntryIntf = "xyz.openbmc_project.Logging.Entry"; 51 constexpr auto logDeleteIntf = "xyz.openbmc_project.Object.Delete"; 52 } // namespace 53 54 using InternalFailure = 55 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 56 using namespace phosphor::logging; 57 using namespace ipmi::fru; 58 59 using SELRecordID = uint16_t; 60 using SELEntry = ipmi::sel::SELEventRecordFormat; 61 using SELCacheMap = std::map<SELRecordID, SELEntry>; 62 63 SELCacheMap selCacheMap __attribute__((init_priority(101))); 64 bool selCacheMapInitialized; 65 std::unique_ptr<sdbusplus::bus::match::match> selAddedMatch 66 __attribute__((init_priority(101))); 67 std::unique_ptr<sdbusplus::bus::match::match> selRemovedMatch 68 __attribute__((init_priority(101))); 69 std::unique_ptr<sdbusplus::bus::match::match> selUpdatedMatch 70 __attribute__((init_priority(101))); 71 72 static inline uint16_t getLoggingId(const std::string& p) 73 { 74 namespace fs = std::filesystem; 75 fs::path entryPath(p); 76 return std::stoul(entryPath.filename().string()); 77 } 78 79 static inline std::string getLoggingObjPath(uint16_t id) 80 { 81 return std::string(ipmi::sel::logBasePath) + "/" + std::to_string(id); 82 } 83 84 std::optional<std::pair<uint16_t, SELEntry>> 85 parseLoggingEntry(const std::string& p) 86 { 87 try 88 { 89 auto id = getLoggingId(p); 90 ipmi::sel::GetSELEntryResponse record{}; 91 record = ipmi::sel::convertLogEntrytoSEL(p); 92 return std::pair<uint16_t, SELEntry>({id, std::move(record.event)}); 93 } 94 catch (const std::exception& e) 95 { 96 fprintf(stderr, "Failed to convert %s to SEL: %s\n", p.c_str(), 97 e.what()); 98 } 99 return std::nullopt; 100 } 101 102 static void selAddedCallback(sdbusplus::message::message& m) 103 { 104 sdbusplus::message::object_path objPath; 105 try 106 { 107 m.read(objPath); 108 } 109 catch (const sdbusplus::exception::exception& e) 110 { 111 log<level::ERR>("Failed to read object path"); 112 return; 113 } 114 std::string p = objPath; 115 auto entry = parseLoggingEntry(p); 116 if (entry) 117 { 118 selCacheMap.insert(std::move(*entry)); 119 } 120 } 121 122 static void selRemovedCallback(sdbusplus::message::message& m) 123 { 124 sdbusplus::message::object_path objPath; 125 try 126 { 127 m.read(objPath); 128 } 129 catch (const sdbusplus::exception::exception& e) 130 { 131 log<level::ERR>("Failed to read object path"); 132 } 133 try 134 { 135 std::string p = objPath; 136 selCacheMap.erase(getLoggingId(p)); 137 } 138 catch (const std::invalid_argument& e) 139 { 140 log<level::ERR>("Invalid logging entry ID"); 141 } 142 } 143 144 static void selUpdatedCallback(sdbusplus::message::message& m) 145 { 146 std::string p = m.get_path(); 147 auto entry = parseLoggingEntry(p); 148 if (entry) 149 { 150 selCacheMap.insert_or_assign(entry->first, std::move(entry->second)); 151 } 152 } 153 154 void registerSelCallbackHandler() 155 { 156 using namespace sdbusplus::bus::match::rules; 157 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 158 if (!selAddedMatch) 159 { 160 selAddedMatch = std::make_unique<sdbusplus::bus::match::match>( 161 bus, interfacesAdded(logWatchPath), 162 std::bind(selAddedCallback, std::placeholders::_1)); 163 } 164 if (!selRemovedMatch) 165 { 166 selRemovedMatch = std::make_unique<sdbusplus::bus::match::match>( 167 bus, interfacesRemoved(logWatchPath), 168 std::bind(selRemovedCallback, std::placeholders::_1)); 169 } 170 if (!selUpdatedMatch) 171 { 172 selUpdatedMatch = std::make_unique<sdbusplus::bus::match::match>( 173 bus, 174 type::signal() + member("PropertiesChanged"s) + 175 interface("org.freedesktop.DBus.Properties"s) + 176 argN(0, logEntryIntf), 177 std::bind(selUpdatedCallback, std::placeholders::_1)); 178 } 179 } 180 181 void initSELCache() 182 { 183 ipmi::sel::ObjectPaths paths; 184 try 185 { 186 ipmi::sel::readLoggingObjectPaths(paths); 187 } 188 catch (const sdbusplus::exception::exception& e) 189 { 190 log<level::ERR>("Failed to get logging object paths"); 191 return; 192 } 193 for (const auto& p : paths) 194 { 195 auto entry = parseLoggingEntry(p); 196 if (entry) 197 { 198 selCacheMap.insert(std::move(*entry)); 199 } 200 } 201 registerSelCallbackHandler(); 202 selCacheMapInitialized = true; 203 } 204 205 /** 206 * @enum Device access mode 207 */ 208 enum class AccessMode 209 { 210 bytes, ///< Device is accessed by bytes 211 words ///< Device is accessed by words 212 }; 213 214 /** @brief implements the get SEL Info command 215 * @returns IPMI completion code plus response data 216 * - selVersion - SEL revision 217 * - entries - Number of log entries in SEL. 218 * - freeSpace - Free Space in bytes. 219 * - addTimeStamp - Most recent addition timestamp 220 * - eraseTimeStamp - Most recent erase timestamp 221 * - operationSupport - Reserve & Delete SEL operations supported 222 */ 223 224 ipmi::RspType<uint8_t, // SEL revision. 225 uint16_t, // number of log entries in SEL. 226 uint16_t, // free Space in bytes. 227 uint32_t, // most recent addition timestamp 228 uint32_t, // most recent erase timestamp. 229 230 bool, // SEL allocation info supported 231 bool, // reserve SEL supported 232 bool, // partial Add SEL Entry supported 233 bool, // delete SEL supported 234 uint3_t, // reserved 235 bool // overflow flag 236 > 237 ipmiStorageGetSelInfo() 238 { 239 uint16_t entries = 0; 240 // Most recent addition timestamp. 241 uint32_t addTimeStamp = ipmi::sel::invalidTimeStamp; 242 243 if (!selCacheMapInitialized) 244 { 245 // In case the initSELCache() fails, try it again 246 initSELCache(); 247 } 248 if (!selCacheMap.empty()) 249 { 250 entries = static_cast<uint16_t>(selCacheMap.size()); 251 252 try 253 { 254 auto objPath = getLoggingObjPath(selCacheMap.rbegin()->first); 255 addTimeStamp = static_cast<uint32_t>( 256 (ipmi::sel::getEntryTimeStamp(objPath).count())); 257 } 258 catch (const InternalFailure& e) 259 { 260 } 261 catch (const std::runtime_error& e) 262 { 263 log<level::ERR>(e.what()); 264 } 265 } 266 267 constexpr uint8_t selVersion = ipmi::sel::selVersion; 268 constexpr uint16_t freeSpace = 0xFFFF; 269 constexpr uint32_t eraseTimeStamp = ipmi::sel::invalidTimeStamp; 270 constexpr uint3_t reserved{0}; 271 272 return ipmi::responseSuccess( 273 selVersion, entries, freeSpace, addTimeStamp, eraseTimeStamp, 274 ipmi::sel::operationSupport::getSelAllocationInfo, 275 ipmi::sel::operationSupport::reserveSel, 276 ipmi::sel::operationSupport::partialAddSelEntry, 277 ipmi::sel::operationSupport::deleteSel, reserved, 278 ipmi::sel::operationSupport::overflow); 279 } 280 281 ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 282 ipmi_request_t request, ipmi_response_t response, 283 ipmi_data_len_t data_len, ipmi_context_t context) 284 { 285 if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest)) 286 { 287 *data_len = 0; 288 return IPMI_CC_REQ_DATA_LEN_INVALID; 289 } 290 291 auto requestData = 292 reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request); 293 294 if (requestData->reservationID != 0) 295 { 296 if (!checkSELReservation(requestData->reservationID)) 297 { 298 *data_len = 0; 299 return IPMI_CC_INVALID_RESERVATION_ID; 300 } 301 } 302 303 if (!selCacheMapInitialized) 304 { 305 // In case the initSELCache() fails, try it again 306 initSELCache(); 307 } 308 309 if (selCacheMap.empty()) 310 { 311 *data_len = 0; 312 return IPMI_CC_SENSOR_INVALID; 313 } 314 315 SELCacheMap::const_iterator iter; 316 317 // Check for the requested SEL Entry. 318 if (requestData->selRecordID == ipmi::sel::firstEntry) 319 { 320 iter = selCacheMap.begin(); 321 } 322 else if (requestData->selRecordID == ipmi::sel::lastEntry) 323 { 324 if (selCacheMap.size() > 1) 325 { 326 iter = selCacheMap.end(); 327 --iter; 328 } 329 else 330 { 331 // Only one entry exists, return the first 332 iter = selCacheMap.begin(); 333 } 334 } 335 else 336 { 337 iter = selCacheMap.find(requestData->selRecordID); 338 if (iter == selCacheMap.end()) 339 { 340 *data_len = 0; 341 return IPMI_CC_SENSOR_INVALID; 342 } 343 } 344 345 ipmi::sel::GetSELEntryResponse record{0, iter->second}; 346 // Identify the next SEL record ID 347 ++iter; 348 if (iter == selCacheMap.end()) 349 { 350 record.nextRecordID = ipmi::sel::lastEntry; 351 } 352 else 353 { 354 record.nextRecordID = iter->first; 355 } 356 357 if (requestData->readLength == ipmi::sel::entireRecord) 358 { 359 std::memcpy(response, &record, sizeof(record)); 360 *data_len = sizeof(record); 361 } 362 else 363 { 364 if (requestData->offset >= ipmi::sel::selRecordSize || 365 requestData->readLength > ipmi::sel::selRecordSize) 366 { 367 *data_len = 0; 368 return IPMI_CC_INVALID_FIELD_REQUEST; 369 } 370 371 auto diff = ipmi::sel::selRecordSize - requestData->offset; 372 auto readLength = 373 std::min(diff, static_cast<int>(requestData->readLength)); 374 375 std::memcpy(response, &record.nextRecordID, 376 sizeof(record.nextRecordID)); 377 std::memcpy(static_cast<uint8_t*>(response) + 378 sizeof(record.nextRecordID), 379 &record.event.eventRecord.recordID + requestData->offset, 380 readLength); 381 *data_len = sizeof(record.nextRecordID) + readLength; 382 } 383 384 return IPMI_CC_OK; 385 } 386 387 /** @brief implements the delete SEL entry command 388 * @request 389 * - reservationID; // reservation ID. 390 * - selRecordID; // SEL record ID. 391 * 392 * @returns ipmi completion code plus response data 393 * - Record ID of the deleted record 394 */ 395 ipmi::RspType<uint16_t // deleted record ID 396 > 397 deleteSELEntry(uint16_t reservationID, uint16_t selRecordID) 398 { 399 400 namespace fs = std::filesystem; 401 402 if (!checkSELReservation(reservationID)) 403 { 404 return ipmi::responseInvalidReservationId(); 405 } 406 407 // Per the IPMI spec, need to cancel the reservation when a SEL entry is 408 // deleted 409 cancelSELReservation(); 410 411 if (!selCacheMapInitialized) 412 { 413 // In case the initSELCache() fails, try it again 414 initSELCache(); 415 } 416 417 if (selCacheMap.empty()) 418 { 419 return ipmi::responseSensorInvalid(); 420 } 421 422 SELCacheMap::const_iterator iter; 423 uint16_t delRecordID = 0; 424 425 if (selRecordID == ipmi::sel::firstEntry) 426 { 427 delRecordID = selCacheMap.begin()->first; 428 } 429 else if (selRecordID == ipmi::sel::lastEntry) 430 { 431 delRecordID = selCacheMap.rbegin()->first; 432 } 433 else 434 { 435 iter = selCacheMap.find(selRecordID); 436 if (iter == selCacheMap.end()) 437 { 438 return ipmi::responseSensorInvalid(); 439 } 440 delRecordID = selRecordID; 441 } 442 443 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 444 std::string service; 445 446 auto objPath = getLoggingObjPath(iter->first); 447 try 448 { 449 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, objPath); 450 } 451 catch (const std::runtime_error& e) 452 { 453 log<level::ERR>(e.what()); 454 return ipmi::responseUnspecifiedError(); 455 } 456 457 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(), 458 ipmi::sel::logDeleteIntf, "Delete"); 459 auto reply = bus.call(methodCall); 460 if (reply.is_method_error()) 461 { 462 return ipmi::responseUnspecifiedError(); 463 } 464 465 return ipmi::responseSuccess(delRecordID); 466 } 467 468 /** @brief implements the Clear SEL command 469 * @request 470 * - reservationID // Reservation ID. 471 * - clr // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) } 472 * - eraseOperation; // requested operation. 473 * 474 * @returns ipmi completion code plus response data 475 * - erase status 476 */ 477 478 ipmi::RspType<uint8_t // erase status 479 > 480 clearSEL(uint16_t reservationID, const std::array<char, 3>& clr, 481 uint8_t eraseOperation) 482 { 483 static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'}; 484 if (clr != clrOk) 485 { 486 return ipmi::responseInvalidFieldRequest(); 487 } 488 489 if (!checkSELReservation(reservationID)) 490 { 491 return ipmi::responseInvalidReservationId(); 492 } 493 494 /* 495 * Erasure status cannot be fetched from DBUS, so always return erasure 496 * status as `erase completed`. 497 */ 498 if (eraseOperation == ipmi::sel::getEraseStatus) 499 { 500 return ipmi::responseSuccess( 501 static_cast<uint8_t>(ipmi::sel::eraseComplete)); 502 } 503 504 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared 505 cancelSELReservation(); 506 507 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 508 ipmi::sel::ObjectPaths objectPaths; 509 auto depth = 0; 510 511 auto mapperCall = 512 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath, 513 ipmi::sel::mapperIntf, "GetSubTreePaths"); 514 mapperCall.append(ipmi::sel::logBasePath); 515 mapperCall.append(depth); 516 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf})); 517 518 try 519 { 520 auto reply = bus.call(mapperCall); 521 if (reply.is_method_error()) 522 { 523 return ipmi::responseSuccess( 524 static_cast<uint8_t>(ipmi::sel::eraseComplete)); 525 } 526 527 reply.read(objectPaths); 528 if (objectPaths.empty()) 529 { 530 return ipmi::responseSuccess( 531 static_cast<uint8_t>(ipmi::sel::eraseComplete)); 532 } 533 } 534 catch (const sdbusplus::exception::exception& e) 535 { 536 return ipmi::responseSuccess( 537 static_cast<uint8_t>(ipmi::sel::eraseComplete)); 538 } 539 540 std::string service; 541 542 try 543 { 544 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, 545 objectPaths.front()); 546 } 547 catch (const std::runtime_error& e) 548 { 549 log<level::ERR>(e.what()); 550 return ipmi::responseUnspecifiedError(); 551 } 552 553 for (const auto& iter : objectPaths) 554 { 555 auto methodCall = bus.new_method_call( 556 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete"); 557 558 auto reply = bus.call(methodCall); 559 if (reply.is_method_error()) 560 { 561 return ipmi::responseUnspecifiedError(); 562 } 563 } 564 565 return ipmi::responseSuccess( 566 static_cast<uint8_t>(ipmi::sel::eraseComplete)); 567 } 568 569 /** @brief implements the get SEL time command 570 * @returns IPMI completion code plus response data 571 * -current time 572 */ 573 ipmi::RspType<uint32_t> // current time 574 ipmiStorageGetSelTime() 575 { 576 using namespace std::chrono; 577 uint64_t bmc_time_usec = 0; 578 std::stringstream bmcTime; 579 580 try 581 { 582 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 583 auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH); 584 std::variant<uint64_t> value; 585 586 // Get bmc time 587 auto method = bus.new_method_call(service.c_str(), BMC_TIME_PATH, 588 DBUS_PROPERTIES, "Get"); 589 590 method.append(TIME_INTERFACE, PROPERTY_ELAPSED); 591 auto reply = bus.call(method); 592 if (reply.is_method_error()) 593 { 594 log<level::ERR>("Error getting time", 595 entry("SERVICE=%s", service.c_str()), 596 entry("PATH=%s", BMC_TIME_PATH)); 597 return ipmi::responseUnspecifiedError(); 598 } 599 reply.read(value); 600 bmc_time_usec = std::get<uint64_t>(value); 601 } 602 catch (const InternalFailure& e) 603 { 604 log<level::ERR>(e.what()); 605 return ipmi::responseUnspecifiedError(); 606 } 607 catch (const std::exception& e) 608 { 609 log<level::ERR>(e.what()); 610 return ipmi::responseUnspecifiedError(); 611 } 612 613 bmcTime << "BMC time:" 614 << duration_cast<seconds>(microseconds(bmc_time_usec)).count(); 615 log<level::DEBUG>(bmcTime.str().c_str()); 616 617 // Time is really long int but IPMI wants just uint32. This works okay until 618 // the number of seconds since 1970 overflows uint32 size.. Still a whole 619 // lot of time here to even think about that. 620 return ipmi::responseSuccess( 621 duration_cast<seconds>(microseconds(bmc_time_usec)).count()); 622 } 623 624 /** @brief implements the set SEL time command 625 * @param selDeviceTime - epoch time 626 * -local time as the number of seconds from 00:00:00, January 1, 1970 627 * @returns IPMI completion code 628 */ 629 ipmi::RspType<> ipmiStorageSetSelTime(uint32_t selDeviceTime) 630 { 631 using namespace std::chrono; 632 microseconds usec{seconds(selDeviceTime)}; 633 634 try 635 { 636 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 637 auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH); 638 std::variant<uint64_t> value{(uint64_t)usec.count()}; 639 640 // Set bmc time 641 auto method = bus.new_method_call(service.c_str(), BMC_TIME_PATH, 642 DBUS_PROPERTIES, "Set"); 643 644 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value); 645 auto reply = bus.call(method); 646 if (reply.is_method_error()) 647 { 648 log<level::ERR>("Error setting time", 649 entry("SERVICE=%s", service.c_str()), 650 entry("PATH=%s", BMC_TIME_PATH)); 651 return ipmi::responseUnspecifiedError(); 652 } 653 } 654 catch (const InternalFailure& e) 655 { 656 log<level::ERR>(e.what()); 657 return ipmi::responseUnspecifiedError(); 658 } 659 catch (const std::exception& e) 660 { 661 log<level::ERR>(e.what()); 662 return ipmi::responseUnspecifiedError(); 663 } 664 665 return ipmi::responseSuccess(); 666 } 667 668 /** @brief implements the reserve SEL command 669 * @returns IPMI completion code plus response data 670 * - SEL reservation ID. 671 */ 672 ipmi::RspType<uint16_t> ipmiStorageReserveSel() 673 { 674 return ipmi::responseSuccess(reserveSel()); 675 } 676 677 /** @brief implements the Add SEL entry command 678 * @request 679 * 680 * - recordID ID used for SEL Record access 681 * - recordType Record Type 682 * - timeStamp Time when event was logged. LS byte first 683 * - generatorID software ID if event was generated from 684 * system software 685 * - evmRev event message format version 686 * - sensorType sensor type code for service that generated 687 * the event 688 * - sensorNumber number of sensors that generated the event 689 * - eventDir event dir 690 * - eventData event data field contents 691 * 692 * @returns ipmi completion code plus response data 693 * - RecordID of the Added SEL entry 694 */ 695 ipmi::RspType<uint16_t // recordID of the Added SEL entry 696 > 697 ipmiStorageAddSEL(uint16_t recordID, uint8_t recordType, uint32_t timeStamp, 698 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, 699 uint8_t sensorNumber, uint8_t eventDir, 700 std::array<uint8_t, eventDataSize> eventData) 701 { 702 // Per the IPMI spec, need to cancel the reservation when a SEL entry is 703 // added 704 cancelSELReservation(); 705 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is 706 // a maintenance procedure associated with eSEL record. 707 static constexpr auto procedureType = 0xDE; 708 if (recordType == procedureType) 709 { 710 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the 711 // procedure number. 712 createProcedureLogEntry(sensorType); 713 } 714 715 return ipmi::responseSuccess(recordID); 716 } 717 718 bool isFruPresent(const std::string& fruPath) 719 { 720 using namespace ipmi::fru; 721 722 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 723 724 auto propValue = 725 ipmi::getDbusProperty(bus, invMgrInterface, invObjPath + fruPath, 726 invItemInterface, itemPresentProp); 727 728 return std::get<bool>(propValue); 729 } 730 731 /** @brief implements the get FRU Inventory Area Info command 732 * 733 * @returns IPMI completion code plus response data 734 * - FRU Inventory area size in bytes, 735 * - access bit 736 **/ 737 ipmi::RspType<uint16_t, // FRU Inventory area size in bytes, 738 uint8_t // access size (bytes / words) 739 > 740 ipmiStorageGetFruInvAreaInfo(uint8_t fruID) 741 { 742 743 auto iter = frus.find(fruID); 744 if (iter == frus.end()) 745 { 746 return ipmi::responseSensorInvalid(); 747 } 748 749 auto path = iter->second[0].path; 750 if (!isFruPresent(path)) 751 { 752 return ipmi::responseSensorInvalid(); 753 } 754 755 try 756 { 757 return ipmi::responseSuccess( 758 static_cast<uint16_t>(getFruAreaData(fruID).size()), 759 static_cast<uint8_t>(AccessMode::bytes)); 760 } 761 catch (const InternalFailure& e) 762 { 763 log<level::ERR>(e.what()); 764 return ipmi::responseUnspecifiedError(); 765 } 766 } 767 768 /**@brief implements the Read FRU Data command 769 * @param fruDeviceId - FRU device ID. FFh = reserved 770 * @param offset - FRU inventory offset to read 771 * @param readCount - count to read 772 * 773 * @return IPMI completion code plus response data 774 * - returnCount - response data count. 775 * - data - response data 776 */ 777 ipmi::RspType<uint8_t, // count returned 778 std::vector<uint8_t>> // FRU data 779 ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t offset, 780 uint8_t readCount) 781 { 782 if (fruDeviceId == 0xFF) 783 { 784 return ipmi::responseInvalidFieldRequest(); 785 } 786 787 auto iter = frus.find(fruDeviceId); 788 if (iter == frus.end()) 789 { 790 return ipmi::responseSensorInvalid(); 791 } 792 793 try 794 { 795 const auto& fruArea = getFruAreaData(fruDeviceId); 796 auto size = fruArea.size(); 797 798 if (offset >= size) 799 { 800 return ipmi::responseParmOutOfRange(); 801 } 802 803 // Write the count of response data. 804 uint8_t returnCount; 805 if ((offset + readCount) <= size) 806 { 807 returnCount = readCount; 808 } 809 else 810 { 811 returnCount = size - offset; 812 } 813 814 std::vector<uint8_t> fruData((fruArea.begin() + offset), 815 (fruArea.begin() + offset + returnCount)); 816 817 return ipmi::responseSuccess(returnCount, fruData); 818 } 819 catch (const InternalFailure& e) 820 { 821 log<level::ERR>(e.what()); 822 return ipmi::responseUnspecifiedError(); 823 } 824 } 825 826 ipmi::RspType<uint8_t, // SDR version 827 uint16_t, // record count LS first 828 uint16_t, // free space in bytes, LS first 829 uint32_t, // addition timestamp LS first 830 uint32_t, // deletion timestamp LS first 831 uint8_t> // operation Support 832 ipmiGetRepositoryInfo() 833 { 834 835 constexpr uint8_t sdrVersion = 0x51; 836 constexpr uint16_t freeSpace = 0xFFFF; 837 constexpr uint32_t additionTimestamp = 0x0; 838 constexpr uint32_t deletionTimestamp = 0x0; 839 constexpr uint8_t operationSupport = 0; 840 841 uint16_t records = frus.size() + ipmi::sensor::sensors.size(); 842 843 return ipmi::responseSuccess(sdrVersion, records, freeSpace, 844 additionTimestamp, deletionTimestamp, 845 operationSupport); 846 } 847 848 void register_netfn_storage_functions() 849 { 850 selCacheMapInitialized = false; 851 initSELCache(); 852 // Handlers with dbus-sdr handler implementation. 853 // Do not register the hander if it dynamic sensors stack is used. 854 855 #ifndef FEATURE_DYNAMIC_SENSORS 856 // <Get SEL Info> 857 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 858 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 859 ipmiStorageGetSelInfo); 860 861 // <Get SEL Time> 862 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 863 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 864 ipmiStorageGetSelTime); 865 866 // <Set SEL Time> 867 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 868 ipmi::storage::cmdSetSelTime, 869 ipmi::Privilege::Operator, ipmiStorageSetSelTime); 870 871 // <Get SEL Entry> 872 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL, 873 getSELEntry, PRIVILEGE_USER); 874 875 // <Delete SEL Entry> 876 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 877 ipmi::storage::cmdDeleteSelEntry, 878 ipmi::Privilege::Operator, deleteSELEntry); 879 880 // <Add SEL Entry> 881 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 882 ipmi::storage::cmdAddSelEntry, 883 ipmi::Privilege::Operator, ipmiStorageAddSEL); 884 885 // <Clear SEL> 886 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 887 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 888 clearSEL); 889 890 // <Get FRU Inventory Area Info> 891 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 892 ipmi::storage::cmdGetFruInventoryAreaInfo, 893 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo); 894 895 // <READ FRU Data> 896 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 897 ipmi::storage::cmdReadFruData, 898 ipmi::Privilege::Operator, ipmiStorageReadFruData); 899 900 // <Get Repository Info> 901 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 902 ipmi::storage::cmdGetSdrRepositoryInfo, 903 ipmi::Privilege::User, ipmiGetRepositoryInfo); 904 905 // <Reserve SDR Repository> 906 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 907 ipmi::storage::cmdReserveSdrRepository, 908 ipmi::Privilege::User, ipmiSensorReserveSdr); 909 910 // <Get SDR> 911 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr, 912 ipmi_sen_get_sdr, PRIVILEGE_USER); 913 914 #endif 915 916 // Common Handers used by both implementation. 917 918 // <Reserve SEL> 919 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 920 ipmi::storage::cmdReserveSel, ipmi::Privilege::User, 921 ipmiStorageReserveSel); 922 923 ipmi::fru::registerCallbackHandler(); 924 return; 925 } 926