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