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>
22e8767d26SPatrick 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 
35*9ce789fcSPatrick Venture static constexpr bool DEBUG = false;
36*9ce789fcSPatrick Venture 
371d4d54ddSJason M. Bills namespace intel_oem::ipmi::sel
381d4d54ddSJason M. Bills {
391d4d54ddSJason M. Bills static const std::filesystem::path selLogDir = "/var/log";
401d4d54ddSJason M. Bills static const std::string selLogFilename = "ipmi_sel";
411d4d54ddSJason M. Bills 
421d4d54ddSJason M. Bills static int getFileTimestamp(const std::filesystem::path& file)
431d4d54ddSJason M. Bills {
441d4d54ddSJason M. Bills     struct stat st;
451d4d54ddSJason M. Bills 
461d4d54ddSJason M. Bills     if (stat(file.c_str(), &st) >= 0)
471d4d54ddSJason M. Bills     {
481d4d54ddSJason M. Bills         return st.st_mtime;
491d4d54ddSJason M. Bills     }
501d4d54ddSJason M. Bills     return ::ipmi::sel::invalidTimeStamp;
511d4d54ddSJason M. Bills }
521d4d54ddSJason M. Bills 
531d4d54ddSJason M. Bills namespace erase_time
547944c307SJason M. Bills {
557944c307SJason M. Bills static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time";
567944c307SJason M. Bills 
577944c307SJason M. Bills void save()
587944c307SJason M. Bills {
597944c307SJason M. Bills     // open the file, creating it if necessary
607944c307SJason M. Bills     int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
617944c307SJason M. Bills     if (fd < 0)
627944c307SJason M. Bills     {
637944c307SJason M. Bills         std::cerr << "Failed to open file\n";
647944c307SJason M. Bills         return;
657944c307SJason M. Bills     }
667944c307SJason M. Bills 
677944c307SJason M. Bills     // update the file timestamp to the current time
687944c307SJason M. Bills     if (futimens(fd, NULL) < 0)
697944c307SJason M. Bills     {
707944c307SJason M. Bills         std::cerr << "Failed to update timestamp: "
717944c307SJason M. Bills                   << std::string(strerror(errno));
727944c307SJason M. Bills     }
737944c307SJason M. Bills     close(fd);
747944c307SJason M. Bills }
757944c307SJason M. Bills 
767944c307SJason M. Bills int get()
777944c307SJason M. Bills {
781d4d54ddSJason M. Bills     return getFileTimestamp(selEraseTimestamp);
797944c307SJason M. Bills }
801d4d54ddSJason M. Bills } // namespace erase_time
811d4d54ddSJason M. Bills } // namespace intel_oem::ipmi::sel
827944c307SJason M. Bills 
833f7c5e40SJason M. Bills namespace ipmi
843f7c5e40SJason M. Bills {
853f7c5e40SJason M. Bills 
863f7c5e40SJason M. Bills namespace storage
873f7c5e40SJason M. Bills {
883f7c5e40SJason M. Bills 
89e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64;
903f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16;
913f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map<
923f7c5e40SJason M. Bills     sdbusplus::message::object_path,
933f7c5e40SJason M. Bills     boost::container::flat_map<
943f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
953f7c5e40SJason M. Bills using ManagedEntry = std::pair<
963f7c5e40SJason M. Bills     sdbusplus::message::object_path,
973f7c5e40SJason M. Bills     boost::container::flat_map<
983f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
993f7c5e40SJason M. Bills 
1003bcba457SJames Feist constexpr static const char* fruDeviceServiceName =
1013bcba457SJames Feist     "xyz.openbmc_project.FruDevice";
102*9ce789fcSPatrick Venture constexpr static const char* entityManagerServiceName =
103*9ce789fcSPatrick Venture     "xyz.openbmc_project.EntityManager";
104e2d1aee3SJason M. Bills constexpr static const size_t cacheTimeoutSeconds = 10;
1053f7c5e40SJason M. Bills 
1064ed6f2c1SJason M. Bills // event direction is bit[7] of eventType where 1b = Deassertion event
1074ed6f2c1SJason M. Bills constexpr static const uint8_t deassertionEvent = 0x80;
108c04e2e70SJason M. Bills 
1093f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache;
1103f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF;
1113f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF;
1123f7c5e40SJason M. Bills 
1133f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
1143f7c5e40SJason M. Bills 
1153f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a
1163f7c5e40SJason M. Bills // collision to verify our dev-id
1173f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
1183f7c5e40SJason M. Bills 
119e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor));
1203f7c5e40SJason M. Bills 
1213f7c5e40SJason M. Bills bool writeFru()
1223f7c5e40SJason M. Bills {
12315419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
12415419dd5SVernon Mauery     sdbusplus::message::message writeFru = dbus->new_method_call(
1253f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
1263f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "WriteFru");
1273f7c5e40SJason M. Bills     writeFru.append(cacheBus, cacheAddr, fruCache);
1283f7c5e40SJason M. Bills     try
1293f7c5e40SJason M. Bills     {
13015419dd5SVernon Mauery         sdbusplus::message::message writeFruResp = dbus->call(writeFru);
1313f7c5e40SJason M. Bills     }
1323f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
1333f7c5e40SJason M. Bills     {
1343f7c5e40SJason M. Bills         // todo: log sel?
1353f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
1363f7c5e40SJason M. Bills             "error writing fru");
1373f7c5e40SJason M. Bills         return false;
1383f7c5e40SJason M. Bills     }
1393f7c5e40SJason M. Bills     return true;
1403f7c5e40SJason M. Bills }
1413f7c5e40SJason M. Bills 
142e2d1aee3SJason M. Bills void createTimer()
143e2d1aee3SJason M. Bills {
144e2d1aee3SJason M. Bills     if (cacheTimer == nullptr)
145e2d1aee3SJason M. Bills     {
146e2d1aee3SJason M. Bills         cacheTimer = std::make_unique<phosphor::Timer>(writeFru);
147e2d1aee3SJason M. Bills     }
148e2d1aee3SJason M. Bills }
149e2d1aee3SJason M. Bills 
1503f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId)
1513f7c5e40SJason M. Bills {
1523f7c5e40SJason M. Bills     static uint8_t lastDevId = 0xFF;
1533f7c5e40SJason M. Bills 
1543f7c5e40SJason M. Bills     bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
1553f7c5e40SJason M. Bills     if (lastDevId == devId && timerRunning)
1563f7c5e40SJason M. Bills     {
1573f7c5e40SJason M. Bills         return IPMI_CC_OK; // cache already up to date
1583f7c5e40SJason M. Bills     }
1593f7c5e40SJason M. Bills     // if timer is running, stop it and writeFru manually
1603f7c5e40SJason M. Bills     else if (timerRunning)
1613f7c5e40SJason M. Bills     {
1623f7c5e40SJason M. Bills         cacheTimer->stop();
1633f7c5e40SJason M. Bills         writeFru();
1643f7c5e40SJason M. Bills     }
1653f7c5e40SJason M. Bills 
16615419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
16715419dd5SVernon Mauery     sdbusplus::message::message getObjects = dbus->new_method_call(
1683f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
1693f7c5e40SJason M. Bills         "GetManagedObjects");
1703f7c5e40SJason M. Bills     ManagedObjectType frus;
1713f7c5e40SJason M. Bills     try
1723f7c5e40SJason M. Bills     {
17315419dd5SVernon Mauery         sdbusplus::message::message resp = dbus->call(getObjects);
1743f7c5e40SJason M. Bills         resp.read(frus);
1753f7c5e40SJason M. Bills     }
1763f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
1773f7c5e40SJason M. Bills     {
1783f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
1793f7c5e40SJason M. Bills             "replaceCacheFru: error getting managed objects");
1803f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
1813f7c5e40SJason M. Bills     }
1823f7c5e40SJason M. Bills 
1833f7c5e40SJason M. Bills     deviceHashes.clear();
1843f7c5e40SJason M. Bills 
1853f7c5e40SJason M. Bills     // hash the object paths to create unique device id's. increment on
1863f7c5e40SJason M. Bills     // collision
1873f7c5e40SJason M. Bills     std::hash<std::string> hasher;
1883f7c5e40SJason M. Bills     for (const auto& fru : frus)
1893f7c5e40SJason M. Bills     {
1903f7c5e40SJason M. Bills         auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
1913f7c5e40SJason M. Bills         if (fruIface == fru.second.end())
1923f7c5e40SJason M. Bills         {
1933f7c5e40SJason M. Bills             continue;
1943f7c5e40SJason M. Bills         }
1953f7c5e40SJason M. Bills 
1963f7c5e40SJason M. Bills         auto busFind = fruIface->second.find("BUS");
1973f7c5e40SJason M. Bills         auto addrFind = fruIface->second.find("ADDRESS");
1983f7c5e40SJason M. Bills         if (busFind == fruIface->second.end() ||
1993f7c5e40SJason M. Bills             addrFind == fruIface->second.end())
2003f7c5e40SJason M. Bills         {
2013f7c5e40SJason M. Bills             phosphor::logging::log<phosphor::logging::level::INFO>(
2023f7c5e40SJason M. Bills                 "fru device missing Bus or Address",
2033f7c5e40SJason M. Bills                 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
2043f7c5e40SJason M. Bills             continue;
2053f7c5e40SJason M. Bills         }
2063f7c5e40SJason M. Bills 
2078166c8d7SVernon Mauery         uint8_t fruBus = std::get<uint32_t>(busFind->second);
2088166c8d7SVernon Mauery         uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
2093f7c5e40SJason M. Bills 
2103f7c5e40SJason M. Bills         uint8_t fruHash = 0;
2113f7c5e40SJason M. Bills         if (fruBus != 0 || fruAddr != 0)
2123f7c5e40SJason M. Bills         {
2133f7c5e40SJason M. Bills             fruHash = hasher(fru.first.str);
2143f7c5e40SJason M. Bills             // can't be 0xFF based on spec, and 0 is reserved for baseboard
2153f7c5e40SJason M. Bills             if (fruHash == 0 || fruHash == 0xFF)
2163f7c5e40SJason M. Bills             {
2173f7c5e40SJason M. Bills                 fruHash = 1;
2183f7c5e40SJason M. Bills             }
2193f7c5e40SJason M. Bills         }
2203f7c5e40SJason M. Bills         std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr);
2213f7c5e40SJason M. Bills 
2223f7c5e40SJason M. Bills         bool emplacePassed = false;
2233f7c5e40SJason M. Bills         while (!emplacePassed)
2243f7c5e40SJason M. Bills         {
2253f7c5e40SJason M. Bills             auto resp = deviceHashes.emplace(fruHash, newDev);
2263f7c5e40SJason M. Bills             emplacePassed = resp.second;
2273f7c5e40SJason M. Bills             if (!emplacePassed)
2283f7c5e40SJason M. Bills             {
2293f7c5e40SJason M. Bills                 fruHash++;
2303f7c5e40SJason M. Bills                 // can't be 0xFF based on spec, and 0 is reserved for
2313f7c5e40SJason M. Bills                 // baseboard
2323f7c5e40SJason M. Bills                 if (fruHash == 0XFF)
2333f7c5e40SJason M. Bills                 {
2343f7c5e40SJason M. Bills                     fruHash = 0x1;
2353f7c5e40SJason M. Bills                 }
2363f7c5e40SJason M. Bills             }
2373f7c5e40SJason M. Bills         }
2383f7c5e40SJason M. Bills     }
2393f7c5e40SJason M. Bills     auto deviceFind = deviceHashes.find(devId);
2403f7c5e40SJason M. Bills     if (deviceFind == deviceHashes.end())
2413f7c5e40SJason M. Bills     {
2423f7c5e40SJason M. Bills         return IPMI_CC_SENSOR_INVALID;
2433f7c5e40SJason M. Bills     }
2443f7c5e40SJason M. Bills 
2453f7c5e40SJason M. Bills     fruCache.clear();
24615419dd5SVernon Mauery     sdbusplus::message::message getRawFru = dbus->new_method_call(
2473f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
2483f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
2493f7c5e40SJason M. Bills     cacheBus = deviceFind->second.first;
2503f7c5e40SJason M. Bills     cacheAddr = deviceFind->second.second;
2513f7c5e40SJason M. Bills     getRawFru.append(cacheBus, cacheAddr);
2523f7c5e40SJason M. Bills     try
2533f7c5e40SJason M. Bills     {
25415419dd5SVernon Mauery         sdbusplus::message::message getRawResp = dbus->call(getRawFru);
2553f7c5e40SJason M. Bills         getRawResp.read(fruCache);
2563f7c5e40SJason M. Bills     }
2573f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
2583f7c5e40SJason M. Bills     {
2593f7c5e40SJason M. Bills         lastDevId = 0xFF;
2603f7c5e40SJason M. Bills         cacheBus = 0xFF;
2613f7c5e40SJason M. Bills         cacheAddr = 0xFF;
2623f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
2633f7c5e40SJason M. Bills     }
2643f7c5e40SJason M. Bills 
2653f7c5e40SJason M. Bills     lastDevId = devId;
2663f7c5e40SJason M. Bills     return IPMI_CC_OK;
2673f7c5e40SJason M. Bills }
2683f7c5e40SJason M. Bills 
2695f4194eeSjayaprakash Mutyala /** @brief implements the read FRU data command
2705f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
2715f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
2725f4194eeSjayaprakash Mutyala  *  @param countToRead        - Count to read
2735f4194eeSjayaprakash Mutyala  *
2745f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
2755f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
2765f4194eeSjayaprakash Mutyala  */
2775f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t,             // Count
2785f4194eeSjayaprakash Mutyala               std::vector<uint8_t> // Requested data
2795f4194eeSjayaprakash Mutyala               >
2805f4194eeSjayaprakash Mutyala     ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t fruInventoryOffset,
2815f4194eeSjayaprakash Mutyala                            uint8_t countToRead)
282e2d1aee3SJason M. Bills {
2835f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
284e2d1aee3SJason M. Bills     {
2855f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
286e2d1aee3SJason M. Bills     }
287e2d1aee3SJason M. Bills 
2885f4194eeSjayaprakash Mutyala     ipmi::Cc status = replaceCacheFru(fruDeviceId);
289e2d1aee3SJason M. Bills 
2905f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
2915f4194eeSjayaprakash Mutyala     {
2925f4194eeSjayaprakash Mutyala         return ipmi::response(status);
293e2d1aee3SJason M. Bills     }
294e2d1aee3SJason M. Bills 
2955f4194eeSjayaprakash Mutyala     size_t fromFruByteLen = 0;
2965f4194eeSjayaprakash Mutyala     if (countToRead + fruInventoryOffset < fruCache.size())
297e2d1aee3SJason M. Bills     {
2985f4194eeSjayaprakash Mutyala         fromFruByteLen = countToRead;
2995f4194eeSjayaprakash Mutyala     }
3005f4194eeSjayaprakash Mutyala     else if (fruCache.size() > fruInventoryOffset)
301e2d1aee3SJason M. Bills     {
3025f4194eeSjayaprakash Mutyala         fromFruByteLen = fruCache.size() - fruInventoryOffset;
3035f4194eeSjayaprakash Mutyala     }
3045f4194eeSjayaprakash Mutyala     else
3055f4194eeSjayaprakash Mutyala     {
3065f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
307e2d1aee3SJason M. Bills     }
308e2d1aee3SJason M. Bills 
3095f4194eeSjayaprakash Mutyala     std::vector<uint8_t> requestedData;
310e2d1aee3SJason M. Bills 
3115f4194eeSjayaprakash Mutyala     requestedData.insert(
3125f4194eeSjayaprakash Mutyala         requestedData.begin(), fruCache.begin() + fruInventoryOffset,
3135f4194eeSjayaprakash Mutyala         fruCache.begin() + fruInventoryOffset + fromFruByteLen);
3145f4194eeSjayaprakash Mutyala 
3155f4194eeSjayaprakash Mutyala     return ipmi::responseSuccess(countToRead, requestedData);
316e2d1aee3SJason M. Bills }
3175f4194eeSjayaprakash Mutyala 
3185f4194eeSjayaprakash Mutyala /** @brief implements the write FRU data command
3195f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
3205f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
3215f4194eeSjayaprakash Mutyala  *  @param dataToWrite        - Data to write
3225f4194eeSjayaprakash Mutyala  *
3235f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
3245f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
3255f4194eeSjayaprakash Mutyala  */
3265f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t>
3275f4194eeSjayaprakash Mutyala     ipmiStorageWriteFruData(uint8_t fruDeviceId, uint16_t fruInventoryOffset,
3285f4194eeSjayaprakash Mutyala                             std::vector<uint8_t>& dataToWrite)
3295f4194eeSjayaprakash Mutyala {
3305f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
3315f4194eeSjayaprakash Mutyala     {
3325f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
3335f4194eeSjayaprakash Mutyala     }
3345f4194eeSjayaprakash Mutyala 
3355f4194eeSjayaprakash Mutyala     size_t writeLen = dataToWrite.size();
3365f4194eeSjayaprakash Mutyala 
3375f4194eeSjayaprakash Mutyala     ipmi::Cc status = replaceCacheFru(fruDeviceId);
3385f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
3395f4194eeSjayaprakash Mutyala     {
3405f4194eeSjayaprakash Mutyala         return ipmi::response(status);
3415f4194eeSjayaprakash Mutyala     }
3425f4194eeSjayaprakash Mutyala     int lastWriteAddr = fruInventoryOffset + writeLen;
343e2d1aee3SJason M. Bills     if (fruCache.size() < lastWriteAddr)
344e2d1aee3SJason M. Bills     {
3455f4194eeSjayaprakash Mutyala         fruCache.resize(fruInventoryOffset + writeLen);
346e2d1aee3SJason M. Bills     }
347e2d1aee3SJason M. Bills 
3485f4194eeSjayaprakash Mutyala     std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
3495f4194eeSjayaprakash Mutyala               fruCache.begin() + fruInventoryOffset);
350e2d1aee3SJason M. Bills 
351e2d1aee3SJason M. Bills     bool atEnd = false;
352e2d1aee3SJason M. Bills 
353e2d1aee3SJason M. Bills     if (fruCache.size() >= sizeof(FRUHeader))
354e2d1aee3SJason M. Bills     {
355e2d1aee3SJason M. Bills         FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
356e2d1aee3SJason M. Bills 
357e2d1aee3SJason M. Bills         int lastRecordStart = std::max(
358e2d1aee3SJason M. Bills             header->internalOffset,
359e2d1aee3SJason M. Bills             std::max(header->chassisOffset,
360e2d1aee3SJason M. Bills                      std::max(header->boardOffset, header->productOffset)));
361e2d1aee3SJason M. Bills         // TODO: Handle Multi-Record FRUs?
362e2d1aee3SJason M. Bills 
363e2d1aee3SJason M. Bills         lastRecordStart *= 8; // header starts in are multiples of 8 bytes
364e2d1aee3SJason M. Bills 
365e2d1aee3SJason M. Bills         // get the length of the area in multiples of 8 bytes
366e2d1aee3SJason M. Bills         if (lastWriteAddr > (lastRecordStart + 1))
367e2d1aee3SJason M. Bills         {
368e2d1aee3SJason M. Bills             // second byte in record area is the length
369e2d1aee3SJason M. Bills             int areaLength(fruCache[lastRecordStart + 1]);
370e2d1aee3SJason M. Bills             areaLength *= 8; // it is in multiples of 8 bytes
371e2d1aee3SJason M. Bills 
372e2d1aee3SJason M. Bills             if (lastWriteAddr >= (areaLength + lastRecordStart))
373e2d1aee3SJason M. Bills             {
374e2d1aee3SJason M. Bills                 atEnd = true;
375e2d1aee3SJason M. Bills             }
376e2d1aee3SJason M. Bills         }
377e2d1aee3SJason M. Bills     }
3785f4194eeSjayaprakash Mutyala     uint8_t countWritten = 0;
379e2d1aee3SJason M. Bills     if (atEnd)
380e2d1aee3SJason M. Bills     {
381e2d1aee3SJason M. Bills         // cancel timer, we're at the end so might as well send it
382e2d1aee3SJason M. Bills         cacheTimer->stop();
383e2d1aee3SJason M. Bills         if (!writeFru())
384e2d1aee3SJason M. Bills         {
3855f4194eeSjayaprakash Mutyala             return ipmi::responseInvalidFieldRequest();
386e2d1aee3SJason M. Bills         }
3875f4194eeSjayaprakash Mutyala         countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF));
388e2d1aee3SJason M. Bills     }
389e2d1aee3SJason M. Bills     else
390e2d1aee3SJason M. Bills     {
391e2d1aee3SJason M. Bills         // start a timer, if no further data is sent in cacheTimeoutSeconds
392e2d1aee3SJason M. Bills         // seconds, check to see if it is valid
393e2d1aee3SJason M. Bills         createTimer();
394e2d1aee3SJason M. Bills         cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
395e2d1aee3SJason M. Bills             std::chrono::seconds(cacheTimeoutSeconds)));
3965f4194eeSjayaprakash Mutyala         countWritten = 0;
397e2d1aee3SJason M. Bills     }
398e2d1aee3SJason M. Bills 
3995f4194eeSjayaprakash Mutyala     return ipmi::responseSuccess(countWritten);
400e2d1aee3SJason M. Bills }
401e2d1aee3SJason M. Bills 
402d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command
403d33acd6bSjayaprakash Mutyala  *  @param fruDeviceId  - FRU Device ID
404d33acd6bSjayaprakash Mutyala  *
405d33acd6bSjayaprakash Mutyala  *  @returns IPMI completion code plus response data
406d33acd6bSjayaprakash Mutyala  *   - inventorySize - Number of possible allocation units
407d33acd6bSjayaprakash Mutyala  *   - accessType    - Allocation unit size in bytes.
408d33acd6bSjayaprakash Mutyala  */
409d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize
410d33acd6bSjayaprakash Mutyala               uint8_t>  // accessType
411d33acd6bSjayaprakash Mutyala     ipmiStorageGetFruInvAreaInfo(uint8_t fruDeviceId)
412e2d1aee3SJason M. Bills {
413d33acd6bSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
414e2d1aee3SJason M. Bills     {
415d33acd6bSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
416e2d1aee3SJason M. Bills     }
417e2d1aee3SJason M. Bills 
418d33acd6bSjayaprakash Mutyala     ipmi::Cc status = replaceCacheFru(fruDeviceId);
419e2d1aee3SJason M. Bills 
420e2d1aee3SJason M. Bills     if (status != IPMI_CC_OK)
421e2d1aee3SJason M. Bills     {
422d33acd6bSjayaprakash Mutyala         return ipmi::response(status);
423e2d1aee3SJason M. Bills     }
424e2d1aee3SJason M. Bills 
425d33acd6bSjayaprakash Mutyala     constexpr uint8_t accessType =
426d33acd6bSjayaprakash Mutyala         static_cast<uint8_t>(GetFRUAreaAccessType::byte);
427e2d1aee3SJason M. Bills 
428d33acd6bSjayaprakash Mutyala     return ipmi::responseSuccess(fruCache.size(), accessType);
429e2d1aee3SJason M. Bills }
430e2d1aee3SJason M. Bills 
4313f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count)
4323f7c5e40SJason M. Bills {
4333f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0);
4343f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
4353f7c5e40SJason M. Bills     {
4363f7c5e40SJason M. Bills         return ret;
4373f7c5e40SJason M. Bills     }
4383f7c5e40SJason M. Bills     count = deviceHashes.size();
4393f7c5e40SJason M. Bills     return IPMI_CC_OK;
4403f7c5e40SJason M. Bills }
4413f7c5e40SJason M. Bills 
4423f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
4433f7c5e40SJason M. Bills {
4443f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
4453f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
4463f7c5e40SJason M. Bills     {
4473f7c5e40SJason M. Bills         return ret;
4483f7c5e40SJason M. Bills     }
4493f7c5e40SJason M. Bills     if (deviceHashes.size() < index)
4503f7c5e40SJason M. Bills     {
4513f7c5e40SJason M. Bills         return IPMI_CC_INVALID_FIELD_REQUEST;
4523f7c5e40SJason M. Bills     }
4533f7c5e40SJason M. Bills     auto device = deviceHashes.begin() + index;
4543f7c5e40SJason M. Bills     uint8_t& bus = device->second.first;
4553f7c5e40SJason M. Bills     uint8_t& address = device->second.second;
4563f7c5e40SJason M. Bills 
4573f7c5e40SJason M. Bills     ManagedObjectType frus;
4583f7c5e40SJason M. Bills 
45915419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
46015419dd5SVernon Mauery     sdbusplus::message::message getObjects = dbus->new_method_call(
4613f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
4623f7c5e40SJason M. Bills         "GetManagedObjects");
4633f7c5e40SJason M. Bills     try
4643f7c5e40SJason M. Bills     {
46515419dd5SVernon Mauery         sdbusplus::message::message resp = dbus->call(getObjects);
4663f7c5e40SJason M. Bills         resp.read(frus);
4673f7c5e40SJason M. Bills     }
4683f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
4693f7c5e40SJason M. Bills     {
4703f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
4713f7c5e40SJason M. Bills     }
4723f7c5e40SJason M. Bills     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
4733f7c5e40SJason M. Bills     auto fru =
4743f7c5e40SJason M. Bills         std::find_if(frus.begin(), frus.end(),
4753f7c5e40SJason M. Bills                      [bus, address, &fruData](ManagedEntry& entry) {
4763f7c5e40SJason M. Bills                          auto findFruDevice =
4773f7c5e40SJason M. Bills                              entry.second.find("xyz.openbmc_project.FruDevice");
4783f7c5e40SJason M. Bills                          if (findFruDevice == entry.second.end())
4793f7c5e40SJason M. Bills                          {
4803f7c5e40SJason M. Bills                              return false;
4813f7c5e40SJason M. Bills                          }
4823f7c5e40SJason M. Bills                          fruData = &(findFruDevice->second);
4833f7c5e40SJason M. Bills                          auto findBus = findFruDevice->second.find("BUS");
4843f7c5e40SJason M. Bills                          auto findAddress =
4853f7c5e40SJason M. Bills                              findFruDevice->second.find("ADDRESS");
4863f7c5e40SJason M. Bills                          if (findBus == findFruDevice->second.end() ||
4873f7c5e40SJason M. Bills                              findAddress == findFruDevice->second.end())
4883f7c5e40SJason M. Bills                          {
4893f7c5e40SJason M. Bills                              return false;
4903f7c5e40SJason M. Bills                          }
4918166c8d7SVernon Mauery                          if (std::get<uint32_t>(findBus->second) != bus)
4923f7c5e40SJason M. Bills                          {
4933f7c5e40SJason M. Bills                              return false;
4943f7c5e40SJason M. Bills                          }
4958166c8d7SVernon Mauery                          if (std::get<uint32_t>(findAddress->second) != address)
4963f7c5e40SJason M. Bills                          {
4973f7c5e40SJason M. Bills                              return false;
4983f7c5e40SJason M. Bills                          }
4993f7c5e40SJason M. Bills                          return true;
5003f7c5e40SJason M. Bills                      });
5013f7c5e40SJason M. Bills     if (fru == frus.end())
5023f7c5e40SJason M. Bills     {
5033f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
5043f7c5e40SJason M. Bills     }
505*9ce789fcSPatrick Venture 
506*9ce789fcSPatrick Venture     boost::container::flat_map<std::string, DbusVariant>* entityData = nullptr;
507*9ce789fcSPatrick Venture     ManagedObjectType entities;
508*9ce789fcSPatrick Venture 
509*9ce789fcSPatrick Venture     try
510*9ce789fcSPatrick Venture     {
511*9ce789fcSPatrick Venture         std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
512*9ce789fcSPatrick Venture 
513*9ce789fcSPatrick Venture         sdbusplus::message::message getObjects = dbus->new_method_call(
514*9ce789fcSPatrick Venture             entityManagerServiceName, "/", "org.freedesktop.DBus.ObjectManager",
515*9ce789fcSPatrick Venture             "GetManagedObjects");
516*9ce789fcSPatrick Venture 
517*9ce789fcSPatrick Venture         sdbusplus::message::message resp = dbus->call(getObjects);
518*9ce789fcSPatrick Venture         resp.read(entities);
519*9ce789fcSPatrick Venture 
520*9ce789fcSPatrick Venture         auto entity = std::find_if(
521*9ce789fcSPatrick Venture             entities.begin(), entities.end(),
522*9ce789fcSPatrick Venture             [bus, address, &entityData](ManagedEntry& entry) {
523*9ce789fcSPatrick Venture                 auto findFruDevice = entry.second.find(
524*9ce789fcSPatrick Venture                     "xyz.openbmc_project.Inventory.Decorator.FruDevice");
525*9ce789fcSPatrick Venture                 if (findFruDevice == entry.second.end())
526*9ce789fcSPatrick Venture                 {
527*9ce789fcSPatrick Venture                     return false;
528*9ce789fcSPatrick Venture                 }
529*9ce789fcSPatrick Venture 
530*9ce789fcSPatrick Venture                 // Integer fields added via Entity-Manager json are uint64_ts by
531*9ce789fcSPatrick Venture                 // default.
532*9ce789fcSPatrick Venture                 auto findBus = findFruDevice->second.find("Bus");
533*9ce789fcSPatrick Venture                 auto findAddress = findFruDevice->second.find("Address");
534*9ce789fcSPatrick Venture 
535*9ce789fcSPatrick Venture                 if (findBus == findFruDevice->second.end() ||
536*9ce789fcSPatrick Venture                     findAddress == findFruDevice->second.end())
537*9ce789fcSPatrick Venture                 {
538*9ce789fcSPatrick Venture                     return false;
539*9ce789fcSPatrick Venture                 }
540*9ce789fcSPatrick Venture                 if ((std::get<uint64_t>(findBus->second) != bus) ||
541*9ce789fcSPatrick Venture                     (std::get<uint64_t>(findAddress->second) != address))
542*9ce789fcSPatrick Venture                 {
543*9ce789fcSPatrick Venture                     return false;
544*9ce789fcSPatrick Venture                 }
545*9ce789fcSPatrick Venture 
546*9ce789fcSPatrick Venture                 auto findIpmiDevice = entry.second.find(
547*9ce789fcSPatrick Venture                     "xyz.openbmc_project.Inventory.Decorator.Ipmi");
548*9ce789fcSPatrick Venture                 if (findIpmiDevice == entry.second.end())
549*9ce789fcSPatrick Venture                 {
550*9ce789fcSPatrick Venture                     return false;
551*9ce789fcSPatrick Venture                 }
552*9ce789fcSPatrick Venture 
553*9ce789fcSPatrick Venture                 entityData = &(findIpmiDevice->second);
554*9ce789fcSPatrick Venture                 return true;
555*9ce789fcSPatrick Venture             });
556*9ce789fcSPatrick Venture 
557*9ce789fcSPatrick Venture         if (entity == entities.end())
558*9ce789fcSPatrick Venture         {
559*9ce789fcSPatrick Venture             if constexpr (DEBUG)
560*9ce789fcSPatrick Venture             {
561*9ce789fcSPatrick Venture                 std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
562*9ce789fcSPatrick Venture                                      "not found for Fru\n");
563*9ce789fcSPatrick Venture             }
564*9ce789fcSPatrick Venture         }
565*9ce789fcSPatrick Venture     }
566*9ce789fcSPatrick Venture     catch (const std::exception& e)
567*9ce789fcSPatrick Venture     {
568*9ce789fcSPatrick Venture         std::fprintf(
569*9ce789fcSPatrick Venture             stderr,
570*9ce789fcSPatrick Venture             "Search for FruDevice+Ipmi Decorator Interface excepted: '%s'\n",
571*9ce789fcSPatrick Venture             e.what());
572*9ce789fcSPatrick Venture     }
573*9ce789fcSPatrick Venture 
5743f7c5e40SJason M. Bills     std::string name;
5753f7c5e40SJason M. Bills     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
5763f7c5e40SJason M. Bills     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
5773f7c5e40SJason M. Bills     if (findProductName != fruData->end())
5783f7c5e40SJason M. Bills     {
5798166c8d7SVernon Mauery         name = std::get<std::string>(findProductName->second);
5803f7c5e40SJason M. Bills     }
5813f7c5e40SJason M. Bills     else if (findBoardName != fruData->end())
5823f7c5e40SJason M. Bills     {
5838166c8d7SVernon Mauery         name = std::get<std::string>(findBoardName->second);
5843f7c5e40SJason M. Bills     }
5853f7c5e40SJason M. Bills     else
5863f7c5e40SJason M. Bills     {
5873f7c5e40SJason M. Bills         name = "UNKNOWN";
5883f7c5e40SJason M. Bills     }
5893f7c5e40SJason M. Bills     if (name.size() > maxFruSdrNameSize)
5903f7c5e40SJason M. Bills     {
5913f7c5e40SJason M. Bills         name = name.substr(0, maxFruSdrNameSize);
5923f7c5e40SJason M. Bills     }
5933f7c5e40SJason M. Bills     size_t sizeDiff = maxFruSdrNameSize - name.size();
5943f7c5e40SJason M. Bills 
5953f7c5e40SJason M. Bills     resp.header.record_id_lsb = 0x0; // calling code is to implement these
5963f7c5e40SJason M. Bills     resp.header.record_id_msb = 0x0;
5973f7c5e40SJason M. Bills     resp.header.sdr_version = ipmiSdrVersion;
59873d0135dSPatrick Venture     resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
5993f7c5e40SJason M. Bills     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
6003f7c5e40SJason M. Bills     resp.key.deviceAddress = 0x20;
6013f7c5e40SJason M. Bills     resp.key.fruID = device->first;
6023f7c5e40SJason M. Bills     resp.key.accessLun = 0x80; // logical / physical fru device
6033f7c5e40SJason M. Bills     resp.key.channelNumber = 0x0;
6043f7c5e40SJason M. Bills     resp.body.reserved = 0x0;
6053f7c5e40SJason M. Bills     resp.body.deviceType = 0x10;
6064f86d1f2SJames Feist     resp.body.deviceTypeModifier = 0x0;
607*9ce789fcSPatrick Venture 
608*9ce789fcSPatrick Venture     uint8_t entityID = 0;
609*9ce789fcSPatrick Venture     uint8_t entityInstance = 0x1;
610*9ce789fcSPatrick Venture 
611*9ce789fcSPatrick Venture     if (entityData)
612*9ce789fcSPatrick Venture     {
613*9ce789fcSPatrick Venture         auto entityIdProperty = entityData->find("EntityId");
614*9ce789fcSPatrick Venture         auto entityInstanceProperty = entityData->find("EntityInstance");
615*9ce789fcSPatrick Venture 
616*9ce789fcSPatrick Venture         if (entityIdProperty != entityData->end())
617*9ce789fcSPatrick Venture         {
618*9ce789fcSPatrick Venture             entityID = static_cast<uint8_t>(
619*9ce789fcSPatrick Venture                 std::get<uint64_t>(entityIdProperty->second));
620*9ce789fcSPatrick Venture         }
621*9ce789fcSPatrick Venture         if (entityInstanceProperty != entityData->end())
622*9ce789fcSPatrick Venture         {
623*9ce789fcSPatrick Venture             entityInstance = static_cast<uint8_t>(
624*9ce789fcSPatrick Venture                 std::get<uint64_t>(entityInstanceProperty->second));
625*9ce789fcSPatrick Venture         }
626*9ce789fcSPatrick Venture     }
627*9ce789fcSPatrick Venture 
628*9ce789fcSPatrick Venture     resp.body.entityID = entityID;
629*9ce789fcSPatrick Venture     resp.body.entityInstance = entityInstance;
630*9ce789fcSPatrick Venture 
6313f7c5e40SJason M. Bills     resp.body.oem = 0x0;
6323f7c5e40SJason M. Bills     resp.body.deviceIDLen = name.size();
6333f7c5e40SJason M. Bills     name.copy(resp.body.deviceID, name.size());
6343f7c5e40SJason M. Bills 
6353f7c5e40SJason M. Bills     return IPMI_CC_OK;
6363f7c5e40SJason M. Bills }
637e2d1aee3SJason M. Bills 
6381d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
639c04e2e70SJason M. Bills {
6401d4d54ddSJason M. Bills     // Loop through the directory looking for ipmi_sel log files
6411d4d54ddSJason M. Bills     for (const std::filesystem::directory_entry& dirEnt :
6421d4d54ddSJason M. Bills          std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir))
643c04e2e70SJason M. Bills     {
6441d4d54ddSJason M. Bills         std::string filename = dirEnt.path().filename();
6451d4d54ddSJason M. Bills         if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename))
6461d4d54ddSJason M. Bills         {
6471d4d54ddSJason M. Bills             // If we find an ipmi_sel log file, save the path
6481d4d54ddSJason M. Bills             selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir /
6491d4d54ddSJason M. Bills                                      filename);
650c04e2e70SJason M. Bills         }
651c04e2e70SJason M. Bills     }
6521d4d54ddSJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
6531d4d54ddSJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
6541d4d54ddSJason M. Bills     // can just sort the list to get them in order from newest to oldest
6551d4d54ddSJason M. Bills     std::sort(selLogFiles.begin(), selLogFiles.end());
656c04e2e70SJason M. Bills 
6571d4d54ddSJason M. Bills     return !selLogFiles.empty();
658c04e2e70SJason M. Bills }
659c04e2e70SJason M. Bills 
6601d4d54ddSJason M. Bills static int countSELEntries()
6611d4d54ddSJason M. Bills {
6621d4d54ddSJason M. Bills     // Get the list of ipmi_sel log files
6631d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
6641d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
6651d4d54ddSJason M. Bills     {
6661d4d54ddSJason M. Bills         return 0;
6671d4d54ddSJason M. Bills     }
6681d4d54ddSJason M. Bills     int numSELEntries = 0;
6691d4d54ddSJason M. Bills     // Loop through each log file and count the number of logs
6701d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
6711d4d54ddSJason M. Bills     {
6721d4d54ddSJason M. Bills         std::ifstream logStream(file);
6731d4d54ddSJason M. Bills         if (!logStream.is_open())
6741d4d54ddSJason M. Bills         {
6751d4d54ddSJason M. Bills             continue;
6761d4d54ddSJason M. Bills         }
6771d4d54ddSJason M. Bills 
6781d4d54ddSJason M. Bills         std::string line;
6791d4d54ddSJason M. Bills         while (std::getline(logStream, line))
6801d4d54ddSJason M. Bills         {
6811d4d54ddSJason M. Bills             numSELEntries++;
6821d4d54ddSJason M. Bills         }
6831d4d54ddSJason M. Bills     }
6841d4d54ddSJason M. Bills     return numSELEntries;
6851d4d54ddSJason M. Bills }
6861d4d54ddSJason M. Bills 
6871d4d54ddSJason M. Bills static bool findSELEntry(const int recordID,
688ff7e15b2SPatrick Venture                          const std::vector<std::filesystem::path>& selLogFiles,
6891d4d54ddSJason M. Bills                          std::string& entry)
6901d4d54ddSJason M. Bills {
6911d4d54ddSJason M. Bills     // Record ID is the first entry field following the timestamp. It is
6921d4d54ddSJason M. Bills     // preceded by a space and followed by a comma
6931d4d54ddSJason M. Bills     std::string search = " " + std::to_string(recordID) + ",";
6941d4d54ddSJason M. Bills 
6951d4d54ddSJason M. Bills     // Loop through the ipmi_sel log entries
6961d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
6971d4d54ddSJason M. Bills     {
6981d4d54ddSJason M. Bills         std::ifstream logStream(file);
6991d4d54ddSJason M. Bills         if (!logStream.is_open())
7001d4d54ddSJason M. Bills         {
7011d4d54ddSJason M. Bills             continue;
7021d4d54ddSJason M. Bills         }
7031d4d54ddSJason M. Bills 
7041d4d54ddSJason M. Bills         while (std::getline(logStream, entry))
7051d4d54ddSJason M. Bills         {
7061d4d54ddSJason M. Bills             // Check if the record ID matches
7071d4d54ddSJason M. Bills             if (entry.find(search) != std::string::npos)
7081d4d54ddSJason M. Bills             {
7091d4d54ddSJason M. Bills                 return true;
7101d4d54ddSJason M. Bills             }
7111d4d54ddSJason M. Bills         }
7121d4d54ddSJason M. Bills     }
7131d4d54ddSJason M. Bills     return false;
7141d4d54ddSJason M. Bills }
7151d4d54ddSJason M. Bills 
7161d4d54ddSJason M. Bills static uint16_t
7171d4d54ddSJason M. Bills     getNextRecordID(const uint16_t recordID,
718ff7e15b2SPatrick Venture                     const std::vector<std::filesystem::path>& selLogFiles)
7191d4d54ddSJason M. Bills {
7201d4d54ddSJason M. Bills     uint16_t nextRecordID = recordID + 1;
7211d4d54ddSJason M. Bills     std::string entry;
7221d4d54ddSJason M. Bills     if (findSELEntry(nextRecordID, selLogFiles, entry))
7231d4d54ddSJason M. Bills     {
7241d4d54ddSJason M. Bills         return nextRecordID;
7251d4d54ddSJason M. Bills     }
7261d4d54ddSJason M. Bills     else
7271d4d54ddSJason M. Bills     {
7281d4d54ddSJason M. Bills         return ipmi::sel::lastEntry;
7291d4d54ddSJason M. Bills     }
730c04e2e70SJason M. Bills }
731c04e2e70SJason M. Bills 
732ff7e15b2SPatrick Venture static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
733c04e2e70SJason M. Bills {
734c04e2e70SJason M. Bills     for (unsigned int i = 0; i < hexStr.size(); i += 2)
735c04e2e70SJason M. Bills     {
736c04e2e70SJason M. Bills         try
737c04e2e70SJason M. Bills         {
738c04e2e70SJason M. Bills             data.push_back(static_cast<uint8_t>(
739c04e2e70SJason M. Bills                 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
740c04e2e70SJason M. Bills         }
741c04e2e70SJason M. Bills         catch (std::invalid_argument& e)
742c04e2e70SJason M. Bills         {
743c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
744c04e2e70SJason M. Bills             return -1;
745c04e2e70SJason M. Bills         }
746c04e2e70SJason M. Bills         catch (std::out_of_range& e)
747c04e2e70SJason M. Bills         {
748c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
749c04e2e70SJason M. Bills             return -1;
750c04e2e70SJason M. Bills         }
751c04e2e70SJason M. Bills     }
752c04e2e70SJason M. Bills     return 0;
753c04e2e70SJason M. Bills }
754c04e2e70SJason M. Bills 
7551d4d54ddSJason M. Bills ipmi::RspType<uint8_t,  // SEL version
7561d4d54ddSJason M. Bills               uint16_t, // SEL entry count
7571d4d54ddSJason M. Bills               uint16_t, // free space
7581d4d54ddSJason M. Bills               uint32_t, // last add timestamp
7591d4d54ddSJason M. Bills               uint32_t, // last erase timestamp
7601d4d54ddSJason M. Bills               uint8_t>  // operation support
7611d4d54ddSJason M. Bills     ipmiStorageGetSELInfo()
762c04e2e70SJason M. Bills {
7631d4d54ddSJason M. Bills     constexpr uint8_t selVersion = ipmi::sel::selVersion;
7641d4d54ddSJason M. Bills     uint16_t entries = countSELEntries();
7651d4d54ddSJason M. Bills     uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp(
7661d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename);
7671d4d54ddSJason M. Bills     uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get();
7681d4d54ddSJason M. Bills     constexpr uint8_t operationSupport =
7691d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selOperationSupport;
7701d4d54ddSJason M. Bills     constexpr uint16_t freeSpace =
7711d4d54ddSJason M. Bills         0xffff; // Spec indicates that more than 64kB is free
772c04e2e70SJason M. Bills 
7731d4d54ddSJason M. Bills     return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
7741d4d54ddSJason M. Bills                                  eraseTimeStamp, operationSupport);
775c04e2e70SJason M. Bills }
776c04e2e70SJason M. Bills 
7771d4d54ddSJason M. Bills using systemEventType = std::tuple<
7781d4d54ddSJason M. Bills     uint32_t, // Timestamp
7791d4d54ddSJason M. Bills     uint16_t, // Generator ID
7801d4d54ddSJason M. Bills     uint8_t,  // EvM Rev
7811d4d54ddSJason M. Bills     uint8_t,  // Sensor Type
7821d4d54ddSJason M. Bills     uint8_t,  // Sensor Number
7831d4d54ddSJason M. Bills     uint7_t,  // Event Type
7841d4d54ddSJason M. Bills     bool,     // Event Direction
7851d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
7861d4d54ddSJason M. Bills using oemTsEventType = std::tuple<
7871d4d54ddSJason M. Bills     uint32_t,                                                   // Timestamp
7881d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data
7891d4d54ddSJason M. Bills using oemEventType =
7901d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data
7911d4d54ddSJason M. Bills 
7921d4d54ddSJason M. Bills ipmi::RspType<uint16_t, // Next Record ID
7931d4d54ddSJason M. Bills               uint16_t, // Record ID
7941d4d54ddSJason M. Bills               uint8_t,  // Record Type
7951d4d54ddSJason M. Bills               std::variant<systemEventType, oemTsEventType,
7961d4d54ddSJason M. Bills                            oemEventType>> // Record Content
7971d4d54ddSJason M. Bills     ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
7981d4d54ddSJason M. Bills                            uint8_t offset, uint8_t size)
799c04e2e70SJason M. Bills {
8001d4d54ddSJason M. Bills     // Only support getting the entire SEL record. If a partial size or non-zero
8011d4d54ddSJason M. Bills     // offset is requested, return an error
8021d4d54ddSJason M. Bills     if (offset != 0 || size != ipmi::sel::entireRecord)
803c04e2e70SJason M. Bills     {
8041d4d54ddSJason M. Bills         return ipmi::responseRetBytesUnavailable();
805c04e2e70SJason M. Bills     }
806c04e2e70SJason M. Bills 
8071d4d54ddSJason M. Bills     // Check the reservation ID if one is provided or required (only if the
8081d4d54ddSJason M. Bills     // offset is non-zero)
8091d4d54ddSJason M. Bills     if (reservationID != 0 || offset != 0)
810c04e2e70SJason M. Bills     {
8111d4d54ddSJason M. Bills         if (!checkSELReservation(reservationID))
812c04e2e70SJason M. Bills         {
8131d4d54ddSJason M. Bills             return ipmi::responseInvalidReservationId();
814c04e2e70SJason M. Bills         }
815c04e2e70SJason M. Bills     }
816c04e2e70SJason M. Bills 
8171d4d54ddSJason M. Bills     // Get the ipmi_sel log files
8181d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
8191d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
820c04e2e70SJason M. Bills     {
8211d4d54ddSJason M. Bills         return ipmi::responseSensorInvalid();
822c04e2e70SJason M. Bills     }
823c04e2e70SJason M. Bills 
8241d4d54ddSJason M. Bills     std::string targetEntry;
825c04e2e70SJason M. Bills 
826c04e2e70SJason M. Bills     if (targetID == ipmi::sel::firstEntry)
827c04e2e70SJason M. Bills     {
8281d4d54ddSJason M. Bills         // The first entry will be at the top of the oldest log file
8291d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.back());
8301d4d54ddSJason M. Bills         if (!logStream.is_open())
831c04e2e70SJason M. Bills         {
8321d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
833c04e2e70SJason M. Bills         }
8341d4d54ddSJason M. Bills 
8351d4d54ddSJason M. Bills         if (!std::getline(logStream, targetEntry))
8361d4d54ddSJason M. Bills         {
8371d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
838c04e2e70SJason M. Bills         }
839c04e2e70SJason M. Bills     }
840c04e2e70SJason M. Bills     else if (targetID == ipmi::sel::lastEntry)
841c04e2e70SJason M. Bills     {
8421d4d54ddSJason M. Bills         // The last entry will be at the bottom of the newest log file
8431d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.front());
8441d4d54ddSJason M. Bills         if (!logStream.is_open())
845c04e2e70SJason M. Bills         {
8461d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
847c04e2e70SJason M. Bills         }
848c04e2e70SJason M. Bills 
8491d4d54ddSJason M. Bills         std::string line;
8501d4d54ddSJason M. Bills         while (std::getline(logStream, line))
851c04e2e70SJason M. Bills         {
8521d4d54ddSJason M. Bills             targetEntry = line;
853c04e2e70SJason M. Bills         }
854c04e2e70SJason M. Bills     }
855c04e2e70SJason M. Bills     else
856c04e2e70SJason M. Bills     {
8571d4d54ddSJason M. Bills         if (!findSELEntry(targetID, selLogFiles, targetEntry))
858c04e2e70SJason M. Bills         {
8591d4d54ddSJason M. Bills             return ipmi::responseSensorInvalid();
8601d4d54ddSJason M. Bills         }
861c04e2e70SJason M. Bills     }
862c04e2e70SJason M. Bills 
86352aaa7d5SJason M. Bills     // The format of the ipmi_sel message is "<Timestamp>
86452aaa7d5SJason M. Bills     // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
86552aaa7d5SJason M. Bills     // First get the Timestamp
86652aaa7d5SJason M. Bills     size_t space = targetEntry.find_first_of(" ");
86752aaa7d5SJason M. Bills     if (space == std::string::npos)
86852aaa7d5SJason M. Bills     {
86952aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
87052aaa7d5SJason M. Bills     }
87152aaa7d5SJason M. Bills     std::string entryTimestamp = targetEntry.substr(0, space);
87252aaa7d5SJason M. Bills     // Then get the log contents
87352aaa7d5SJason M. Bills     size_t entryStart = targetEntry.find_first_not_of(" ", space);
87452aaa7d5SJason M. Bills     if (entryStart == std::string::npos)
87552aaa7d5SJason M. Bills     {
87652aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
87752aaa7d5SJason M. Bills     }
87852aaa7d5SJason M. Bills     std::string_view entry(targetEntry);
87952aaa7d5SJason M. Bills     entry.remove_prefix(entryStart);
88052aaa7d5SJason M. Bills     // Use split to separate the entry into its fields
8811d4d54ddSJason M. Bills     std::vector<std::string> targetEntryFields;
88252aaa7d5SJason M. Bills     boost::split(targetEntryFields, entry, boost::is_any_of(","),
8831d4d54ddSJason M. Bills                  boost::token_compress_on);
88452aaa7d5SJason M. Bills     if (targetEntryFields.size() < 3)
8851d4d54ddSJason M. Bills     {
8861d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
887c04e2e70SJason M. Bills     }
8881a2fbddfSJason M. Bills     std::string& recordIDStr = targetEntryFields[0];
8891a2fbddfSJason M. Bills     std::string& recordTypeStr = targetEntryFields[1];
8901a2fbddfSJason M. Bills     std::string& eventDataStr = targetEntryFields[2];
891c04e2e70SJason M. Bills 
8921a2fbddfSJason M. Bills     uint16_t recordID;
8931a2fbddfSJason M. Bills     uint8_t recordType;
8941a2fbddfSJason M. Bills     try
8951a2fbddfSJason M. Bills     {
8961a2fbddfSJason M. Bills         recordID = std::stoul(recordIDStr);
8971a2fbddfSJason M. Bills         recordType = std::stoul(recordTypeStr, nullptr, 16);
8981a2fbddfSJason M. Bills     }
8991a2fbddfSJason M. Bills     catch (const std::invalid_argument&)
9001a2fbddfSJason M. Bills     {
9011a2fbddfSJason M. Bills         return ipmi::responseUnspecifiedError();
9021a2fbddfSJason M. Bills     }
9031d4d54ddSJason M. Bills     uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
9041d4d54ddSJason M. Bills     std::vector<uint8_t> eventDataBytes;
9051a2fbddfSJason M. Bills     if (fromHexStr(eventDataStr, eventDataBytes) < 0)
9061d4d54ddSJason M. Bills     {
9071d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
9081d4d54ddSJason M. Bills     }
9091d4d54ddSJason M. Bills 
9101d4d54ddSJason M. Bills     if (recordType == intel_oem::ipmi::sel::systemEvent)
9111d4d54ddSJason M. Bills     {
9121d4d54ddSJason M. Bills         // Get the timestamp
9131d4d54ddSJason M. Bills         std::tm timeStruct = {};
91452aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
9151d4d54ddSJason M. Bills 
9161d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
9171d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
9181d4d54ddSJason M. Bills         {
9191d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
9201d4d54ddSJason M. Bills         }
9211d4d54ddSJason M. Bills 
9221d4d54ddSJason M. Bills         // Set the event message revision
9231d4d54ddSJason M. Bills         uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev;
9241d4d54ddSJason M. Bills 
9251a2fbddfSJason M. Bills         uint16_t generatorID = 0;
9261a2fbddfSJason M. Bills         uint8_t sensorType = 0;
9271a2fbddfSJason M. Bills         uint8_t sensorNum = 0xFF;
9281a2fbddfSJason M. Bills         uint7_t eventType = 0;
9291a2fbddfSJason M. Bills         bool eventDir = 0;
9301a2fbddfSJason M. Bills         // System type events should have six fields
9311a2fbddfSJason M. Bills         if (targetEntryFields.size() >= 6)
9321a2fbddfSJason M. Bills         {
9331a2fbddfSJason M. Bills             std::string& generatorIDStr = targetEntryFields[3];
9341a2fbddfSJason M. Bills             std::string& sensorPath = targetEntryFields[4];
9351a2fbddfSJason M. Bills             std::string& eventDirStr = targetEntryFields[5];
9361a2fbddfSJason M. Bills 
9371a2fbddfSJason M. Bills             // Get the generator ID
9381a2fbddfSJason M. Bills             try
9391a2fbddfSJason M. Bills             {
9401a2fbddfSJason M. Bills                 generatorID = std::stoul(generatorIDStr, nullptr, 16);
9411a2fbddfSJason M. Bills             }
9421a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
9431a2fbddfSJason M. Bills             {
9441a2fbddfSJason M. Bills                 std::cerr << "Invalid Generator ID\n";
9451a2fbddfSJason M. Bills             }
9461a2fbddfSJason M. Bills 
9471d4d54ddSJason M. Bills             // Get the sensor type, sensor number, and event type for the sensor
9481a2fbddfSJason M. Bills             sensorType = getSensorTypeFromPath(sensorPath);
9491a2fbddfSJason M. Bills             sensorNum = getSensorNumberFromPath(sensorPath);
9501a2fbddfSJason M. Bills             eventType = getSensorEventTypeFromPath(sensorPath);
9511d4d54ddSJason M. Bills 
9521d4d54ddSJason M. Bills             // Get the event direction
9531a2fbddfSJason M. Bills             try
9541a2fbddfSJason M. Bills             {
9551a2fbddfSJason M. Bills                 eventDir = std::stoul(eventDirStr) ? 0 : 1;
9561a2fbddfSJason M. Bills             }
9571a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
9581a2fbddfSJason M. Bills             {
9591a2fbddfSJason M. Bills                 std::cerr << "Invalid Event Direction\n";
9601a2fbddfSJason M. Bills             }
9611a2fbddfSJason M. Bills         }
9621d4d54ddSJason M. Bills 
9631d4d54ddSJason M. Bills         // Only keep the eventData bytes that fit in the record
9641d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
9651d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
9661d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
9671d4d54ddSJason M. Bills                     eventData.begin());
9681d4d54ddSJason M. Bills 
9691d4d54ddSJason M. Bills         return ipmi::responseSuccess(
9701d4d54ddSJason M. Bills             nextRecordID, recordID, recordType,
9711d4d54ddSJason M. Bills             systemEventType{timestamp, generatorID, evmRev, sensorType,
9721d4d54ddSJason M. Bills                             sensorNum, eventType, eventDir, eventData});
9731d4d54ddSJason M. Bills     }
9741d4d54ddSJason M. Bills     else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst &&
9751d4d54ddSJason M. Bills              recordType <= intel_oem::ipmi::sel::oemTsEventLast)
9761d4d54ddSJason M. Bills     {
9771d4d54ddSJason M. Bills         // Get the timestamp
9781d4d54ddSJason M. Bills         std::tm timeStruct = {};
97952aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
9801d4d54ddSJason M. Bills 
9811d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
9821d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
9831d4d54ddSJason M. Bills         {
9841d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
9851d4d54ddSJason M. Bills         }
9861d4d54ddSJason M. Bills 
9871d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
9881d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{};
9891d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
9901d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
9911d4d54ddSJason M. Bills                     eventData.begin());
9921d4d54ddSJason M. Bills 
9931d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
9941d4d54ddSJason M. Bills                                      oemTsEventType{timestamp, eventData});
9951d4d54ddSJason M. Bills     }
996c5136aaaSPatrick Venture     else if (recordType >= intel_oem::ipmi::sel::oemEventFirst)
9971d4d54ddSJason M. Bills     {
9981d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
9991d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{};
10001d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10011d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10021d4d54ddSJason M. Bills                     eventData.begin());
10031d4d54ddSJason M. Bills 
10041d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
10051d4d54ddSJason M. Bills                                      eventData);
10061d4d54ddSJason M. Bills     }
10071d4d54ddSJason M. Bills 
10081d4d54ddSJason M. Bills     return ipmi::responseUnspecifiedError();
1009c04e2e70SJason M. Bills }
1010c04e2e70SJason M. Bills 
10116dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(
10126dd8f047SJason M. Bills     uint16_t recordID, uint8_t recordType, uint32_t timestamp,
10136dd8f047SJason M. Bills     uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
10146dd8f047SJason M. Bills     uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
10156dd8f047SJason M. Bills     uint8_t eventData3)
1016c04e2e70SJason M. Bills {
1017c04e2e70SJason M. Bills     // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1018c04e2e70SJason M. Bills     // added
1019c04e2e70SJason M. Bills     cancelSELReservation();
1020c04e2e70SJason M. Bills 
10216dd8f047SJason M. Bills     // Send this request to the Redfish hooks to log it as a Redfish message
10226dd8f047SJason M. Bills     // instead.  There is no need to add it to the SEL, so just return success.
10236dd8f047SJason M. Bills     intel_oem::ipmi::sel::checkRedfishHooks(
10246dd8f047SJason M. Bills         recordID, recordType, timestamp, generatorID, evmRev, sensorType,
10256dd8f047SJason M. Bills         sensorNum, eventType, eventData1, eventData2, eventData3);
102699b78ec8SJason M. Bills 
10276dd8f047SJason M. Bills     uint16_t responseID = 0xFFFF;
10286dd8f047SJason M. Bills     return ipmi::responseSuccess(responseID);
1029c04e2e70SJason M. Bills }
1030c04e2e70SJason M. Bills 
10311d4d54ddSJason M. Bills ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx,
10321d4d54ddSJason M. Bills                                            uint16_t reservationID,
10331d4d54ddSJason M. Bills                                            const std::array<uint8_t, 3>& clr,
10341d4d54ddSJason M. Bills                                            uint8_t eraseOperation)
1035c04e2e70SJason M. Bills {
10361d4d54ddSJason M. Bills     if (!checkSELReservation(reservationID))
1037c04e2e70SJason M. Bills     {
10381d4d54ddSJason M. Bills         return ipmi::responseInvalidReservationId();
1039c04e2e70SJason M. Bills     }
1040c04e2e70SJason M. Bills 
10411d4d54ddSJason M. Bills     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
10421d4d54ddSJason M. Bills     if (clr != clrExpected)
1043c04e2e70SJason M. Bills     {
10441d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
1045c04e2e70SJason M. Bills     }
1046c04e2e70SJason M. Bills 
10471d4d54ddSJason M. Bills     // Erasure status cannot be fetched, so always return erasure status as
10481d4d54ddSJason M. Bills     // `erase completed`.
10491d4d54ddSJason M. Bills     if (eraseOperation == ipmi::sel::getEraseStatus)
1050c04e2e70SJason M. Bills     {
10511d4d54ddSJason M. Bills         return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1052c04e2e70SJason M. Bills     }
1053c04e2e70SJason M. Bills 
10541d4d54ddSJason M. Bills     // Check that initiate erase is correct
10551d4d54ddSJason M. Bills     if (eraseOperation != ipmi::sel::initiateErase)
10561d4d54ddSJason M. Bills     {
10571d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
10581d4d54ddSJason M. Bills     }
10591d4d54ddSJason M. Bills 
10601d4d54ddSJason M. Bills     // Per the IPMI spec, need to cancel any reservation when the SEL is
10611d4d54ddSJason M. Bills     // cleared
1062c04e2e70SJason M. Bills     cancelSELReservation();
1063c04e2e70SJason M. Bills 
10647944c307SJason M. Bills     // Save the erase time
10657944c307SJason M. Bills     intel_oem::ipmi::sel::erase_time::save();
10667944c307SJason M. Bills 
10671d4d54ddSJason M. Bills     // Clear the SEL by deleting the log files
10681d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
10691d4d54ddSJason M. Bills     if (getSELLogFiles(selLogFiles))
1070c04e2e70SJason M. Bills     {
10711d4d54ddSJason M. Bills         for (const std::filesystem::path& file : selLogFiles)
10721d4d54ddSJason M. Bills         {
10731d4d54ddSJason M. Bills             std::error_code ec;
10741d4d54ddSJason M. Bills             std::filesystem::remove(file, ec);
1075c04e2e70SJason M. Bills         }
1076c04e2e70SJason M. Bills     }
1077c04e2e70SJason M. Bills 
10781d4d54ddSJason M. Bills     // Reload rsyslog so it knows to start new log files
107915419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
108015419dd5SVernon Mauery     sdbusplus::message::message rsyslogReload = dbus->new_method_call(
10811d4d54ddSJason M. Bills         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
10821d4d54ddSJason M. Bills         "org.freedesktop.systemd1.Manager", "ReloadUnit");
10831d4d54ddSJason M. Bills     rsyslogReload.append("rsyslog.service", "replace");
10841d4d54ddSJason M. Bills     try
10851d4d54ddSJason M. Bills     {
108615419dd5SVernon Mauery         sdbusplus::message::message reloadResponse = dbus->call(rsyslogReload);
10871d4d54ddSJason M. Bills     }
10881d4d54ddSJason M. Bills     catch (sdbusplus::exception_t& e)
10891d4d54ddSJason M. Bills     {
10901d4d54ddSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1091c04e2e70SJason M. Bills     }
1092c04e2e70SJason M. Bills 
10931d4d54ddSJason M. Bills     return ipmi::responseSuccess(ipmi::sel::eraseComplete);
10941d4d54ddSJason M. Bills }
10951d4d54ddSJason M. Bills 
10961a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
10971a47462eSJason M. Bills {
10981a47462eSJason M. Bills     struct timespec selTime = {};
10991a47462eSJason M. Bills 
11001a47462eSJason M. Bills     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
11011a47462eSJason M. Bills     {
11021a47462eSJason M. Bills         return ipmi::responseUnspecifiedError();
11031a47462eSJason M. Bills     }
11041a47462eSJason M. Bills 
11051a47462eSJason M. Bills     return ipmi::responseSuccess(selTime.tv_sec);
11061a47462eSJason M. Bills }
11071a47462eSJason M. Bills 
11081d4d54ddSJason M. Bills ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime)
1109cac97a53SJason M. Bills {
1110cac97a53SJason M. Bills     // Set SEL Time is not supported
11111d4d54ddSJason M. Bills     return ipmi::responseInvalidCommand();
1112cac97a53SJason M. Bills }
1113cac97a53SJason M. Bills 
111474c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
111574c50c64SJames Feist {
111674c50c64SJames Feist     std::vector<uint8_t> resp;
111774c50c64SJames Feist     if (index == 0)
111874c50c64SJames Feist     {
111974c50c64SJames Feist         Type12Record bmc = {};
112074c50c64SJames Feist         bmc.header.record_id_lsb = recordId;
112174c50c64SJames Feist         bmc.header.record_id_msb = recordId >> 8;
112274c50c64SJames Feist         bmc.header.sdr_version = ipmiSdrVersion;
112374c50c64SJames Feist         bmc.header.record_type = 0x12;
112474c50c64SJames Feist         bmc.header.record_length = 0x1b;
112574c50c64SJames Feist         bmc.slaveAddress = 0x20;
112674c50c64SJames Feist         bmc.channelNumber = 0;
112774c50c64SJames Feist         bmc.powerStateNotification = 0;
112874c50c64SJames Feist         bmc.deviceCapabilities = 0xBF;
112974c50c64SJames Feist         bmc.reserved = 0;
113074c50c64SJames Feist         bmc.entityID = 0x2E;
113174c50c64SJames Feist         bmc.entityInstance = 1;
113274c50c64SJames Feist         bmc.oem = 0;
113374c50c64SJames Feist         bmc.typeLengthCode = 0xD0;
113474c50c64SJames Feist         std::string bmcName = "Basbrd Mgmt Ctlr";
113574c50c64SJames Feist         std::copy(bmcName.begin(), bmcName.end(), bmc.name);
113674c50c64SJames Feist         uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
113774c50c64SJames Feist         resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
113874c50c64SJames Feist     }
113974c50c64SJames Feist     else if (index == 1)
114074c50c64SJames Feist     {
114174c50c64SJames Feist         Type12Record me = {};
114274c50c64SJames Feist         me.header.record_id_lsb = recordId;
114374c50c64SJames Feist         me.header.record_id_msb = recordId >> 8;
114474c50c64SJames Feist         me.header.sdr_version = ipmiSdrVersion;
114574c50c64SJames Feist         me.header.record_type = 0x12;
114674c50c64SJames Feist         me.header.record_length = 0x16;
114774c50c64SJames Feist         me.slaveAddress = 0x2C;
114874c50c64SJames Feist         me.channelNumber = 6;
114974c50c64SJames Feist         me.powerStateNotification = 0x24;
115074c50c64SJames Feist         me.deviceCapabilities = 0x21;
115174c50c64SJames Feist         me.reserved = 0;
115274c50c64SJames Feist         me.entityID = 0x2E;
115374c50c64SJames Feist         me.entityInstance = 2;
115474c50c64SJames Feist         me.oem = 0;
115574c50c64SJames Feist         me.typeLengthCode = 0xCB;
115674c50c64SJames Feist         std::string meName = "Mgmt Engine";
115774c50c64SJames Feist         std::copy(meName.begin(), meName.end(), me.name);
115874c50c64SJames Feist         uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
115974c50c64SJames Feist         resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
116074c50c64SJames Feist     }
116174c50c64SJames Feist     else
116274c50c64SJames Feist     {
116374c50c64SJames Feist         throw std::runtime_error("getType12SDRs:: Illegal index " +
116474c50c64SJames Feist                                  std::to_string(index));
116574c50c64SJames Feist     }
116674c50c64SJames Feist 
116774c50c64SJames Feist     return resp;
116874c50c64SJames Feist }
116974c50c64SJames Feist 
1170e2d1aee3SJason M. Bills void registerStorageFunctions()
1171e2d1aee3SJason M. Bills {
1172e2d1aee3SJason M. Bills     // <Get FRU Inventory Area Info>
1173d33acd6bSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1174d33acd6bSjayaprakash Mutyala                           ipmi::storage::cmdGetFruInventoryAreaInfo,
1175d33acd6bSjayaprakash Mutyala                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1176c04e2e70SJason M. Bills     // <READ FRU Data>
11775f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
11785f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
11795f4194eeSjayaprakash Mutyala                           ipmiStorageReadFruData);
1180e2d1aee3SJason M. Bills 
1181c04e2e70SJason M. Bills     // <WRITE FRU Data>
11825f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
11835f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdWriteFruData,
11845f4194eeSjayaprakash Mutyala                           ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1185c04e2e70SJason M. Bills 
1186c04e2e70SJason M. Bills     // <Get SEL Info>
11871d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1188542498e9SJason M. Bills                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1189542498e9SJason M. Bills                           ipmiStorageGetSELInfo);
1190c04e2e70SJason M. Bills 
1191c04e2e70SJason M. Bills     // <Get SEL Entry>
11921d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1193542498e9SJason M. Bills                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1194542498e9SJason M. Bills                           ipmiStorageGetSELEntry);
1195c04e2e70SJason M. Bills 
1196c04e2e70SJason M. Bills     // <Add SEL Entry>
11976dd8f047SJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
119898bbf69aSVernon Mauery                           ipmi::storage::cmdAddSelEntry,
11996dd8f047SJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1200c04e2e70SJason M. Bills 
1201c04e2e70SJason M. Bills     // <Clear SEL>
12021d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12031d4d54ddSJason M. Bills                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
12041d4d54ddSJason M. Bills                           ipmiStorageClearSEL);
1205cac97a53SJason M. Bills 
12061a47462eSJason M. Bills     // <Get SEL Time>
12071a47462eSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1208542498e9SJason M. Bills                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1209542498e9SJason M. Bills                           ipmiStorageGetSELTime);
12101a47462eSJason M. Bills 
1211cac97a53SJason M. Bills     // <Set SEL Time>
12121d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12131d4d54ddSJason M. Bills                           ipmi::storage::cmdSetSelTime,
12141d4d54ddSJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1215e2d1aee3SJason M. Bills }
12163f7c5e40SJason M. Bills } // namespace storage
12173f7c5e40SJason M. Bills } // namespace ipmi
1218