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