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