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