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