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