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