13f7c5e40SJason M. Bills /* 21a2fbddfSJason M. Bills // Copyright (c) 2017-2019 Intel Corporation 33f7c5e40SJason M. Bills // 43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License"); 53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License. 63f7c5e40SJason M. Bills // You may obtain a copy of the License at 73f7c5e40SJason M. Bills // 83f7c5e40SJason M. Bills // http://www.apache.org/licenses/LICENSE-2.0 93f7c5e40SJason M. Bills // 103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software 113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS, 123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and 143f7c5e40SJason M. Bills // limitations under the License. 153f7c5e40SJason M. Bills */ 163f7c5e40SJason M. Bills 171d4d54ddSJason M. Bills #include <boost/algorithm/string.hpp> 183f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp> 19c04e2e70SJason M. Bills #include <boost/process.hpp> 203f7c5e40SJason M. Bills #include <commandutils.hpp> 211d4d54ddSJason M. Bills #include <filesystem> 22*e8767d26SPatrick Venture #include <functional> 233f7c5e40SJason M. Bills #include <iostream> 2499b78ec8SJason M. Bills #include <ipmi_to_redfish_hooks.hpp> 252a265d54SJames Feist #include <ipmid/api.hpp> 26c04e2e70SJason M. Bills #include <phosphor-ipmi-host/selutility.hpp> 273f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp> 283f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp> 293f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp> 30c04e2e70SJason M. Bills #include <sdrutils.hpp> 31c04e2e70SJason M. Bills #include <stdexcept> 323f7c5e40SJason M. Bills #include <storagecommands.hpp> 3352aaa7d5SJason M. Bills #include <string_view> 343f7c5e40SJason M. Bills 351d4d54ddSJason M. Bills namespace intel_oem::ipmi::sel 361d4d54ddSJason M. Bills { 371d4d54ddSJason M. Bills static const std::filesystem::path selLogDir = "/var/log"; 381d4d54ddSJason M. Bills static const std::string selLogFilename = "ipmi_sel"; 391d4d54ddSJason M. Bills 401d4d54ddSJason M. Bills static int getFileTimestamp(const std::filesystem::path& file) 411d4d54ddSJason M. Bills { 421d4d54ddSJason M. Bills struct stat st; 431d4d54ddSJason M. Bills 441d4d54ddSJason M. Bills if (stat(file.c_str(), &st) >= 0) 451d4d54ddSJason M. Bills { 461d4d54ddSJason M. Bills return st.st_mtime; 471d4d54ddSJason M. Bills } 481d4d54ddSJason M. Bills return ::ipmi::sel::invalidTimeStamp; 491d4d54ddSJason M. Bills } 501d4d54ddSJason M. Bills 511d4d54ddSJason M. Bills namespace erase_time 527944c307SJason M. Bills { 537944c307SJason M. Bills static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time"; 547944c307SJason M. Bills 557944c307SJason M. Bills void save() 567944c307SJason M. Bills { 577944c307SJason M. Bills // open the file, creating it if necessary 587944c307SJason M. Bills int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); 597944c307SJason M. Bills if (fd < 0) 607944c307SJason M. Bills { 617944c307SJason M. Bills std::cerr << "Failed to open file\n"; 627944c307SJason M. Bills return; 637944c307SJason M. Bills } 647944c307SJason M. Bills 657944c307SJason M. Bills // update the file timestamp to the current time 667944c307SJason M. Bills if (futimens(fd, NULL) < 0) 677944c307SJason M. Bills { 687944c307SJason M. Bills std::cerr << "Failed to update timestamp: " 697944c307SJason M. Bills << std::string(strerror(errno)); 707944c307SJason M. Bills } 717944c307SJason M. Bills close(fd); 727944c307SJason M. Bills } 737944c307SJason M. Bills 747944c307SJason M. Bills int get() 757944c307SJason M. Bills { 761d4d54ddSJason M. Bills return getFileTimestamp(selEraseTimestamp); 777944c307SJason M. Bills } 781d4d54ddSJason M. Bills } // namespace erase_time 791d4d54ddSJason M. Bills } // namespace intel_oem::ipmi::sel 807944c307SJason M. Bills 813f7c5e40SJason M. Bills namespace ipmi 823f7c5e40SJason M. Bills { 833f7c5e40SJason M. Bills 843f7c5e40SJason M. Bills namespace storage 853f7c5e40SJason M. Bills { 863f7c5e40SJason M. Bills 87e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64; 883f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16; 893f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map< 903f7c5e40SJason M. Bills sdbusplus::message::object_path, 913f7c5e40SJason M. Bills boost::container::flat_map< 923f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 933f7c5e40SJason M. Bills using ManagedEntry = std::pair< 943f7c5e40SJason M. Bills sdbusplus::message::object_path, 953f7c5e40SJason M. Bills boost::container::flat_map< 963f7c5e40SJason M. Bills std::string, boost::container::flat_map<std::string, DbusVariant>>>; 973f7c5e40SJason M. Bills 983bcba457SJames Feist constexpr static const char* fruDeviceServiceName = 993bcba457SJames Feist "xyz.openbmc_project.FruDevice"; 100e2d1aee3SJason M. Bills constexpr static const size_t cacheTimeoutSeconds = 10; 1013f7c5e40SJason M. Bills 1024ed6f2c1SJason M. Bills // event direction is bit[7] of eventType where 1b = Deassertion event 1034ed6f2c1SJason M. Bills constexpr static const uint8_t deassertionEvent = 0x80; 104c04e2e70SJason M. Bills 1053f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache; 1063f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF; 1073f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF; 1083f7c5e40SJason M. Bills 1093f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr; 1103f7c5e40SJason M. Bills 1113f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a 1123f7c5e40SJason M. Bills // collision to verify our dev-id 1133f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes; 1143f7c5e40SJason M. Bills 115e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor)); 1163f7c5e40SJason M. Bills 1173f7c5e40SJason M. Bills bool writeFru() 1183f7c5e40SJason M. Bills { 11915419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 12015419dd5SVernon Mauery sdbusplus::message::message writeFru = dbus->new_method_call( 1213f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 1223f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "WriteFru"); 1233f7c5e40SJason M. Bills writeFru.append(cacheBus, cacheAddr, fruCache); 1243f7c5e40SJason M. Bills try 1253f7c5e40SJason M. Bills { 12615419dd5SVernon Mauery sdbusplus::message::message writeFruResp = dbus->call(writeFru); 1273f7c5e40SJason M. Bills } 1283f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 1293f7c5e40SJason M. Bills { 1303f7c5e40SJason M. Bills // todo: log sel? 1313f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 1323f7c5e40SJason M. Bills "error writing fru"); 1333f7c5e40SJason M. Bills return false; 1343f7c5e40SJason M. Bills } 1353f7c5e40SJason M. Bills return true; 1363f7c5e40SJason M. Bills } 1373f7c5e40SJason M. Bills 138e2d1aee3SJason M. Bills void createTimer() 139e2d1aee3SJason M. Bills { 140e2d1aee3SJason M. Bills if (cacheTimer == nullptr) 141e2d1aee3SJason M. Bills { 142e2d1aee3SJason M. Bills cacheTimer = std::make_unique<phosphor::Timer>(writeFru); 143e2d1aee3SJason M. Bills } 144e2d1aee3SJason M. Bills } 145e2d1aee3SJason M. Bills 1463f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId) 1473f7c5e40SJason M. Bills { 1483f7c5e40SJason M. Bills static uint8_t lastDevId = 0xFF; 1493f7c5e40SJason M. Bills 1503f7c5e40SJason M. Bills bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired(); 1513f7c5e40SJason M. Bills if (lastDevId == devId && timerRunning) 1523f7c5e40SJason M. Bills { 1533f7c5e40SJason M. Bills return IPMI_CC_OK; // cache already up to date 1543f7c5e40SJason M. Bills } 1553f7c5e40SJason M. Bills // if timer is running, stop it and writeFru manually 1563f7c5e40SJason M. Bills else if (timerRunning) 1573f7c5e40SJason M. Bills { 1583f7c5e40SJason M. Bills cacheTimer->stop(); 1593f7c5e40SJason M. Bills writeFru(); 1603f7c5e40SJason M. Bills } 1613f7c5e40SJason M. Bills 16215419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 16315419dd5SVernon Mauery sdbusplus::message::message getObjects = dbus->new_method_call( 1643f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 1653f7c5e40SJason M. Bills "GetManagedObjects"); 1663f7c5e40SJason M. Bills ManagedObjectType frus; 1673f7c5e40SJason M. Bills try 1683f7c5e40SJason M. Bills { 16915419dd5SVernon Mauery sdbusplus::message::message resp = dbus->call(getObjects); 1703f7c5e40SJason M. Bills resp.read(frus); 1713f7c5e40SJason M. Bills } 1723f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 1733f7c5e40SJason M. Bills { 1743f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>( 1753f7c5e40SJason M. Bills "replaceCacheFru: error getting managed objects"); 1763f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 1773f7c5e40SJason M. Bills } 1783f7c5e40SJason M. Bills 1793f7c5e40SJason M. Bills deviceHashes.clear(); 1803f7c5e40SJason M. Bills 1813f7c5e40SJason M. Bills // hash the object paths to create unique device id's. increment on 1823f7c5e40SJason M. Bills // collision 1833f7c5e40SJason M. Bills std::hash<std::string> hasher; 1843f7c5e40SJason M. Bills for (const auto& fru : frus) 1853f7c5e40SJason M. Bills { 1863f7c5e40SJason M. Bills auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice"); 1873f7c5e40SJason M. Bills if (fruIface == fru.second.end()) 1883f7c5e40SJason M. Bills { 1893f7c5e40SJason M. Bills continue; 1903f7c5e40SJason M. Bills } 1913f7c5e40SJason M. Bills 1923f7c5e40SJason M. Bills auto busFind = fruIface->second.find("BUS"); 1933f7c5e40SJason M. Bills auto addrFind = fruIface->second.find("ADDRESS"); 1943f7c5e40SJason M. Bills if (busFind == fruIface->second.end() || 1953f7c5e40SJason M. Bills addrFind == fruIface->second.end()) 1963f7c5e40SJason M. Bills { 1973f7c5e40SJason M. Bills phosphor::logging::log<phosphor::logging::level::INFO>( 1983f7c5e40SJason M. Bills "fru device missing Bus or Address", 1993f7c5e40SJason M. Bills phosphor::logging::entry("FRU=%s", fru.first.str.c_str())); 2003f7c5e40SJason M. Bills continue; 2013f7c5e40SJason M. Bills } 2023f7c5e40SJason M. Bills 2038166c8d7SVernon Mauery uint8_t fruBus = std::get<uint32_t>(busFind->second); 2048166c8d7SVernon Mauery uint8_t fruAddr = std::get<uint32_t>(addrFind->second); 2053f7c5e40SJason M. Bills 2063f7c5e40SJason M. Bills uint8_t fruHash = 0; 2073f7c5e40SJason M. Bills if (fruBus != 0 || fruAddr != 0) 2083f7c5e40SJason M. Bills { 2093f7c5e40SJason M. Bills fruHash = hasher(fru.first.str); 2103f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for baseboard 2113f7c5e40SJason M. Bills if (fruHash == 0 || fruHash == 0xFF) 2123f7c5e40SJason M. Bills { 2133f7c5e40SJason M. Bills fruHash = 1; 2143f7c5e40SJason M. Bills } 2153f7c5e40SJason M. Bills } 2163f7c5e40SJason M. Bills std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr); 2173f7c5e40SJason M. Bills 2183f7c5e40SJason M. Bills bool emplacePassed = false; 2193f7c5e40SJason M. Bills while (!emplacePassed) 2203f7c5e40SJason M. Bills { 2213f7c5e40SJason M. Bills auto resp = deviceHashes.emplace(fruHash, newDev); 2223f7c5e40SJason M. Bills emplacePassed = resp.second; 2233f7c5e40SJason M. Bills if (!emplacePassed) 2243f7c5e40SJason M. Bills { 2253f7c5e40SJason M. Bills fruHash++; 2263f7c5e40SJason M. Bills // can't be 0xFF based on spec, and 0 is reserved for 2273f7c5e40SJason M. Bills // baseboard 2283f7c5e40SJason M. Bills if (fruHash == 0XFF) 2293f7c5e40SJason M. Bills { 2303f7c5e40SJason M. Bills fruHash = 0x1; 2313f7c5e40SJason M. Bills } 2323f7c5e40SJason M. Bills } 2333f7c5e40SJason M. Bills } 2343f7c5e40SJason M. Bills } 2353f7c5e40SJason M. Bills auto deviceFind = deviceHashes.find(devId); 2363f7c5e40SJason M. Bills if (deviceFind == deviceHashes.end()) 2373f7c5e40SJason M. Bills { 2383f7c5e40SJason M. Bills return IPMI_CC_SENSOR_INVALID; 2393f7c5e40SJason M. Bills } 2403f7c5e40SJason M. Bills 2413f7c5e40SJason M. Bills fruCache.clear(); 24215419dd5SVernon Mauery sdbusplus::message::message getRawFru = dbus->new_method_call( 2433f7c5e40SJason M. Bills fruDeviceServiceName, "/xyz/openbmc_project/FruDevice", 2443f7c5e40SJason M. Bills "xyz.openbmc_project.FruDeviceManager", "GetRawFru"); 2453f7c5e40SJason M. Bills cacheBus = deviceFind->second.first; 2463f7c5e40SJason M. Bills cacheAddr = deviceFind->second.second; 2473f7c5e40SJason M. Bills getRawFru.append(cacheBus, cacheAddr); 2483f7c5e40SJason M. Bills try 2493f7c5e40SJason M. Bills { 25015419dd5SVernon Mauery sdbusplus::message::message getRawResp = dbus->call(getRawFru); 2513f7c5e40SJason M. Bills getRawResp.read(fruCache); 2523f7c5e40SJason M. Bills } 2533f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 2543f7c5e40SJason M. Bills { 2553f7c5e40SJason M. Bills lastDevId = 0xFF; 2563f7c5e40SJason M. Bills cacheBus = 0xFF; 2573f7c5e40SJason M. Bills cacheAddr = 0xFF; 2583f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 2593f7c5e40SJason M. Bills } 2603f7c5e40SJason M. Bills 2613f7c5e40SJason M. Bills lastDevId = devId; 2623f7c5e40SJason M. Bills return IPMI_CC_OK; 2633f7c5e40SJason M. Bills } 2643f7c5e40SJason M. Bills 2655f4194eeSjayaprakash Mutyala /** @brief implements the read FRU data command 2665f4194eeSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 2675f4194eeSjayaprakash Mutyala * @param fruInventoryOffset - FRU Inventory Offset to write 2685f4194eeSjayaprakash Mutyala * @param countToRead - Count to read 2695f4194eeSjayaprakash Mutyala * 2705f4194eeSjayaprakash Mutyala * @returns ipmi completion code plus response data 2715f4194eeSjayaprakash Mutyala * - countWritten - Count written 2725f4194eeSjayaprakash Mutyala */ 2735f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t, // Count 2745f4194eeSjayaprakash Mutyala std::vector<uint8_t> // Requested data 2755f4194eeSjayaprakash Mutyala > 2765f4194eeSjayaprakash Mutyala ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t fruInventoryOffset, 2775f4194eeSjayaprakash Mutyala uint8_t countToRead) 278e2d1aee3SJason M. Bills { 2795f4194eeSjayaprakash Mutyala if (fruDeviceId == 0xFF) 280e2d1aee3SJason M. Bills { 2815f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 282e2d1aee3SJason M. Bills } 283e2d1aee3SJason M. Bills 2845f4194eeSjayaprakash Mutyala ipmi::Cc status = replaceCacheFru(fruDeviceId); 285e2d1aee3SJason M. Bills 2865f4194eeSjayaprakash Mutyala if (status != ipmi::ccSuccess) 2875f4194eeSjayaprakash Mutyala { 2885f4194eeSjayaprakash Mutyala return ipmi::response(status); 289e2d1aee3SJason M. Bills } 290e2d1aee3SJason M. Bills 2915f4194eeSjayaprakash Mutyala size_t fromFruByteLen = 0; 2925f4194eeSjayaprakash Mutyala if (countToRead + fruInventoryOffset < fruCache.size()) 293e2d1aee3SJason M. Bills { 2945f4194eeSjayaprakash Mutyala fromFruByteLen = countToRead; 2955f4194eeSjayaprakash Mutyala } 2965f4194eeSjayaprakash Mutyala else if (fruCache.size() > fruInventoryOffset) 297e2d1aee3SJason M. Bills { 2985f4194eeSjayaprakash Mutyala fromFruByteLen = fruCache.size() - fruInventoryOffset; 2995f4194eeSjayaprakash Mutyala } 3005f4194eeSjayaprakash Mutyala else 3015f4194eeSjayaprakash Mutyala { 3025f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 303e2d1aee3SJason M. Bills } 304e2d1aee3SJason M. Bills 3055f4194eeSjayaprakash Mutyala std::vector<uint8_t> requestedData; 306e2d1aee3SJason M. Bills 3075f4194eeSjayaprakash Mutyala requestedData.insert( 3085f4194eeSjayaprakash Mutyala requestedData.begin(), fruCache.begin() + fruInventoryOffset, 3095f4194eeSjayaprakash Mutyala fruCache.begin() + fruInventoryOffset + fromFruByteLen); 3105f4194eeSjayaprakash Mutyala 3115f4194eeSjayaprakash Mutyala return ipmi::responseSuccess(countToRead, requestedData); 312e2d1aee3SJason M. Bills } 3135f4194eeSjayaprakash Mutyala 3145f4194eeSjayaprakash Mutyala /** @brief implements the write FRU data command 3155f4194eeSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 3165f4194eeSjayaprakash Mutyala * @param fruInventoryOffset - FRU Inventory Offset to write 3175f4194eeSjayaprakash Mutyala * @param dataToWrite - Data to write 3185f4194eeSjayaprakash Mutyala * 3195f4194eeSjayaprakash Mutyala * @returns ipmi completion code plus response data 3205f4194eeSjayaprakash Mutyala * - countWritten - Count written 3215f4194eeSjayaprakash Mutyala */ 3225f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t> 3235f4194eeSjayaprakash Mutyala ipmiStorageWriteFruData(uint8_t fruDeviceId, uint16_t fruInventoryOffset, 3245f4194eeSjayaprakash Mutyala std::vector<uint8_t>& dataToWrite) 3255f4194eeSjayaprakash Mutyala { 3265f4194eeSjayaprakash Mutyala if (fruDeviceId == 0xFF) 3275f4194eeSjayaprakash Mutyala { 3285f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 3295f4194eeSjayaprakash Mutyala } 3305f4194eeSjayaprakash Mutyala 3315f4194eeSjayaprakash Mutyala size_t writeLen = dataToWrite.size(); 3325f4194eeSjayaprakash Mutyala 3335f4194eeSjayaprakash Mutyala ipmi::Cc status = replaceCacheFru(fruDeviceId); 3345f4194eeSjayaprakash Mutyala if (status != ipmi::ccSuccess) 3355f4194eeSjayaprakash Mutyala { 3365f4194eeSjayaprakash Mutyala return ipmi::response(status); 3375f4194eeSjayaprakash Mutyala } 3385f4194eeSjayaprakash Mutyala int lastWriteAddr = fruInventoryOffset + writeLen; 339e2d1aee3SJason M. Bills if (fruCache.size() < lastWriteAddr) 340e2d1aee3SJason M. Bills { 3415f4194eeSjayaprakash Mutyala fruCache.resize(fruInventoryOffset + writeLen); 342e2d1aee3SJason M. Bills } 343e2d1aee3SJason M. Bills 3445f4194eeSjayaprakash Mutyala std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen, 3455f4194eeSjayaprakash Mutyala fruCache.begin() + fruInventoryOffset); 346e2d1aee3SJason M. Bills 347e2d1aee3SJason M. Bills bool atEnd = false; 348e2d1aee3SJason M. Bills 349e2d1aee3SJason M. Bills if (fruCache.size() >= sizeof(FRUHeader)) 350e2d1aee3SJason M. Bills { 351e2d1aee3SJason M. Bills FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data()); 352e2d1aee3SJason M. Bills 353e2d1aee3SJason M. Bills int lastRecordStart = std::max( 354e2d1aee3SJason M. Bills header->internalOffset, 355e2d1aee3SJason M. Bills std::max(header->chassisOffset, 356e2d1aee3SJason M. Bills std::max(header->boardOffset, header->productOffset))); 357e2d1aee3SJason M. Bills // TODO: Handle Multi-Record FRUs? 358e2d1aee3SJason M. Bills 359e2d1aee3SJason M. Bills lastRecordStart *= 8; // header starts in are multiples of 8 bytes 360e2d1aee3SJason M. Bills 361e2d1aee3SJason M. Bills // get the length of the area in multiples of 8 bytes 362e2d1aee3SJason M. Bills if (lastWriteAddr > (lastRecordStart + 1)) 363e2d1aee3SJason M. Bills { 364e2d1aee3SJason M. Bills // second byte in record area is the length 365e2d1aee3SJason M. Bills int areaLength(fruCache[lastRecordStart + 1]); 366e2d1aee3SJason M. Bills areaLength *= 8; // it is in multiples of 8 bytes 367e2d1aee3SJason M. Bills 368e2d1aee3SJason M. Bills if (lastWriteAddr >= (areaLength + lastRecordStart)) 369e2d1aee3SJason M. Bills { 370e2d1aee3SJason M. Bills atEnd = true; 371e2d1aee3SJason M. Bills } 372e2d1aee3SJason M. Bills } 373e2d1aee3SJason M. Bills } 3745f4194eeSjayaprakash Mutyala uint8_t countWritten = 0; 375e2d1aee3SJason M. Bills if (atEnd) 376e2d1aee3SJason M. Bills { 377e2d1aee3SJason M. Bills // cancel timer, we're at the end so might as well send it 378e2d1aee3SJason M. Bills cacheTimer->stop(); 379e2d1aee3SJason M. Bills if (!writeFru()) 380e2d1aee3SJason M. Bills { 3815f4194eeSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 382e2d1aee3SJason M. Bills } 3835f4194eeSjayaprakash Mutyala countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF)); 384e2d1aee3SJason M. Bills } 385e2d1aee3SJason M. Bills else 386e2d1aee3SJason M. Bills { 387e2d1aee3SJason M. Bills // start a timer, if no further data is sent in cacheTimeoutSeconds 388e2d1aee3SJason M. Bills // seconds, check to see if it is valid 389e2d1aee3SJason M. Bills createTimer(); 390e2d1aee3SJason M. Bills cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>( 391e2d1aee3SJason M. Bills std::chrono::seconds(cacheTimeoutSeconds))); 3925f4194eeSjayaprakash Mutyala countWritten = 0; 393e2d1aee3SJason M. Bills } 394e2d1aee3SJason M. Bills 3955f4194eeSjayaprakash Mutyala return ipmi::responseSuccess(countWritten); 396e2d1aee3SJason M. Bills } 397e2d1aee3SJason M. Bills 398d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command 399d33acd6bSjayaprakash Mutyala * @param fruDeviceId - FRU Device ID 400d33acd6bSjayaprakash Mutyala * 401d33acd6bSjayaprakash Mutyala * @returns IPMI completion code plus response data 402d33acd6bSjayaprakash Mutyala * - inventorySize - Number of possible allocation units 403d33acd6bSjayaprakash Mutyala * - accessType - Allocation unit size in bytes. 404d33acd6bSjayaprakash Mutyala */ 405d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize 406d33acd6bSjayaprakash Mutyala uint8_t> // accessType 407d33acd6bSjayaprakash Mutyala ipmiStorageGetFruInvAreaInfo(uint8_t fruDeviceId) 408e2d1aee3SJason M. Bills { 409d33acd6bSjayaprakash Mutyala if (fruDeviceId == 0xFF) 410e2d1aee3SJason M. Bills { 411d33acd6bSjayaprakash Mutyala return ipmi::responseInvalidFieldRequest(); 412e2d1aee3SJason M. Bills } 413e2d1aee3SJason M. Bills 414d33acd6bSjayaprakash Mutyala ipmi::Cc status = replaceCacheFru(fruDeviceId); 415e2d1aee3SJason M. Bills 416e2d1aee3SJason M. Bills if (status != IPMI_CC_OK) 417e2d1aee3SJason M. Bills { 418d33acd6bSjayaprakash Mutyala return ipmi::response(status); 419e2d1aee3SJason M. Bills } 420e2d1aee3SJason M. Bills 421d33acd6bSjayaprakash Mutyala constexpr uint8_t accessType = 422d33acd6bSjayaprakash Mutyala static_cast<uint8_t>(GetFRUAreaAccessType::byte); 423e2d1aee3SJason M. Bills 424d33acd6bSjayaprakash Mutyala return ipmi::responseSuccess(fruCache.size(), accessType); 425e2d1aee3SJason M. Bills } 426e2d1aee3SJason M. Bills 4273f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count) 4283f7c5e40SJason M. Bills { 4293f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); 4303f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 4313f7c5e40SJason M. Bills { 4323f7c5e40SJason M. Bills return ret; 4333f7c5e40SJason M. Bills } 4343f7c5e40SJason M. Bills count = deviceHashes.size(); 4353f7c5e40SJason M. Bills return IPMI_CC_OK; 4363f7c5e40SJason M. Bills } 4373f7c5e40SJason M. Bills 4383f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp) 4393f7c5e40SJason M. Bills { 4403f7c5e40SJason M. Bills ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list 4413f7c5e40SJason M. Bills if (ret != IPMI_CC_OK) 4423f7c5e40SJason M. Bills { 4433f7c5e40SJason M. Bills return ret; 4443f7c5e40SJason M. Bills } 4453f7c5e40SJason M. Bills if (deviceHashes.size() < index) 4463f7c5e40SJason M. Bills { 4473f7c5e40SJason M. Bills return IPMI_CC_INVALID_FIELD_REQUEST; 4483f7c5e40SJason M. Bills } 4493f7c5e40SJason M. Bills auto device = deviceHashes.begin() + index; 4503f7c5e40SJason M. Bills uint8_t& bus = device->second.first; 4513f7c5e40SJason M. Bills uint8_t& address = device->second.second; 4523f7c5e40SJason M. Bills 4533f7c5e40SJason M. Bills ManagedObjectType frus; 4543f7c5e40SJason M. Bills 45515419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 45615419dd5SVernon Mauery sdbusplus::message::message getObjects = dbus->new_method_call( 4573f7c5e40SJason M. Bills fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager", 4583f7c5e40SJason M. Bills "GetManagedObjects"); 4593f7c5e40SJason M. Bills try 4603f7c5e40SJason M. Bills { 46115419dd5SVernon Mauery sdbusplus::message::message resp = dbus->call(getObjects); 4623f7c5e40SJason M. Bills resp.read(frus); 4633f7c5e40SJason M. Bills } 4643f7c5e40SJason M. Bills catch (sdbusplus::exception_t&) 4653f7c5e40SJason M. Bills { 4663f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 4673f7c5e40SJason M. Bills } 4683f7c5e40SJason M. Bills boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr; 4693f7c5e40SJason M. Bills auto fru = 4703f7c5e40SJason M. Bills std::find_if(frus.begin(), frus.end(), 4713f7c5e40SJason M. Bills [bus, address, &fruData](ManagedEntry& entry) { 4723f7c5e40SJason M. Bills auto findFruDevice = 4733f7c5e40SJason M. Bills entry.second.find("xyz.openbmc_project.FruDevice"); 4743f7c5e40SJason M. Bills if (findFruDevice == entry.second.end()) 4753f7c5e40SJason M. Bills { 4763f7c5e40SJason M. Bills return false; 4773f7c5e40SJason M. Bills } 4783f7c5e40SJason M. Bills fruData = &(findFruDevice->second); 4793f7c5e40SJason M. Bills auto findBus = findFruDevice->second.find("BUS"); 4803f7c5e40SJason M. Bills auto findAddress = 4813f7c5e40SJason M. Bills findFruDevice->second.find("ADDRESS"); 4823f7c5e40SJason M. Bills if (findBus == findFruDevice->second.end() || 4833f7c5e40SJason M. Bills findAddress == findFruDevice->second.end()) 4843f7c5e40SJason M. Bills { 4853f7c5e40SJason M. Bills return false; 4863f7c5e40SJason M. Bills } 4878166c8d7SVernon Mauery if (std::get<uint32_t>(findBus->second) != bus) 4883f7c5e40SJason M. Bills { 4893f7c5e40SJason M. Bills return false; 4903f7c5e40SJason M. Bills } 4918166c8d7SVernon Mauery if (std::get<uint32_t>(findAddress->second) != address) 4923f7c5e40SJason M. Bills { 4933f7c5e40SJason M. Bills return false; 4943f7c5e40SJason M. Bills } 4953f7c5e40SJason M. Bills return true; 4963f7c5e40SJason M. Bills }); 4973f7c5e40SJason M. Bills if (fru == frus.end()) 4983f7c5e40SJason M. Bills { 4993f7c5e40SJason M. Bills return IPMI_CC_RESPONSE_ERROR; 5003f7c5e40SJason M. Bills } 5013f7c5e40SJason M. Bills std::string name; 5023f7c5e40SJason M. Bills auto findProductName = fruData->find("BOARD_PRODUCT_NAME"); 5033f7c5e40SJason M. Bills auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME"); 5043f7c5e40SJason M. Bills if (findProductName != fruData->end()) 5053f7c5e40SJason M. Bills { 5068166c8d7SVernon Mauery name = std::get<std::string>(findProductName->second); 5073f7c5e40SJason M. Bills } 5083f7c5e40SJason M. Bills else if (findBoardName != fruData->end()) 5093f7c5e40SJason M. Bills { 5108166c8d7SVernon Mauery name = std::get<std::string>(findBoardName->second); 5113f7c5e40SJason M. Bills } 5123f7c5e40SJason M. Bills else 5133f7c5e40SJason M. Bills { 5143f7c5e40SJason M. Bills name = "UNKNOWN"; 5153f7c5e40SJason M. Bills } 5163f7c5e40SJason M. Bills if (name.size() > maxFruSdrNameSize) 5173f7c5e40SJason M. Bills { 5183f7c5e40SJason M. Bills name = name.substr(0, maxFruSdrNameSize); 5193f7c5e40SJason M. Bills } 5203f7c5e40SJason M. Bills size_t sizeDiff = maxFruSdrNameSize - name.size(); 5213f7c5e40SJason M. Bills 5223f7c5e40SJason M. Bills resp.header.record_id_lsb = 0x0; // calling code is to implement these 5233f7c5e40SJason M. Bills resp.header.record_id_msb = 0x0; 5243f7c5e40SJason M. Bills resp.header.sdr_version = ipmiSdrVersion; 5253f7c5e40SJason M. Bills resp.header.record_type = 0x11; // FRU Device Locator 5263f7c5e40SJason M. Bills resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff; 5273f7c5e40SJason M. Bills resp.key.deviceAddress = 0x20; 5283f7c5e40SJason M. Bills resp.key.fruID = device->first; 5293f7c5e40SJason M. Bills resp.key.accessLun = 0x80; // logical / physical fru device 5303f7c5e40SJason M. Bills resp.key.channelNumber = 0x0; 5313f7c5e40SJason M. Bills resp.body.reserved = 0x0; 5323f7c5e40SJason M. Bills resp.body.deviceType = 0x10; 5334f86d1f2SJames Feist resp.body.deviceTypeModifier = 0x0; 5343f7c5e40SJason M. Bills resp.body.entityID = 0x0; 5353f7c5e40SJason M. Bills resp.body.entityInstance = 0x1; 5363f7c5e40SJason M. Bills resp.body.oem = 0x0; 5373f7c5e40SJason M. Bills resp.body.deviceIDLen = name.size(); 5383f7c5e40SJason M. Bills name.copy(resp.body.deviceID, name.size()); 5393f7c5e40SJason M. Bills 5403f7c5e40SJason M. Bills return IPMI_CC_OK; 5413f7c5e40SJason M. Bills } 542e2d1aee3SJason M. Bills 5431d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles) 544c04e2e70SJason M. Bills { 5451d4d54ddSJason M. Bills // Loop through the directory looking for ipmi_sel log files 5461d4d54ddSJason M. Bills for (const std::filesystem::directory_entry& dirEnt : 5471d4d54ddSJason M. Bills std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir)) 548c04e2e70SJason M. Bills { 5491d4d54ddSJason M. Bills std::string filename = dirEnt.path().filename(); 5501d4d54ddSJason M. Bills if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename)) 5511d4d54ddSJason M. Bills { 5521d4d54ddSJason M. Bills // If we find an ipmi_sel log file, save the path 5531d4d54ddSJason M. Bills selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir / 5541d4d54ddSJason M. Bills filename); 555c04e2e70SJason M. Bills } 556c04e2e70SJason M. Bills } 5571d4d54ddSJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 5581d4d54ddSJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 5591d4d54ddSJason M. Bills // can just sort the list to get them in order from newest to oldest 5601d4d54ddSJason M. Bills std::sort(selLogFiles.begin(), selLogFiles.end()); 561c04e2e70SJason M. Bills 5621d4d54ddSJason M. Bills return !selLogFiles.empty(); 563c04e2e70SJason M. Bills } 564c04e2e70SJason M. Bills 5651d4d54ddSJason M. Bills static int countSELEntries() 5661d4d54ddSJason M. Bills { 5671d4d54ddSJason M. Bills // Get the list of ipmi_sel log files 5681d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 5691d4d54ddSJason M. Bills if (!getSELLogFiles(selLogFiles)) 5701d4d54ddSJason M. Bills { 5711d4d54ddSJason M. Bills return 0; 5721d4d54ddSJason M. Bills } 5731d4d54ddSJason M. Bills int numSELEntries = 0; 5741d4d54ddSJason M. Bills // Loop through each log file and count the number of logs 5751d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 5761d4d54ddSJason M. Bills { 5771d4d54ddSJason M. Bills std::ifstream logStream(file); 5781d4d54ddSJason M. Bills if (!logStream.is_open()) 5791d4d54ddSJason M. Bills { 5801d4d54ddSJason M. Bills continue; 5811d4d54ddSJason M. Bills } 5821d4d54ddSJason M. Bills 5831d4d54ddSJason M. Bills std::string line; 5841d4d54ddSJason M. Bills while (std::getline(logStream, line)) 5851d4d54ddSJason M. Bills { 5861d4d54ddSJason M. Bills numSELEntries++; 5871d4d54ddSJason M. Bills } 5881d4d54ddSJason M. Bills } 5891d4d54ddSJason M. Bills return numSELEntries; 5901d4d54ddSJason M. Bills } 5911d4d54ddSJason M. Bills 5921d4d54ddSJason M. Bills static bool findSELEntry(const int recordID, 593ff7e15b2SPatrick Venture const std::vector<std::filesystem::path>& selLogFiles, 5941d4d54ddSJason M. Bills std::string& entry) 5951d4d54ddSJason M. Bills { 5961d4d54ddSJason M. Bills // Record ID is the first entry field following the timestamp. It is 5971d4d54ddSJason M. Bills // preceded by a space and followed by a comma 5981d4d54ddSJason M. Bills std::string search = " " + std::to_string(recordID) + ","; 5991d4d54ddSJason M. Bills 6001d4d54ddSJason M. Bills // Loop through the ipmi_sel log entries 6011d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 6021d4d54ddSJason M. Bills { 6031d4d54ddSJason M. Bills std::ifstream logStream(file); 6041d4d54ddSJason M. Bills if (!logStream.is_open()) 6051d4d54ddSJason M. Bills { 6061d4d54ddSJason M. Bills continue; 6071d4d54ddSJason M. Bills } 6081d4d54ddSJason M. Bills 6091d4d54ddSJason M. Bills while (std::getline(logStream, entry)) 6101d4d54ddSJason M. Bills { 6111d4d54ddSJason M. Bills // Check if the record ID matches 6121d4d54ddSJason M. Bills if (entry.find(search) != std::string::npos) 6131d4d54ddSJason M. Bills { 6141d4d54ddSJason M. Bills return true; 6151d4d54ddSJason M. Bills } 6161d4d54ddSJason M. Bills } 6171d4d54ddSJason M. Bills } 6181d4d54ddSJason M. Bills return false; 6191d4d54ddSJason M. Bills } 6201d4d54ddSJason M. Bills 6211d4d54ddSJason M. Bills static uint16_t 6221d4d54ddSJason M. Bills getNextRecordID(const uint16_t recordID, 623ff7e15b2SPatrick Venture const std::vector<std::filesystem::path>& selLogFiles) 6241d4d54ddSJason M. Bills { 6251d4d54ddSJason M. Bills uint16_t nextRecordID = recordID + 1; 6261d4d54ddSJason M. Bills std::string entry; 6271d4d54ddSJason M. Bills if (findSELEntry(nextRecordID, selLogFiles, entry)) 6281d4d54ddSJason M. Bills { 6291d4d54ddSJason M. Bills return nextRecordID; 6301d4d54ddSJason M. Bills } 6311d4d54ddSJason M. Bills else 6321d4d54ddSJason M. Bills { 6331d4d54ddSJason M. Bills return ipmi::sel::lastEntry; 6341d4d54ddSJason M. Bills } 635c04e2e70SJason M. Bills } 636c04e2e70SJason M. Bills 637ff7e15b2SPatrick Venture static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data) 638c04e2e70SJason M. Bills { 639c04e2e70SJason M. Bills for (unsigned int i = 0; i < hexStr.size(); i += 2) 640c04e2e70SJason M. Bills { 641c04e2e70SJason M. Bills try 642c04e2e70SJason M. Bills { 643c04e2e70SJason M. Bills data.push_back(static_cast<uint8_t>( 644c04e2e70SJason M. Bills std::stoul(hexStr.substr(i, 2), nullptr, 16))); 645c04e2e70SJason M. Bills } 646c04e2e70SJason M. Bills catch (std::invalid_argument& e) 647c04e2e70SJason M. Bills { 648c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 649c04e2e70SJason M. Bills return -1; 650c04e2e70SJason M. Bills } 651c04e2e70SJason M. Bills catch (std::out_of_range& e) 652c04e2e70SJason M. Bills { 653c04e2e70SJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 654c04e2e70SJason M. Bills return -1; 655c04e2e70SJason M. Bills } 656c04e2e70SJason M. Bills } 657c04e2e70SJason M. Bills return 0; 658c04e2e70SJason M. Bills } 659c04e2e70SJason M. Bills 6601d4d54ddSJason M. Bills ipmi::RspType<uint8_t, // SEL version 6611d4d54ddSJason M. Bills uint16_t, // SEL entry count 6621d4d54ddSJason M. Bills uint16_t, // free space 6631d4d54ddSJason M. Bills uint32_t, // last add timestamp 6641d4d54ddSJason M. Bills uint32_t, // last erase timestamp 6651d4d54ddSJason M. Bills uint8_t> // operation support 6661d4d54ddSJason M. Bills ipmiStorageGetSELInfo() 667c04e2e70SJason M. Bills { 6681d4d54ddSJason M. Bills constexpr uint8_t selVersion = ipmi::sel::selVersion; 6691d4d54ddSJason M. Bills uint16_t entries = countSELEntries(); 6701d4d54ddSJason M. Bills uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp( 6711d4d54ddSJason M. Bills intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename); 6721d4d54ddSJason M. Bills uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get(); 6731d4d54ddSJason M. Bills constexpr uint8_t operationSupport = 6741d4d54ddSJason M. Bills intel_oem::ipmi::sel::selOperationSupport; 6751d4d54ddSJason M. Bills constexpr uint16_t freeSpace = 6761d4d54ddSJason M. Bills 0xffff; // Spec indicates that more than 64kB is free 677c04e2e70SJason M. Bills 6781d4d54ddSJason M. Bills return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp, 6791d4d54ddSJason M. Bills eraseTimeStamp, operationSupport); 680c04e2e70SJason M. Bills } 681c04e2e70SJason M. Bills 6821d4d54ddSJason M. Bills using systemEventType = std::tuple< 6831d4d54ddSJason M. Bills uint32_t, // Timestamp 6841d4d54ddSJason M. Bills uint16_t, // Generator ID 6851d4d54ddSJason M. Bills uint8_t, // EvM Rev 6861d4d54ddSJason M. Bills uint8_t, // Sensor Type 6871d4d54ddSJason M. Bills uint8_t, // Sensor Number 6881d4d54ddSJason M. Bills uint7_t, // Event Type 6891d4d54ddSJason M. Bills bool, // Event Direction 6901d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data 6911d4d54ddSJason M. Bills using oemTsEventType = std::tuple< 6921d4d54ddSJason M. Bills uint32_t, // Timestamp 6931d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data 6941d4d54ddSJason M. Bills using oemEventType = 6951d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data 6961d4d54ddSJason M. Bills 6971d4d54ddSJason M. Bills ipmi::RspType<uint16_t, // Next Record ID 6981d4d54ddSJason M. Bills uint16_t, // Record ID 6991d4d54ddSJason M. Bills uint8_t, // Record Type 7001d4d54ddSJason M. Bills std::variant<systemEventType, oemTsEventType, 7011d4d54ddSJason M. Bills oemEventType>> // Record Content 7021d4d54ddSJason M. Bills ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID, 7031d4d54ddSJason M. Bills uint8_t offset, uint8_t size) 704c04e2e70SJason M. Bills { 7051d4d54ddSJason M. Bills // Only support getting the entire SEL record. If a partial size or non-zero 7061d4d54ddSJason M. Bills // offset is requested, return an error 7071d4d54ddSJason M. Bills if (offset != 0 || size != ipmi::sel::entireRecord) 708c04e2e70SJason M. Bills { 7091d4d54ddSJason M. Bills return ipmi::responseRetBytesUnavailable(); 710c04e2e70SJason M. Bills } 711c04e2e70SJason M. Bills 7121d4d54ddSJason M. Bills // Check the reservation ID if one is provided or required (only if the 7131d4d54ddSJason M. Bills // offset is non-zero) 7141d4d54ddSJason M. Bills if (reservationID != 0 || offset != 0) 715c04e2e70SJason M. Bills { 7161d4d54ddSJason M. Bills if (!checkSELReservation(reservationID)) 717c04e2e70SJason M. Bills { 7181d4d54ddSJason M. Bills return ipmi::responseInvalidReservationId(); 719c04e2e70SJason M. Bills } 720c04e2e70SJason M. Bills } 721c04e2e70SJason M. Bills 7221d4d54ddSJason M. Bills // Get the ipmi_sel log files 7231d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 7241d4d54ddSJason M. Bills if (!getSELLogFiles(selLogFiles)) 725c04e2e70SJason M. Bills { 7261d4d54ddSJason M. Bills return ipmi::responseSensorInvalid(); 727c04e2e70SJason M. Bills } 728c04e2e70SJason M. Bills 7291d4d54ddSJason M. Bills std::string targetEntry; 730c04e2e70SJason M. Bills 731c04e2e70SJason M. Bills if (targetID == ipmi::sel::firstEntry) 732c04e2e70SJason M. Bills { 7331d4d54ddSJason M. Bills // The first entry will be at the top of the oldest log file 7341d4d54ddSJason M. Bills std::ifstream logStream(selLogFiles.back()); 7351d4d54ddSJason M. Bills if (!logStream.is_open()) 736c04e2e70SJason M. Bills { 7371d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 738c04e2e70SJason M. Bills } 7391d4d54ddSJason M. Bills 7401d4d54ddSJason M. Bills if (!std::getline(logStream, targetEntry)) 7411d4d54ddSJason M. Bills { 7421d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 743c04e2e70SJason M. Bills } 744c04e2e70SJason M. Bills } 745c04e2e70SJason M. Bills else if (targetID == ipmi::sel::lastEntry) 746c04e2e70SJason M. Bills { 7471d4d54ddSJason M. Bills // The last entry will be at the bottom of the newest log file 7481d4d54ddSJason M. Bills std::ifstream logStream(selLogFiles.front()); 7491d4d54ddSJason M. Bills if (!logStream.is_open()) 750c04e2e70SJason M. Bills { 7511d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 752c04e2e70SJason M. Bills } 753c04e2e70SJason M. Bills 7541d4d54ddSJason M. Bills std::string line; 7551d4d54ddSJason M. Bills while (std::getline(logStream, line)) 756c04e2e70SJason M. Bills { 7571d4d54ddSJason M. Bills targetEntry = line; 758c04e2e70SJason M. Bills } 759c04e2e70SJason M. Bills } 760c04e2e70SJason M. Bills else 761c04e2e70SJason M. Bills { 7621d4d54ddSJason M. Bills if (!findSELEntry(targetID, selLogFiles, targetEntry)) 763c04e2e70SJason M. Bills { 7641d4d54ddSJason M. Bills return ipmi::responseSensorInvalid(); 7651d4d54ddSJason M. Bills } 766c04e2e70SJason M. Bills } 767c04e2e70SJason M. Bills 76852aaa7d5SJason M. Bills // The format of the ipmi_sel message is "<Timestamp> 76952aaa7d5SJason M. Bills // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]". 77052aaa7d5SJason M. Bills // First get the Timestamp 77152aaa7d5SJason M. Bills size_t space = targetEntry.find_first_of(" "); 77252aaa7d5SJason M. Bills if (space == std::string::npos) 77352aaa7d5SJason M. Bills { 77452aaa7d5SJason M. Bills return ipmi::responseUnspecifiedError(); 77552aaa7d5SJason M. Bills } 77652aaa7d5SJason M. Bills std::string entryTimestamp = targetEntry.substr(0, space); 77752aaa7d5SJason M. Bills // Then get the log contents 77852aaa7d5SJason M. Bills size_t entryStart = targetEntry.find_first_not_of(" ", space); 77952aaa7d5SJason M. Bills if (entryStart == std::string::npos) 78052aaa7d5SJason M. Bills { 78152aaa7d5SJason M. Bills return ipmi::responseUnspecifiedError(); 78252aaa7d5SJason M. Bills } 78352aaa7d5SJason M. Bills std::string_view entry(targetEntry); 78452aaa7d5SJason M. Bills entry.remove_prefix(entryStart); 78552aaa7d5SJason M. Bills // Use split to separate the entry into its fields 7861d4d54ddSJason M. Bills std::vector<std::string> targetEntryFields; 78752aaa7d5SJason M. Bills boost::split(targetEntryFields, entry, boost::is_any_of(","), 7881d4d54ddSJason M. Bills boost::token_compress_on); 78952aaa7d5SJason M. Bills if (targetEntryFields.size() < 3) 7901d4d54ddSJason M. Bills { 7911d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 792c04e2e70SJason M. Bills } 7931a2fbddfSJason M. Bills std::string& recordIDStr = targetEntryFields[0]; 7941a2fbddfSJason M. Bills std::string& recordTypeStr = targetEntryFields[1]; 7951a2fbddfSJason M. Bills std::string& eventDataStr = targetEntryFields[2]; 796c04e2e70SJason M. Bills 7971a2fbddfSJason M. Bills uint16_t recordID; 7981a2fbddfSJason M. Bills uint8_t recordType; 7991a2fbddfSJason M. Bills try 8001a2fbddfSJason M. Bills { 8011a2fbddfSJason M. Bills recordID = std::stoul(recordIDStr); 8021a2fbddfSJason M. Bills recordType = std::stoul(recordTypeStr, nullptr, 16); 8031a2fbddfSJason M. Bills } 8041a2fbddfSJason M. Bills catch (const std::invalid_argument&) 8051a2fbddfSJason M. Bills { 8061a2fbddfSJason M. Bills return ipmi::responseUnspecifiedError(); 8071a2fbddfSJason M. Bills } 8081d4d54ddSJason M. Bills uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles); 8091d4d54ddSJason M. Bills std::vector<uint8_t> eventDataBytes; 8101a2fbddfSJason M. Bills if (fromHexStr(eventDataStr, eventDataBytes) < 0) 8111d4d54ddSJason M. Bills { 8121d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 8131d4d54ddSJason M. Bills } 8141d4d54ddSJason M. Bills 8151d4d54ddSJason M. Bills if (recordType == intel_oem::ipmi::sel::systemEvent) 8161d4d54ddSJason M. Bills { 8171d4d54ddSJason M. Bills // Get the timestamp 8181d4d54ddSJason M. Bills std::tm timeStruct = {}; 81952aaa7d5SJason M. Bills std::istringstream entryStream(entryTimestamp); 8201d4d54ddSJason M. Bills 8211d4d54ddSJason M. Bills uint32_t timestamp = ipmi::sel::invalidTimeStamp; 8221d4d54ddSJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 8231d4d54ddSJason M. Bills { 8241d4d54ddSJason M. Bills timestamp = std::mktime(&timeStruct); 8251d4d54ddSJason M. Bills } 8261d4d54ddSJason M. Bills 8271d4d54ddSJason M. Bills // Set the event message revision 8281d4d54ddSJason M. Bills uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev; 8291d4d54ddSJason M. Bills 8301a2fbddfSJason M. Bills uint16_t generatorID = 0; 8311a2fbddfSJason M. Bills uint8_t sensorType = 0; 8321a2fbddfSJason M. Bills uint8_t sensorNum = 0xFF; 8331a2fbddfSJason M. Bills uint7_t eventType = 0; 8341a2fbddfSJason M. Bills bool eventDir = 0; 8351a2fbddfSJason M. Bills // System type events should have six fields 8361a2fbddfSJason M. Bills if (targetEntryFields.size() >= 6) 8371a2fbddfSJason M. Bills { 8381a2fbddfSJason M. Bills std::string& generatorIDStr = targetEntryFields[3]; 8391a2fbddfSJason M. Bills std::string& sensorPath = targetEntryFields[4]; 8401a2fbddfSJason M. Bills std::string& eventDirStr = targetEntryFields[5]; 8411a2fbddfSJason M. Bills 8421a2fbddfSJason M. Bills // Get the generator ID 8431a2fbddfSJason M. Bills try 8441a2fbddfSJason M. Bills { 8451a2fbddfSJason M. Bills generatorID = std::stoul(generatorIDStr, nullptr, 16); 8461a2fbddfSJason M. Bills } 8471a2fbddfSJason M. Bills catch (const std::invalid_argument&) 8481a2fbddfSJason M. Bills { 8491a2fbddfSJason M. Bills std::cerr << "Invalid Generator ID\n"; 8501a2fbddfSJason M. Bills } 8511a2fbddfSJason M. Bills 8521d4d54ddSJason M. Bills // Get the sensor type, sensor number, and event type for the sensor 8531a2fbddfSJason M. Bills sensorType = getSensorTypeFromPath(sensorPath); 8541a2fbddfSJason M. Bills sensorNum = getSensorNumberFromPath(sensorPath); 8551a2fbddfSJason M. Bills eventType = getSensorEventTypeFromPath(sensorPath); 8561d4d54ddSJason M. Bills 8571d4d54ddSJason M. Bills // Get the event direction 8581a2fbddfSJason M. Bills try 8591a2fbddfSJason M. Bills { 8601a2fbddfSJason M. Bills eventDir = std::stoul(eventDirStr) ? 0 : 1; 8611a2fbddfSJason M. Bills } 8621a2fbddfSJason M. Bills catch (const std::invalid_argument&) 8631a2fbddfSJason M. Bills { 8641a2fbddfSJason M. Bills std::cerr << "Invalid Event Direction\n"; 8651a2fbddfSJason M. Bills } 8661a2fbddfSJason M. Bills } 8671d4d54ddSJason M. Bills 8681d4d54ddSJason M. Bills // Only keep the eventData bytes that fit in the record 8691d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{}; 8701d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 8711d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 8721d4d54ddSJason M. Bills eventData.begin()); 8731d4d54ddSJason M. Bills 8741d4d54ddSJason M. Bills return ipmi::responseSuccess( 8751d4d54ddSJason M. Bills nextRecordID, recordID, recordType, 8761d4d54ddSJason M. Bills systemEventType{timestamp, generatorID, evmRev, sensorType, 8771d4d54ddSJason M. Bills sensorNum, eventType, eventDir, eventData}); 8781d4d54ddSJason M. Bills } 8791d4d54ddSJason M. Bills else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst && 8801d4d54ddSJason M. Bills recordType <= intel_oem::ipmi::sel::oemTsEventLast) 8811d4d54ddSJason M. Bills { 8821d4d54ddSJason M. Bills // Get the timestamp 8831d4d54ddSJason M. Bills std::tm timeStruct = {}; 88452aaa7d5SJason M. Bills std::istringstream entryStream(entryTimestamp); 8851d4d54ddSJason M. Bills 8861d4d54ddSJason M. Bills uint32_t timestamp = ipmi::sel::invalidTimeStamp; 8871d4d54ddSJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 8881d4d54ddSJason M. Bills { 8891d4d54ddSJason M. Bills timestamp = std::mktime(&timeStruct); 8901d4d54ddSJason M. Bills } 8911d4d54ddSJason M. Bills 8921d4d54ddSJason M. Bills // Only keep the bytes that fit in the record 8931d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{}; 8941d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 8951d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 8961d4d54ddSJason M. Bills eventData.begin()); 8971d4d54ddSJason M. Bills 8981d4d54ddSJason M. Bills return ipmi::responseSuccess(nextRecordID, recordID, recordType, 8991d4d54ddSJason M. Bills oemTsEventType{timestamp, eventData}); 9001d4d54ddSJason M. Bills } 9011d4d54ddSJason M. Bills else if (recordType >= intel_oem::ipmi::sel::oemEventFirst && 9021d4d54ddSJason M. Bills recordType <= intel_oem::ipmi::sel::oemEventLast) 9031d4d54ddSJason M. Bills { 9041d4d54ddSJason M. Bills // Only keep the bytes that fit in the record 9051d4d54ddSJason M. Bills std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{}; 9061d4d54ddSJason M. Bills std::copy_n(eventDataBytes.begin(), 9071d4d54ddSJason M. Bills std::min(eventDataBytes.size(), eventData.size()), 9081d4d54ddSJason M. Bills eventData.begin()); 9091d4d54ddSJason M. Bills 9101d4d54ddSJason M. Bills return ipmi::responseSuccess(nextRecordID, recordID, recordType, 9111d4d54ddSJason M. Bills eventData); 9121d4d54ddSJason M. Bills } 9131d4d54ddSJason M. Bills 9141d4d54ddSJason M. Bills return ipmi::responseUnspecifiedError(); 915c04e2e70SJason M. Bills } 916c04e2e70SJason M. Bills 9176dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry( 9186dd8f047SJason M. Bills uint16_t recordID, uint8_t recordType, uint32_t timestamp, 9196dd8f047SJason M. Bills uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum, 9206dd8f047SJason M. Bills uint8_t eventType, uint8_t eventData1, uint8_t eventData2, 9216dd8f047SJason M. Bills uint8_t eventData3) 922c04e2e70SJason M. Bills { 923c04e2e70SJason M. Bills // Per the IPMI spec, need to cancel any reservation when a SEL entry is 924c04e2e70SJason M. Bills // added 925c04e2e70SJason M. Bills cancelSELReservation(); 926c04e2e70SJason M. Bills 9276dd8f047SJason M. Bills // Send this request to the Redfish hooks to log it as a Redfish message 9286dd8f047SJason M. Bills // instead. There is no need to add it to the SEL, so just return success. 9296dd8f047SJason M. Bills intel_oem::ipmi::sel::checkRedfishHooks( 9306dd8f047SJason M. Bills recordID, recordType, timestamp, generatorID, evmRev, sensorType, 9316dd8f047SJason M. Bills sensorNum, eventType, eventData1, eventData2, eventData3); 93299b78ec8SJason M. Bills 9336dd8f047SJason M. Bills uint16_t responseID = 0xFFFF; 9346dd8f047SJason M. Bills return ipmi::responseSuccess(responseID); 935c04e2e70SJason M. Bills } 936c04e2e70SJason M. Bills 9371d4d54ddSJason M. Bills ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx, 9381d4d54ddSJason M. Bills uint16_t reservationID, 9391d4d54ddSJason M. Bills const std::array<uint8_t, 3>& clr, 9401d4d54ddSJason M. Bills uint8_t eraseOperation) 941c04e2e70SJason M. Bills { 9421d4d54ddSJason M. Bills if (!checkSELReservation(reservationID)) 943c04e2e70SJason M. Bills { 9441d4d54ddSJason M. Bills return ipmi::responseInvalidReservationId(); 945c04e2e70SJason M. Bills } 946c04e2e70SJason M. Bills 9471d4d54ddSJason M. Bills static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'}; 9481d4d54ddSJason M. Bills if (clr != clrExpected) 949c04e2e70SJason M. Bills { 9501d4d54ddSJason M. Bills return ipmi::responseInvalidFieldRequest(); 951c04e2e70SJason M. Bills } 952c04e2e70SJason M. Bills 9531d4d54ddSJason M. Bills // Erasure status cannot be fetched, so always return erasure status as 9541d4d54ddSJason M. Bills // `erase completed`. 9551d4d54ddSJason M. Bills if (eraseOperation == ipmi::sel::getEraseStatus) 956c04e2e70SJason M. Bills { 9571d4d54ddSJason M. Bills return ipmi::responseSuccess(ipmi::sel::eraseComplete); 958c04e2e70SJason M. Bills } 959c04e2e70SJason M. Bills 9601d4d54ddSJason M. Bills // Check that initiate erase is correct 9611d4d54ddSJason M. Bills if (eraseOperation != ipmi::sel::initiateErase) 9621d4d54ddSJason M. Bills { 9631d4d54ddSJason M. Bills return ipmi::responseInvalidFieldRequest(); 9641d4d54ddSJason M. Bills } 9651d4d54ddSJason M. Bills 9661d4d54ddSJason M. Bills // Per the IPMI spec, need to cancel any reservation when the SEL is 9671d4d54ddSJason M. Bills // cleared 968c04e2e70SJason M. Bills cancelSELReservation(); 969c04e2e70SJason M. Bills 9707944c307SJason M. Bills // Save the erase time 9717944c307SJason M. Bills intel_oem::ipmi::sel::erase_time::save(); 9727944c307SJason M. Bills 9731d4d54ddSJason M. Bills // Clear the SEL by deleting the log files 9741d4d54ddSJason M. Bills std::vector<std::filesystem::path> selLogFiles; 9751d4d54ddSJason M. Bills if (getSELLogFiles(selLogFiles)) 976c04e2e70SJason M. Bills { 9771d4d54ddSJason M. Bills for (const std::filesystem::path& file : selLogFiles) 9781d4d54ddSJason M. Bills { 9791d4d54ddSJason M. Bills std::error_code ec; 9801d4d54ddSJason M. Bills std::filesystem::remove(file, ec); 981c04e2e70SJason M. Bills } 982c04e2e70SJason M. Bills } 983c04e2e70SJason M. Bills 9841d4d54ddSJason M. Bills // Reload rsyslog so it knows to start new log files 98515419dd5SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); 98615419dd5SVernon Mauery sdbusplus::message::message rsyslogReload = dbus->new_method_call( 9871d4d54ddSJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 9881d4d54ddSJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit"); 9891d4d54ddSJason M. Bills rsyslogReload.append("rsyslog.service", "replace"); 9901d4d54ddSJason M. Bills try 9911d4d54ddSJason M. Bills { 99215419dd5SVernon Mauery sdbusplus::message::message reloadResponse = dbus->call(rsyslogReload); 9931d4d54ddSJason M. Bills } 9941d4d54ddSJason M. Bills catch (sdbusplus::exception_t& e) 9951d4d54ddSJason M. Bills { 9961d4d54ddSJason M. Bills phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 997c04e2e70SJason M. Bills } 998c04e2e70SJason M. Bills 9991d4d54ddSJason M. Bills return ipmi::responseSuccess(ipmi::sel::eraseComplete); 10001d4d54ddSJason M. Bills } 10011d4d54ddSJason M. Bills 10021a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime() 10031a47462eSJason M. Bills { 10041a47462eSJason M. Bills struct timespec selTime = {}; 10051a47462eSJason M. Bills 10061a47462eSJason M. Bills if (clock_gettime(CLOCK_REALTIME, &selTime) < 0) 10071a47462eSJason M. Bills { 10081a47462eSJason M. Bills return ipmi::responseUnspecifiedError(); 10091a47462eSJason M. Bills } 10101a47462eSJason M. Bills 10111a47462eSJason M. Bills return ipmi::responseSuccess(selTime.tv_sec); 10121a47462eSJason M. Bills } 10131a47462eSJason M. Bills 10141d4d54ddSJason M. Bills ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime) 1015cac97a53SJason M. Bills { 1016cac97a53SJason M. Bills // Set SEL Time is not supported 10171d4d54ddSJason M. Bills return ipmi::responseInvalidCommand(); 1018cac97a53SJason M. Bills } 1019cac97a53SJason M. Bills 102074c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId) 102174c50c64SJames Feist { 102274c50c64SJames Feist std::vector<uint8_t> resp; 102374c50c64SJames Feist if (index == 0) 102474c50c64SJames Feist { 102574c50c64SJames Feist Type12Record bmc = {}; 102674c50c64SJames Feist bmc.header.record_id_lsb = recordId; 102774c50c64SJames Feist bmc.header.record_id_msb = recordId >> 8; 102874c50c64SJames Feist bmc.header.sdr_version = ipmiSdrVersion; 102974c50c64SJames Feist bmc.header.record_type = 0x12; 103074c50c64SJames Feist bmc.header.record_length = 0x1b; 103174c50c64SJames Feist bmc.slaveAddress = 0x20; 103274c50c64SJames Feist bmc.channelNumber = 0; 103374c50c64SJames Feist bmc.powerStateNotification = 0; 103474c50c64SJames Feist bmc.deviceCapabilities = 0xBF; 103574c50c64SJames Feist bmc.reserved = 0; 103674c50c64SJames Feist bmc.entityID = 0x2E; 103774c50c64SJames Feist bmc.entityInstance = 1; 103874c50c64SJames Feist bmc.oem = 0; 103974c50c64SJames Feist bmc.typeLengthCode = 0xD0; 104074c50c64SJames Feist std::string bmcName = "Basbrd Mgmt Ctlr"; 104174c50c64SJames Feist std::copy(bmcName.begin(), bmcName.end(), bmc.name); 104274c50c64SJames Feist uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc); 104374c50c64SJames Feist resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record)); 104474c50c64SJames Feist } 104574c50c64SJames Feist else if (index == 1) 104674c50c64SJames Feist { 104774c50c64SJames Feist Type12Record me = {}; 104874c50c64SJames Feist me.header.record_id_lsb = recordId; 104974c50c64SJames Feist me.header.record_id_msb = recordId >> 8; 105074c50c64SJames Feist me.header.sdr_version = ipmiSdrVersion; 105174c50c64SJames Feist me.header.record_type = 0x12; 105274c50c64SJames Feist me.header.record_length = 0x16; 105374c50c64SJames Feist me.slaveAddress = 0x2C; 105474c50c64SJames Feist me.channelNumber = 6; 105574c50c64SJames Feist me.powerStateNotification = 0x24; 105674c50c64SJames Feist me.deviceCapabilities = 0x21; 105774c50c64SJames Feist me.reserved = 0; 105874c50c64SJames Feist me.entityID = 0x2E; 105974c50c64SJames Feist me.entityInstance = 2; 106074c50c64SJames Feist me.oem = 0; 106174c50c64SJames Feist me.typeLengthCode = 0xCB; 106274c50c64SJames Feist std::string meName = "Mgmt Engine"; 106374c50c64SJames Feist std::copy(meName.begin(), meName.end(), me.name); 106474c50c64SJames Feist uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me); 106574c50c64SJames Feist resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record)); 106674c50c64SJames Feist } 106774c50c64SJames Feist else 106874c50c64SJames Feist { 106974c50c64SJames Feist throw std::runtime_error("getType12SDRs:: Illegal index " + 107074c50c64SJames Feist std::to_string(index)); 107174c50c64SJames Feist } 107274c50c64SJames Feist 107374c50c64SJames Feist return resp; 107474c50c64SJames Feist } 107574c50c64SJames Feist 1076e2d1aee3SJason M. Bills void registerStorageFunctions() 1077e2d1aee3SJason M. Bills { 1078e2d1aee3SJason M. Bills // <Get FRU Inventory Area Info> 1079d33acd6bSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage, 1080d33acd6bSjayaprakash Mutyala ipmi::storage::cmdGetFruInventoryAreaInfo, 1081d33acd6bSjayaprakash Mutyala ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo); 1082c04e2e70SJason M. Bills // <READ FRU Data> 10835f4194eeSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 10845f4194eeSjayaprakash Mutyala ipmi::storage::cmdReadFruData, ipmi::Privilege::User, 10855f4194eeSjayaprakash Mutyala ipmiStorageReadFruData); 1086e2d1aee3SJason M. Bills 1087c04e2e70SJason M. Bills // <WRITE FRU Data> 10885f4194eeSjayaprakash Mutyala ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 10895f4194eeSjayaprakash Mutyala ipmi::storage::cmdWriteFruData, 10905f4194eeSjayaprakash Mutyala ipmi::Privilege::Operator, ipmiStorageWriteFruData); 1091c04e2e70SJason M. Bills 1092c04e2e70SJason M. Bills // <Get SEL Info> 10931d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1094542498e9SJason M. Bills ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User, 1095542498e9SJason M. Bills ipmiStorageGetSELInfo); 1096c04e2e70SJason M. Bills 1097c04e2e70SJason M. Bills // <Get SEL Entry> 10981d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1099542498e9SJason M. Bills ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User, 1100542498e9SJason M. Bills ipmiStorageGetSELEntry); 1101c04e2e70SJason M. Bills 1102c04e2e70SJason M. Bills // <Add SEL Entry> 11036dd8f047SJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 110498bbf69aSVernon Mauery ipmi::storage::cmdAddSelEntry, 11056dd8f047SJason M. Bills ipmi::Privilege::Operator, ipmiStorageAddSELEntry); 1106c04e2e70SJason M. Bills 1107c04e2e70SJason M. Bills // <Clear SEL> 11081d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 11091d4d54ddSJason M. Bills ipmi::storage::cmdClearSel, ipmi::Privilege::Operator, 11101d4d54ddSJason M. Bills ipmiStorageClearSEL); 1111cac97a53SJason M. Bills 11121a47462eSJason M. Bills // <Get SEL Time> 11131a47462eSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 1114542498e9SJason M. Bills ipmi::storage::cmdGetSelTime, ipmi::Privilege::User, 1115542498e9SJason M. Bills ipmiStorageGetSELTime); 11161a47462eSJason M. Bills 1117cac97a53SJason M. Bills // <Set SEL Time> 11181d4d54ddSJason M. Bills ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage, 11191d4d54ddSJason M. Bills ipmi::storage::cmdSetSelTime, 11201d4d54ddSJason M. Bills ipmi::Privilege::Operator, ipmiStorageSetSELTime); 1121e2d1aee3SJason M. Bills } 11223f7c5e40SJason M. Bills } // namespace storage 11233f7c5e40SJason M. Bills } // namespace ipmi 1124