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 
17ca99ef59SPatrick Venture #include "storagecommands.hpp"
18ca99ef59SPatrick Venture 
19ca99ef59SPatrick Venture #include "commandutils.hpp"
2021a1b5f8SPeter Foley #include "fruutils.hpp"
21ca99ef59SPatrick Venture #include "ipmi_to_redfish_hooks.hpp"
22ca99ef59SPatrick Venture #include "sdrutils.hpp"
23c2a07d4bSPatrick Venture #include "types.hpp"
24ca99ef59SPatrick Venture 
251d4d54ddSJason M. Bills #include <boost/algorithm/string.hpp>
263f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp>
27*ee27fe09SJohnathan Mantey #include <boost/process.hpp>
282a265d54SJames Feist #include <ipmid/api.hpp>
292569025eSJames Feist #include <ipmid/message.hpp>
303f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp>
313f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp>
323f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp>
33fcd2d3a9SJames Feist 
34fcd2d3a9SJames Feist #include <filesystem>
35f23fd543SArchana Kakani #include <fstream>
36*ee27fe09SJohnathan Mantey #include <functional>
37fcd2d3a9SJames Feist #include <iostream>
38c04e2e70SJason M. Bills #include <stdexcept>
39*ee27fe09SJohnathan Mantey #include <string_view>
403f7c5e40SJason M. Bills 
419ce789fcSPatrick Venture static constexpr bool DEBUG = false;
429ce789fcSPatrick Venture 
431d4d54ddSJason M. Bills namespace intel_oem::ipmi::sel
441d4d54ddSJason M. Bills {
451d4d54ddSJason M. Bills static const std::filesystem::path selLogDir = "/var/log";
461d4d54ddSJason M. Bills static const std::string selLogFilename = "ipmi_sel";
471d4d54ddSJason M. Bills 
481d4d54ddSJason M. Bills static int getFileTimestamp(const std::filesystem::path& file)
491d4d54ddSJason M. Bills {
501d4d54ddSJason M. Bills     struct stat st;
511d4d54ddSJason M. Bills 
521d4d54ddSJason M. Bills     if (stat(file.c_str(), &st) >= 0)
531d4d54ddSJason M. Bills     {
541d4d54ddSJason M. Bills         return st.st_mtime;
551d4d54ddSJason M. Bills     }
561d4d54ddSJason M. Bills     return ::ipmi::sel::invalidTimeStamp;
571d4d54ddSJason M. Bills }
581d4d54ddSJason M. Bills 
591d4d54ddSJason M. Bills namespace erase_time
607944c307SJason M. Bills {
617944c307SJason M. Bills static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time";
627944c307SJason M. Bills 
637944c307SJason M. Bills void save()
647944c307SJason M. Bills {
657944c307SJason M. Bills     // open the file, creating it if necessary
667944c307SJason M. Bills     int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
677944c307SJason M. Bills     if (fd < 0)
687944c307SJason M. Bills     {
697944c307SJason M. Bills         std::cerr << "Failed to open file\n";
707944c307SJason M. Bills         return;
717944c307SJason M. Bills     }
727944c307SJason M. Bills 
737944c307SJason M. Bills     // update the file timestamp to the current time
747944c307SJason M. Bills     if (futimens(fd, NULL) < 0)
757944c307SJason M. Bills     {
767944c307SJason M. Bills         std::cerr << "Failed to update timestamp: "
777944c307SJason M. Bills                   << std::string(strerror(errno));
787944c307SJason M. Bills     }
797944c307SJason M. Bills     close(fd);
807944c307SJason M. Bills }
817944c307SJason M. Bills 
827944c307SJason M. Bills int get()
837944c307SJason M. Bills {
841d4d54ddSJason M. Bills     return getFileTimestamp(selEraseTimestamp);
857944c307SJason M. Bills }
861d4d54ddSJason M. Bills } // namespace erase_time
871d4d54ddSJason M. Bills } // namespace intel_oem::ipmi::sel
887944c307SJason M. Bills 
893f7c5e40SJason M. Bills namespace ipmi
903f7c5e40SJason M. Bills {
913f7c5e40SJason M. Bills 
923f7c5e40SJason M. Bills namespace storage
933f7c5e40SJason M. Bills {
943f7c5e40SJason M. Bills 
95e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64;
963f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16;
97e4f710d7SJames Feist using ObjectType = boost::container::flat_map<
98e4f710d7SJames Feist     std::string, boost::container::flat_map<std::string, DbusVariant>>;
99e4f710d7SJames Feist using ManagedObjectType =
100e4f710d7SJames Feist     boost::container::flat_map<sdbusplus::message::object_path, ObjectType>;
101e4f710d7SJames Feist using ManagedEntry = std::pair<sdbusplus::message::object_path, ObjectType>;
1023f7c5e40SJason M. Bills 
1033bcba457SJames Feist constexpr static const char* fruDeviceServiceName =
1043bcba457SJames Feist     "xyz.openbmc_project.FruDevice";
1052569025eSJames Feist constexpr static const size_t writeTimeoutSeconds = 10;
106358e7dfaSAnoop S constexpr static const char* chassisTypeRackMount = "23";
1073f7c5e40SJason M. Bills 
1084ed6f2c1SJason M. Bills // event direction is bit[7] of eventType where 1b = Deassertion event
1094ed6f2c1SJason M. Bills constexpr static const uint8_t deassertionEvent = 0x80;
110c04e2e70SJason M. Bills 
1113f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache;
11207574006SJohnathan Mantey static uint16_t cacheBus = 0xFFFF;
1133f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF;
114e4f710d7SJames Feist static uint8_t lastDevId = 0xFF;
1153f7c5e40SJason M. Bills 
11607574006SJohnathan Mantey static uint16_t writeBus = 0xFFFF;
1172569025eSJames Feist static uint8_t writeAddr = 0XFF;
1182569025eSJames Feist 
119f0feb49cSPatrick Williams std::unique_ptr<sdbusplus::Timer> writeTimer = nullptr;
120f944d2e5SPatrick Williams static std::vector<sdbusplus::bus::match_t> fruMatches;
1213f7c5e40SJason M. Bills 
1222569025eSJames Feist ManagedObjectType frus;
1232569025eSJames Feist 
1243f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a
1253f7c5e40SJason M. Bills // collision to verify our dev-id
12607574006SJohnathan Mantey boost::container::flat_map<uint8_t, std::pair<uint16_t, uint8_t>> deviceHashes;
1273f7c5e40SJason M. Bills 
128e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor));
1293f7c5e40SJason M. Bills 
1303f7c5e40SJason M. Bills bool writeFru()
1313f7c5e40SJason M. Bills {
13207574006SJohnathan Mantey     if (writeBus == 0xFFFF && writeAddr == 0xFF)
1332569025eSJames Feist     {
1342569025eSJames Feist         return true;
1352569025eSJames Feist     }
13615419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
137f944d2e5SPatrick Williams     sdbusplus::message_t writeFru = dbus->new_method_call(
1383f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
1393f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "WriteFru");
1402569025eSJames Feist     writeFru.append(writeBus, writeAddr, fruCache);
1413f7c5e40SJason M. Bills     try
1423f7c5e40SJason M. Bills     {
143f944d2e5SPatrick Williams         sdbusplus::message_t writeFruResp = dbus->call(writeFru);
1443f7c5e40SJason M. Bills     }
145bd51e6a9SPatrick Williams     catch (const sdbusplus::exception_t&)
1463f7c5e40SJason M. Bills     {
1473f7c5e40SJason M. Bills         // todo: log sel?
1483f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
1493f7c5e40SJason M. Bills             "error writing fru");
1503f7c5e40SJason M. Bills         return false;
1513f7c5e40SJason M. Bills     }
15207574006SJohnathan Mantey     writeBus = 0xFFFF;
1532569025eSJames Feist     writeAddr = 0xFF;
1543f7c5e40SJason M. Bills     return true;
1553f7c5e40SJason M. Bills }
1563f7c5e40SJason M. Bills 
1572569025eSJames Feist void createTimers()
158e2d1aee3SJason M. Bills {
159f0feb49cSPatrick Williams     writeTimer = std::make_unique<sdbusplus::Timer>(writeFru);
160e2d1aee3SJason M. Bills }
161e2d1aee3SJason M. Bills 
162e4f710d7SJames Feist void recalculateHashes()
1633f7c5e40SJason M. Bills {
1643f7c5e40SJason M. Bills     deviceHashes.clear();
1653f7c5e40SJason M. Bills     // hash the object paths to create unique device id's. increment on
1663f7c5e40SJason M. Bills     // collision
1673f7c5e40SJason M. Bills     std::hash<std::string> hasher;
1683f7c5e40SJason M. Bills     for (const auto& fru : frus)
1693f7c5e40SJason M. Bills     {
1703f7c5e40SJason M. Bills         auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
1713f7c5e40SJason M. Bills         if (fruIface == fru.second.end())
1723f7c5e40SJason M. Bills         {
1733f7c5e40SJason M. Bills             continue;
1743f7c5e40SJason M. Bills         }
1753f7c5e40SJason M. Bills 
1763f7c5e40SJason M. Bills         auto busFind = fruIface->second.find("BUS");
1773f7c5e40SJason M. Bills         auto addrFind = fruIface->second.find("ADDRESS");
1783f7c5e40SJason M. Bills         if (busFind == fruIface->second.end() ||
1793f7c5e40SJason M. Bills             addrFind == fruIface->second.end())
1803f7c5e40SJason M. Bills         {
1813f7c5e40SJason M. Bills             phosphor::logging::log<phosphor::logging::level::INFO>(
1823f7c5e40SJason M. Bills                 "fru device missing Bus or Address",
1833f7c5e40SJason M. Bills                 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
1843f7c5e40SJason M. Bills             continue;
1853f7c5e40SJason M. Bills         }
1863f7c5e40SJason M. Bills 
18707574006SJohnathan Mantey         uint16_t fruBus = std::get<uint32_t>(busFind->second);
1888166c8d7SVernon Mauery         uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
189358e7dfaSAnoop S         auto chassisFind = fruIface->second.find("CHASSIS_TYPE");
190358e7dfaSAnoop S         std::string chassisType;
191358e7dfaSAnoop S         if (chassisFind != fruIface->second.end())
192358e7dfaSAnoop S         {
193358e7dfaSAnoop S             chassisType = std::get<std::string>(chassisFind->second);
194358e7dfaSAnoop S         }
1953f7c5e40SJason M. Bills 
1963f7c5e40SJason M. Bills         uint8_t fruHash = 0;
197358e7dfaSAnoop S         if (chassisType.compare(chassisTypeRackMount) != 0)
1983f7c5e40SJason M. Bills         {
1993f7c5e40SJason M. Bills             fruHash = hasher(fru.first.str);
2003f7c5e40SJason M. Bills             // can't be 0xFF based on spec, and 0 is reserved for baseboard
2013f7c5e40SJason M. Bills             if (fruHash == 0 || fruHash == 0xFF)
2023f7c5e40SJason M. Bills             {
2033f7c5e40SJason M. Bills                 fruHash = 1;
2043f7c5e40SJason M. Bills             }
2053f7c5e40SJason M. Bills         }
20607574006SJohnathan Mantey         std::pair<uint16_t, uint8_t> newDev(fruBus, fruAddr);
2073f7c5e40SJason M. Bills 
2083f7c5e40SJason M. Bills         bool emplacePassed = false;
2093f7c5e40SJason M. Bills         while (!emplacePassed)
2103f7c5e40SJason M. Bills         {
2113f7c5e40SJason M. Bills             auto resp = deviceHashes.emplace(fruHash, newDev);
2123f7c5e40SJason M. Bills             emplacePassed = resp.second;
2133f7c5e40SJason M. Bills             if (!emplacePassed)
2143f7c5e40SJason M. Bills             {
2153f7c5e40SJason M. Bills                 fruHash++;
2163f7c5e40SJason M. Bills                 // can't be 0xFF based on spec, and 0 is reserved for
2173f7c5e40SJason M. Bills                 // baseboard
2183f7c5e40SJason M. Bills                 if (fruHash == 0XFF)
2193f7c5e40SJason M. Bills                 {
2203f7c5e40SJason M. Bills                     fruHash = 0x1;
2213f7c5e40SJason M. Bills                 }
2223f7c5e40SJason M. Bills             }
2233f7c5e40SJason M. Bills         }
2243f7c5e40SJason M. Bills     }
225e4f710d7SJames Feist }
226e4f710d7SJames Feist 
227e4f710d7SJames Feist void replaceCacheFru(const std::shared_ptr<sdbusplus::asio::connection>& bus,
228f23fd543SArchana Kakani                      boost::asio::yield_context& yield)
229e4f710d7SJames Feist {
230e4f710d7SJames Feist     boost::system::error_code ec;
231e4f710d7SJames Feist 
232*ee27fe09SJohnathan Mantey     frus = bus->yield_method_call<ManagedObjectType>(
233*ee27fe09SJohnathan Mantey         yield, ec, fruDeviceServiceName, "/",
234*ee27fe09SJohnathan Mantey         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
235e4f710d7SJames Feist     if (ec)
236e4f710d7SJames Feist     {
237e4f710d7SJames Feist         phosphor::logging::log<phosphor::logging::level::ERR>(
238*ee27fe09SJohnathan Mantey             "GetMangagedObjects for getSensorMap failed",
239e4f710d7SJames Feist             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
240e4f710d7SJames Feist 
241e4f710d7SJames Feist         return;
242e4f710d7SJames Feist     }
243e4f710d7SJames Feist     recalculateHashes();
244e4f710d7SJames Feist }
245e4f710d7SJames Feist 
246dcff1506SVernon Mauery ipmi::Cc getFru(ipmi::Context::ptr& ctx, uint8_t devId)
247e4f710d7SJames Feist {
248e4f710d7SJames Feist     if (lastDevId == devId && devId != 0xFF)
249e4f710d7SJames Feist     {
250e4f710d7SJames Feist         return ipmi::ccSuccess;
251e4f710d7SJames Feist     }
252e4f710d7SJames Feist 
2533f7c5e40SJason M. Bills     auto deviceFind = deviceHashes.find(devId);
254*ee27fe09SJohnathan Mantey     if (deviceFind == deviceHashes.end())
2553f7c5e40SJason M. Bills     {
2563f7c5e40SJason M. Bills         return IPMI_CC_SENSOR_INVALID;
2573f7c5e40SJason M. Bills     }
2583f7c5e40SJason M. Bills 
2593432a0acSPavanKumarIntel     if (writeTimer->isRunning())
2603432a0acSPavanKumarIntel     {
2613432a0acSPavanKumarIntel         phosphor::logging::log<phosphor::logging::level::ERR>(
2623432a0acSPavanKumarIntel             "Couldn't get raw fru as fru is updating");
2633432a0acSPavanKumarIntel         return ipmi::ccBusy;
2643432a0acSPavanKumarIntel     }
2653f7c5e40SJason M. Bills     fruCache.clear();
2662569025eSJames Feist 
2673f7c5e40SJason M. Bills     cacheBus = deviceFind->second.first;
2683f7c5e40SJason M. Bills     cacheAddr = deviceFind->second.second;
2692569025eSJames Feist 
270e4f710d7SJames Feist     boost::system::error_code ec;
271e4f710d7SJames Feist 
2722569025eSJames Feist     fruCache = ctx->bus->yield_method_call<std::vector<uint8_t>>(
273*ee27fe09SJohnathan Mantey         ctx->yield, ec, fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
2742569025eSJames Feist         "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
2752569025eSJames Feist         cacheAddr);
276*ee27fe09SJohnathan Mantey     if (ec)
2773f7c5e40SJason M. Bills     {
2782569025eSJames Feist         phosphor::logging::log<phosphor::logging::level::ERR>(
2792569025eSJames Feist             "Couldn't get raw fru",
2802569025eSJames Feist             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
28107574006SJohnathan Mantey         cacheBus = 0xFFFF;
2823f7c5e40SJason M. Bills         cacheAddr = 0xFF;
2832569025eSJames Feist         return ipmi::ccResponseError;
2843f7c5e40SJason M. Bills     }
2853f7c5e40SJason M. Bills 
2863f7c5e40SJason M. Bills     lastDevId = devId;
2872569025eSJames Feist     return ipmi::ccSuccess;
2883f7c5e40SJason M. Bills }
2893f7c5e40SJason M. Bills 
290e4f710d7SJames Feist void writeFruIfRunning()
291e4f710d7SJames Feist {
292e4f710d7SJames Feist     if (!writeTimer->isRunning())
293e4f710d7SJames Feist     {
294e4f710d7SJames Feist         return;
295e4f710d7SJames Feist     }
296e4f710d7SJames Feist     writeTimer->stop();
297e4f710d7SJames Feist     writeFru();
298e4f710d7SJames Feist }
299e4f710d7SJames Feist 
300e4f710d7SJames Feist void startMatch(void)
301e4f710d7SJames Feist {
302e4f710d7SJames Feist     if (fruMatches.size())
303e4f710d7SJames Feist     {
304e4f710d7SJames Feist         return;
305e4f710d7SJames Feist     }
306e4f710d7SJames Feist 
307e4f710d7SJames Feist     fruMatches.reserve(2);
308e4f710d7SJames Feist 
309e4f710d7SJames Feist     auto bus = getSdBus();
310e4f710d7SJames Feist     fruMatches.emplace_back(*bus,
311e4f710d7SJames Feist                             "type='signal',arg0path='/xyz/openbmc_project/"
312e4f710d7SJames Feist                             "FruDevice/',member='InterfacesAdded'",
313f944d2e5SPatrick Williams                             [](sdbusplus::message_t& message) {
314e4f710d7SJames Feist         sdbusplus::message::object_path path;
315e4f710d7SJames Feist         ObjectType object;
316e4f710d7SJames Feist         try
317e4f710d7SJames Feist         {
318e4f710d7SJames Feist             message.read(path, object);
319e4f710d7SJames Feist         }
320bd51e6a9SPatrick Williams         catch (const sdbusplus::exception_t&)
321e4f710d7SJames Feist         {
322e4f710d7SJames Feist             return;
323e4f710d7SJames Feist         }
324b37abfb2SPatrick Williams         auto findType = object.find("xyz.openbmc_project.FruDevice");
325e4f710d7SJames Feist         if (findType == object.end())
326e4f710d7SJames Feist         {
327e4f710d7SJames Feist             return;
328e4f710d7SJames Feist         }
329e4f710d7SJames Feist         writeFruIfRunning();
330e4f710d7SJames Feist         frus[path] = object;
331e4f710d7SJames Feist         recalculateHashes();
332e4f710d7SJames Feist         lastDevId = 0xFF;
333e4f710d7SJames Feist     });
334e4f710d7SJames Feist 
335e4f710d7SJames Feist     fruMatches.emplace_back(*bus,
336e4f710d7SJames Feist                             "type='signal',arg0path='/xyz/openbmc_project/"
337e4f710d7SJames Feist                             "FruDevice/',member='InterfacesRemoved'",
338f944d2e5SPatrick Williams                             [](sdbusplus::message_t& message) {
339e4f710d7SJames Feist         sdbusplus::message::object_path path;
340e4f710d7SJames Feist         std::set<std::string> interfaces;
341e4f710d7SJames Feist         try
342e4f710d7SJames Feist         {
343e4f710d7SJames Feist             message.read(path, interfaces);
344e4f710d7SJames Feist         }
345bd51e6a9SPatrick Williams         catch (const sdbusplus::exception_t&)
346e4f710d7SJames Feist         {
347e4f710d7SJames Feist             return;
348e4f710d7SJames Feist         }
349b37abfb2SPatrick Williams         auto findType = interfaces.find("xyz.openbmc_project.FruDevice");
350e4f710d7SJames Feist         if (findType == interfaces.end())
351e4f710d7SJames Feist         {
352e4f710d7SJames Feist             return;
353e4f710d7SJames Feist         }
354e4f710d7SJames Feist         writeFruIfRunning();
355e4f710d7SJames Feist         frus.erase(path);
356e4f710d7SJames Feist         recalculateHashes();
357e4f710d7SJames Feist         lastDevId = 0xFF;
358e4f710d7SJames Feist     });
359e4f710d7SJames Feist 
360e4f710d7SJames Feist     // call once to populate
361e4f710d7SJames Feist     boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
362e4f710d7SJames Feist         replaceCacheFru(getSdBus(), yield);
363e4f710d7SJames Feist     });
364e4f710d7SJames Feist }
365e4f710d7SJames Feist 
3665f4194eeSjayaprakash Mutyala /** @brief implements the read FRU data command
3675f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
3685f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
3695f4194eeSjayaprakash Mutyala  *  @param countToRead        - Count to read
3705f4194eeSjayaprakash Mutyala  *
3715f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
3725f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
3735f4194eeSjayaprakash Mutyala  */
3745f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t,             // Count
3755f4194eeSjayaprakash Mutyala               std::vector<uint8_t> // Requested data
3765f4194eeSjayaprakash Mutyala               >
377dcff1506SVernon Mauery     ipmiStorageReadFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId,
3782569025eSJames Feist                            uint16_t fruInventoryOffset, uint8_t countToRead)
379e2d1aee3SJason M. Bills {
3805f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
381e2d1aee3SJason M. Bills     {
3825f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
383e2d1aee3SJason M. Bills     }
384e2d1aee3SJason M. Bills 
385e4f710d7SJames Feist     ipmi::Cc status = getFru(ctx, fruDeviceId);
386e2d1aee3SJason M. Bills 
3875f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
3885f4194eeSjayaprakash Mutyala     {
3895f4194eeSjayaprakash Mutyala         return ipmi::response(status);
390e2d1aee3SJason M. Bills     }
391e2d1aee3SJason M. Bills 
3925f4194eeSjayaprakash Mutyala     size_t fromFruByteLen = 0;
3935f4194eeSjayaprakash Mutyala     if (countToRead + fruInventoryOffset < fruCache.size())
394e2d1aee3SJason M. Bills     {
3955f4194eeSjayaprakash Mutyala         fromFruByteLen = countToRead;
3965f4194eeSjayaprakash Mutyala     }
3975f4194eeSjayaprakash Mutyala     else if (fruCache.size() > fruInventoryOffset)
398e2d1aee3SJason M. Bills     {
3995f4194eeSjayaprakash Mutyala         fromFruByteLen = fruCache.size() - fruInventoryOffset;
4005f4194eeSjayaprakash Mutyala     }
4015f4194eeSjayaprakash Mutyala     else
4025f4194eeSjayaprakash Mutyala     {
4039210838eSsrikanta mondal         return ipmi::responseReqDataLenExceeded();
404e2d1aee3SJason M. Bills     }
405e2d1aee3SJason M. Bills 
4065f4194eeSjayaprakash Mutyala     std::vector<uint8_t> requestedData;
407e2d1aee3SJason M. Bills 
4085f4194eeSjayaprakash Mutyala     requestedData.insert(
4095f4194eeSjayaprakash Mutyala         requestedData.begin(), fruCache.begin() + fruInventoryOffset,
4105f4194eeSjayaprakash Mutyala         fruCache.begin() + fruInventoryOffset + fromFruByteLen);
4115f4194eeSjayaprakash Mutyala 
41270b17f93SPatrick Venture     return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()),
41370b17f93SPatrick Venture                                  requestedData);
414e2d1aee3SJason M. Bills }
4155f4194eeSjayaprakash Mutyala 
4165f4194eeSjayaprakash Mutyala /** @brief implements the write FRU data command
4175f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
4185f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
4195f4194eeSjayaprakash Mutyala  *  @param dataToWrite        - Data to write
4205f4194eeSjayaprakash Mutyala  *
4215f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
4225f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
4235f4194eeSjayaprakash Mutyala  */
4245f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t>
425dcff1506SVernon Mauery     ipmiStorageWriteFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId,
4262569025eSJames Feist                             uint16_t fruInventoryOffset,
4275f4194eeSjayaprakash Mutyala                             std::vector<uint8_t>& dataToWrite)
4285f4194eeSjayaprakash Mutyala {
4295f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
4305f4194eeSjayaprakash Mutyala     {
4315f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
4325f4194eeSjayaprakash Mutyala     }
4335f4194eeSjayaprakash Mutyala 
4345f4194eeSjayaprakash Mutyala     size_t writeLen = dataToWrite.size();
4355f4194eeSjayaprakash Mutyala 
436e4f710d7SJames Feist     ipmi::Cc status = getFru(ctx, fruDeviceId);
4375f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
4385f4194eeSjayaprakash Mutyala     {
4395f4194eeSjayaprakash Mutyala         return ipmi::response(status);
4405f4194eeSjayaprakash Mutyala     }
441dcff1506SVernon Mauery     size_t lastWriteAddr = fruInventoryOffset + writeLen;
442e2d1aee3SJason M. Bills     if (fruCache.size() < lastWriteAddr)
443e2d1aee3SJason M. Bills     {
4445f4194eeSjayaprakash Mutyala         fruCache.resize(fruInventoryOffset + writeLen);
445e2d1aee3SJason M. Bills     }
446e2d1aee3SJason M. Bills 
4475f4194eeSjayaprakash Mutyala     std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
4485f4194eeSjayaprakash Mutyala               fruCache.begin() + fruInventoryOffset);
449e2d1aee3SJason M. Bills 
45021a1b5f8SPeter Foley     bool atEnd = validateBasicFruContent(fruCache, lastWriteAddr);
4515f4194eeSjayaprakash Mutyala     uint8_t countWritten = 0;
4522569025eSJames Feist 
4532569025eSJames Feist     writeBus = cacheBus;
4542569025eSJames Feist     writeAddr = cacheAddr;
455e2d1aee3SJason M. Bills     if (atEnd)
456e2d1aee3SJason M. Bills     {
457e2d1aee3SJason M. Bills         // cancel timer, we're at the end so might as well send it
4582569025eSJames Feist         writeTimer->stop();
459e2d1aee3SJason M. Bills         if (!writeFru())
460e2d1aee3SJason M. Bills         {
4615f4194eeSjayaprakash Mutyala             return ipmi::responseInvalidFieldRequest();
462e2d1aee3SJason M. Bills         }
4635f4194eeSjayaprakash Mutyala         countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF));
464e2d1aee3SJason M. Bills     }
465e2d1aee3SJason M. Bills     else
466e2d1aee3SJason M. Bills     {
4672569025eSJames Feist         // start a timer, if no further data is sent  to check to see if it is
4682569025eSJames Feist         // valid
4692569025eSJames Feist         writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
4702569025eSJames Feist             std::chrono::seconds(writeTimeoutSeconds)));
4715f4194eeSjayaprakash Mutyala         countWritten = 0;
472e2d1aee3SJason M. Bills     }
473e2d1aee3SJason M. Bills 
4745f4194eeSjayaprakash Mutyala     return ipmi::responseSuccess(countWritten);
475e2d1aee3SJason M. Bills }
476e2d1aee3SJason M. Bills 
477d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command
478d33acd6bSjayaprakash Mutyala  *  @param fruDeviceId  - FRU Device ID
479d33acd6bSjayaprakash Mutyala  *
480d33acd6bSjayaprakash Mutyala  *  @returns IPMI completion code plus response data
481d33acd6bSjayaprakash Mutyala  *   - inventorySize - Number of possible allocation units
482d33acd6bSjayaprakash Mutyala  *   - accessType    - Allocation unit size in bytes.
483d33acd6bSjayaprakash Mutyala  */
484d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize
485d33acd6bSjayaprakash Mutyala               uint8_t>  // accessType
486dcff1506SVernon Mauery     ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr& ctx, uint8_t fruDeviceId)
487e2d1aee3SJason M. Bills {
488d33acd6bSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
489e2d1aee3SJason M. Bills     {
490d33acd6bSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
491e2d1aee3SJason M. Bills     }
492e2d1aee3SJason M. Bills 
4931e2ab061SJayaprakash Mutyala     ipmi::Cc ret = getFru(ctx, fruDeviceId);
4941e2ab061SJayaprakash Mutyala     if (ret != ipmi::ccSuccess)
4951e2ab061SJayaprakash Mutyala     {
4961e2ab061SJayaprakash Mutyala         return ipmi::response(ret);
4971e2ab061SJayaprakash Mutyala     }
498e2d1aee3SJason M. Bills 
499d33acd6bSjayaprakash Mutyala     constexpr uint8_t accessType =
500d33acd6bSjayaprakash Mutyala         static_cast<uint8_t>(GetFRUAreaAccessType::byte);
501e2d1aee3SJason M. Bills 
502d33acd6bSjayaprakash Mutyala     return ipmi::responseSuccess(fruCache.size(), accessType);
503e2d1aee3SJason M. Bills }
504e2d1aee3SJason M. Bills 
505dcff1506SVernon Mauery ipmi::Cc getFruSdrCount(ipmi::Context::ptr&, size_t& count)
5063f7c5e40SJason M. Bills {
5073f7c5e40SJason M. Bills     count = deviceHashes.size();
508dcff1506SVernon Mauery     return ipmi::ccSuccess;
5093f7c5e40SJason M. Bills }
5103f7c5e40SJason M. Bills 
511dcff1506SVernon Mauery ipmi::Cc getFruSdrs(ipmi::Context::ptr& ctx, size_t index,
5122569025eSJames Feist                     get_sdr::SensorDataFruRecord& resp)
5133f7c5e40SJason M. Bills {
5143f7c5e40SJason M. Bills     if (deviceHashes.size() < index)
5153f7c5e40SJason M. Bills     {
516dcff1506SVernon Mauery         return ipmi::ccInvalidFieldRequest;
5173f7c5e40SJason M. Bills     }
5183f7c5e40SJason M. Bills     auto device = deviceHashes.begin() + index;
51907574006SJohnathan Mantey     uint16_t& bus = device->second.first;
5203f7c5e40SJason M. Bills     uint8_t& address = device->second.second;
5213f7c5e40SJason M. Bills 
5223f7c5e40SJason M. Bills     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
523b37abfb2SPatrick Williams     auto fru = std::find_if(frus.begin(), frus.end(),
5243f7c5e40SJason M. Bills                             [bus, address, &fruData](ManagedEntry& entry) {
525b37abfb2SPatrick Williams         auto findFruDevice = entry.second.find("xyz.openbmc_project.FruDevice");
5263f7c5e40SJason M. Bills         if (findFruDevice == entry.second.end())
5273f7c5e40SJason M. Bills         {
5283f7c5e40SJason M. Bills             return false;
5293f7c5e40SJason M. Bills         }
5303f7c5e40SJason M. Bills         fruData = &(findFruDevice->second);
5313f7c5e40SJason M. Bills         auto findBus = findFruDevice->second.find("BUS");
532b37abfb2SPatrick Williams         auto findAddress = findFruDevice->second.find("ADDRESS");
5333f7c5e40SJason M. Bills         if (findBus == findFruDevice->second.end() ||
5343f7c5e40SJason M. Bills             findAddress == findFruDevice->second.end())
5353f7c5e40SJason M. Bills         {
5363f7c5e40SJason M. Bills             return false;
5373f7c5e40SJason M. Bills         }
5388166c8d7SVernon Mauery         if (std::get<uint32_t>(findBus->second) != bus)
5393f7c5e40SJason M. Bills         {
5403f7c5e40SJason M. Bills             return false;
5413f7c5e40SJason M. Bills         }
5428166c8d7SVernon Mauery         if (std::get<uint32_t>(findAddress->second) != address)
5433f7c5e40SJason M. Bills         {
5443f7c5e40SJason M. Bills             return false;
5453f7c5e40SJason M. Bills         }
5463f7c5e40SJason M. Bills         return true;
5473f7c5e40SJason M. Bills     });
5483f7c5e40SJason M. Bills     if (fru == frus.end())
5493f7c5e40SJason M. Bills     {
550dcff1506SVernon Mauery         return ipmi::ccResponseError;
5513f7c5e40SJason M. Bills     }
5529ce789fcSPatrick Venture 
5532569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS
5542569025eSJames Feist 
5559ce789fcSPatrick Venture     boost::container::flat_map<std::string, DbusVariant>* entityData = nullptr;
5569ce789fcSPatrick Venture 
5572569025eSJames Feist     // todo: this should really use caching, this is a very inefficient lookup
5582569025eSJames Feist     boost::system::error_code ec;
5592569025eSJames Feist     ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>(
5609d2894d9SNan Zhou         ctx->yield, ec, "xyz.openbmc_project.EntityManager",
5619d2894d9SNan Zhou         "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
5629d2894d9SNan Zhou         "GetManagedObjects");
5632569025eSJames Feist 
5642569025eSJames Feist     if (ec)
5659ce789fcSPatrick Venture     {
5662569025eSJames Feist         phosphor::logging::log<phosphor::logging::level::ERR>(
5672569025eSJames Feist             "GetMangagedObjects for getSensorMap failed",
5682569025eSJames Feist             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
5699ce789fcSPatrick Venture 
5702569025eSJames Feist         return ipmi::ccResponseError;
5712569025eSJames Feist     }
5729ce789fcSPatrick Venture 
573b37abfb2SPatrick Williams     auto entity =
574b37abfb2SPatrick Williams         std::find_if(entities.begin(), entities.end(),
5759ce789fcSPatrick Venture                      [bus, address, &entityData](ManagedEntry& entry) {
5769ce789fcSPatrick Venture         auto findFruDevice = entry.second.find(
5779ce789fcSPatrick Venture             "xyz.openbmc_project.Inventory.Decorator.FruDevice");
5789ce789fcSPatrick Venture         if (findFruDevice == entry.second.end())
5799ce789fcSPatrick Venture         {
5809ce789fcSPatrick Venture             return false;
5819ce789fcSPatrick Venture         }
5829ce789fcSPatrick Venture 
5839ce789fcSPatrick Venture         // Integer fields added via Entity-Manager json are uint64_ts by
5849ce789fcSPatrick Venture         // default.
5859ce789fcSPatrick Venture         auto findBus = findFruDevice->second.find("Bus");
5869ce789fcSPatrick Venture         auto findAddress = findFruDevice->second.find("Address");
5879ce789fcSPatrick Venture 
5889ce789fcSPatrick Venture         if (findBus == findFruDevice->second.end() ||
5899ce789fcSPatrick Venture             findAddress == findFruDevice->second.end())
5909ce789fcSPatrick Venture         {
5919ce789fcSPatrick Venture             return false;
5929ce789fcSPatrick Venture         }
5939ce789fcSPatrick Venture         if ((std::get<uint64_t>(findBus->second) != bus) ||
5949ce789fcSPatrick Venture             (std::get<uint64_t>(findAddress->second) != address))
5959ce789fcSPatrick Venture         {
5969ce789fcSPatrick Venture             return false;
5979ce789fcSPatrick Venture         }
5989ce789fcSPatrick Venture 
599b4b020c4SPatrick Venture         // At this point we found the device entry and should return
600b4b020c4SPatrick Venture         // true.
601b37abfb2SPatrick Williams         auto findIpmiDevice =
602b37abfb2SPatrick Williams             entry.second.find("xyz.openbmc_project.Inventory.Decorator.Ipmi");
603b4b020c4SPatrick Venture         if (findIpmiDevice != entry.second.end())
6049ce789fcSPatrick Venture         {
605b4b020c4SPatrick Venture             entityData = &(findIpmiDevice->second);
6069ce789fcSPatrick Venture         }
6079ce789fcSPatrick Venture 
6089ce789fcSPatrick Venture         return true;
6099ce789fcSPatrick Venture     });
6109ce789fcSPatrick Venture 
6119ce789fcSPatrick Venture     if (entity == entities.end())
6129ce789fcSPatrick Venture     {
6139ce789fcSPatrick Venture         if constexpr (DEBUG)
6149ce789fcSPatrick Venture         {
6159ce789fcSPatrick Venture             std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
6169ce789fcSPatrick Venture                                  "not found for Fru\n");
6179ce789fcSPatrick Venture         }
6189ce789fcSPatrick Venture     }
6192569025eSJames Feist 
6202569025eSJames Feist #endif
6219ce789fcSPatrick Venture 
6223f7c5e40SJason M. Bills     std::string name;
6233f7c5e40SJason M. Bills     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
6243f7c5e40SJason M. Bills     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
6253f7c5e40SJason M. Bills     if (findProductName != fruData->end())
6263f7c5e40SJason M. Bills     {
6278166c8d7SVernon Mauery         name = std::get<std::string>(findProductName->second);
6283f7c5e40SJason M. Bills     }
6293f7c5e40SJason M. Bills     else if (findBoardName != fruData->end())
6303f7c5e40SJason M. Bills     {
6318166c8d7SVernon Mauery         name = std::get<std::string>(findBoardName->second);
6323f7c5e40SJason M. Bills     }
6333f7c5e40SJason M. Bills     else
6343f7c5e40SJason M. Bills     {
6353f7c5e40SJason M. Bills         name = "UNKNOWN";
6363f7c5e40SJason M. Bills     }
6373f7c5e40SJason M. Bills     if (name.size() > maxFruSdrNameSize)
6383f7c5e40SJason M. Bills     {
6393f7c5e40SJason M. Bills         name = name.substr(0, maxFruSdrNameSize);
6403f7c5e40SJason M. Bills     }
6413f7c5e40SJason M. Bills     size_t sizeDiff = maxFruSdrNameSize - name.size();
6423f7c5e40SJason M. Bills 
6433f7c5e40SJason M. Bills     resp.header.record_id_lsb = 0x0; // calling code is to implement these
6443f7c5e40SJason M. Bills     resp.header.record_id_msb = 0x0;
6453f7c5e40SJason M. Bills     resp.header.sdr_version = ipmiSdrVersion;
64673d0135dSPatrick Venture     resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
6473f7c5e40SJason M. Bills     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
6483f7c5e40SJason M. Bills     resp.key.deviceAddress = 0x20;
6493f7c5e40SJason M. Bills     resp.key.fruID = device->first;
6503f7c5e40SJason M. Bills     resp.key.accessLun = 0x80; // logical / physical fru device
6513f7c5e40SJason M. Bills     resp.key.channelNumber = 0x0;
6523f7c5e40SJason M. Bills     resp.body.reserved = 0x0;
6533f7c5e40SJason M. Bills     resp.body.deviceType = 0x10;
6544f86d1f2SJames Feist     resp.body.deviceTypeModifier = 0x0;
6559ce789fcSPatrick Venture 
6569ce789fcSPatrick Venture     uint8_t entityID = 0;
6579ce789fcSPatrick Venture     uint8_t entityInstance = 0x1;
6589ce789fcSPatrick Venture 
6592569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS
6609ce789fcSPatrick Venture     if (entityData)
6619ce789fcSPatrick Venture     {
6629ce789fcSPatrick Venture         auto entityIdProperty = entityData->find("EntityId");
6639ce789fcSPatrick Venture         auto entityInstanceProperty = entityData->find("EntityInstance");
6649ce789fcSPatrick Venture 
6659ce789fcSPatrick Venture         if (entityIdProperty != entityData->end())
6669ce789fcSPatrick Venture         {
6679ce789fcSPatrick Venture             entityID = static_cast<uint8_t>(
6689ce789fcSPatrick Venture                 std::get<uint64_t>(entityIdProperty->second));
6699ce789fcSPatrick Venture         }
6709ce789fcSPatrick Venture         if (entityInstanceProperty != entityData->end())
6719ce789fcSPatrick Venture         {
6729ce789fcSPatrick Venture             entityInstance = static_cast<uint8_t>(
6739ce789fcSPatrick Venture                 std::get<uint64_t>(entityInstanceProperty->second));
6749ce789fcSPatrick Venture         }
6759ce789fcSPatrick Venture     }
6762569025eSJames Feist #endif
6779ce789fcSPatrick Venture 
6789ce789fcSPatrick Venture     resp.body.entityID = entityID;
6799ce789fcSPatrick Venture     resp.body.entityInstance = entityInstance;
6809ce789fcSPatrick Venture 
6813f7c5e40SJason M. Bills     resp.body.oem = 0x0;
6823f7c5e40SJason M. Bills     resp.body.deviceIDLen = name.size();
6833f7c5e40SJason M. Bills     name.copy(resp.body.deviceID, name.size());
6843f7c5e40SJason M. Bills 
685dcff1506SVernon Mauery     return ipmi::ccSuccess;
6863f7c5e40SJason M. Bills }
687e2d1aee3SJason M. Bills 
6881d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
689c04e2e70SJason M. Bills {
6901d4d54ddSJason M. Bills     // Loop through the directory looking for ipmi_sel log files
6911d4d54ddSJason M. Bills     for (const std::filesystem::directory_entry& dirEnt :
6921d4d54ddSJason M. Bills          std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir))
693c04e2e70SJason M. Bills     {
6941d4d54ddSJason M. Bills         std::string filename = dirEnt.path().filename();
6951d4d54ddSJason M. Bills         if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename))
6961d4d54ddSJason M. Bills         {
6971d4d54ddSJason M. Bills             // If we find an ipmi_sel log file, save the path
6981d4d54ddSJason M. Bills             selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir /
6991d4d54ddSJason M. Bills                                      filename);
700c04e2e70SJason M. Bills         }
701c04e2e70SJason M. Bills     }
7021d4d54ddSJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
7031d4d54ddSJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
7041d4d54ddSJason M. Bills     // can just sort the list to get them in order from newest to oldest
7051d4d54ddSJason M. Bills     std::sort(selLogFiles.begin(), selLogFiles.end());
706c04e2e70SJason M. Bills 
7071d4d54ddSJason M. Bills     return !selLogFiles.empty();
708c04e2e70SJason M. Bills }
709c04e2e70SJason M. Bills 
7101d4d54ddSJason M. Bills static int countSELEntries()
7111d4d54ddSJason M. Bills {
7121d4d54ddSJason M. Bills     // Get the list of ipmi_sel log files
7131d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
7141d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
7151d4d54ddSJason M. Bills     {
7161d4d54ddSJason M. Bills         return 0;
7171d4d54ddSJason M. Bills     }
7181d4d54ddSJason M. Bills     int numSELEntries = 0;
7191d4d54ddSJason M. Bills     // Loop through each log file and count the number of logs
7201d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
7211d4d54ddSJason M. Bills     {
7221d4d54ddSJason M. Bills         std::ifstream logStream(file);
7231d4d54ddSJason M. Bills         if (!logStream.is_open())
7241d4d54ddSJason M. Bills         {
7251d4d54ddSJason M. Bills             continue;
7261d4d54ddSJason M. Bills         }
7271d4d54ddSJason M. Bills 
7281d4d54ddSJason M. Bills         std::string line;
7291d4d54ddSJason M. Bills         while (std::getline(logStream, line))
7301d4d54ddSJason M. Bills         {
7311d4d54ddSJason M. Bills             numSELEntries++;
7321d4d54ddSJason M. Bills         }
7331d4d54ddSJason M. Bills     }
7341d4d54ddSJason M. Bills     return numSELEntries;
7351d4d54ddSJason M. Bills }
7361d4d54ddSJason M. Bills 
7371d4d54ddSJason M. Bills static bool findSELEntry(const int recordID,
738ff7e15b2SPatrick Venture                          const std::vector<std::filesystem::path>& selLogFiles,
7391d4d54ddSJason M. Bills                          std::string& entry)
7401d4d54ddSJason M. Bills {
7411d4d54ddSJason M. Bills     // Record ID is the first entry field following the timestamp. It is
7421d4d54ddSJason M. Bills     // preceded by a space and followed by a comma
7431d4d54ddSJason M. Bills     std::string search = " " + std::to_string(recordID) + ",";
7441d4d54ddSJason M. Bills 
7451d4d54ddSJason M. Bills     // Loop through the ipmi_sel log entries
7461d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
7471d4d54ddSJason M. Bills     {
7481d4d54ddSJason M. Bills         std::ifstream logStream(file);
7491d4d54ddSJason M. Bills         if (!logStream.is_open())
7501d4d54ddSJason M. Bills         {
7511d4d54ddSJason M. Bills             continue;
7521d4d54ddSJason M. Bills         }
7531d4d54ddSJason M. Bills 
7541d4d54ddSJason M. Bills         while (std::getline(logStream, entry))
7551d4d54ddSJason M. Bills         {
7561d4d54ddSJason M. Bills             // Check if the record ID matches
7571d4d54ddSJason M. Bills             if (entry.find(search) != std::string::npos)
7581d4d54ddSJason M. Bills             {
7591d4d54ddSJason M. Bills                 return true;
7601d4d54ddSJason M. Bills             }
7611d4d54ddSJason M. Bills         }
7621d4d54ddSJason M. Bills     }
7631d4d54ddSJason M. Bills     return false;
7641d4d54ddSJason M. Bills }
7651d4d54ddSJason M. Bills 
7661d4d54ddSJason M. Bills static uint16_t
7671d4d54ddSJason M. Bills     getNextRecordID(const uint16_t recordID,
768ff7e15b2SPatrick Venture                     const std::vector<std::filesystem::path>& selLogFiles)
7691d4d54ddSJason M. Bills {
7701d4d54ddSJason M. Bills     uint16_t nextRecordID = recordID + 1;
7711d4d54ddSJason M. Bills     std::string entry;
7721d4d54ddSJason M. Bills     if (findSELEntry(nextRecordID, selLogFiles, entry))
7731d4d54ddSJason M. Bills     {
7741d4d54ddSJason M. Bills         return nextRecordID;
7751d4d54ddSJason M. Bills     }
7761d4d54ddSJason M. Bills     else
7771d4d54ddSJason M. Bills     {
7781d4d54ddSJason M. Bills         return ipmi::sel::lastEntry;
7791d4d54ddSJason M. Bills     }
780c04e2e70SJason M. Bills }
781c04e2e70SJason M. Bills 
782ff7e15b2SPatrick Venture static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
783c04e2e70SJason M. Bills {
784c04e2e70SJason M. Bills     for (unsigned int i = 0; i < hexStr.size(); i += 2)
785c04e2e70SJason M. Bills     {
786c04e2e70SJason M. Bills         try
787c04e2e70SJason M. Bills         {
788c04e2e70SJason M. Bills             data.push_back(static_cast<uint8_t>(
789c04e2e70SJason M. Bills                 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
790c04e2e70SJason M. Bills         }
791bd51e6a9SPatrick Williams         catch (const std::invalid_argument& e)
792c04e2e70SJason M. Bills         {
793c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
794c04e2e70SJason M. Bills             return -1;
795c04e2e70SJason M. Bills         }
796bd51e6a9SPatrick Williams         catch (const std::out_of_range& e)
797c04e2e70SJason M. Bills         {
798c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
799c04e2e70SJason M. Bills             return -1;
800c04e2e70SJason M. Bills         }
801c04e2e70SJason M. Bills     }
802c04e2e70SJason M. Bills     return 0;
803c04e2e70SJason M. Bills }
804c04e2e70SJason M. Bills 
8051d4d54ddSJason M. Bills ipmi::RspType<uint8_t,  // SEL version
8061d4d54ddSJason M. Bills               uint16_t, // SEL entry count
8071d4d54ddSJason M. Bills               uint16_t, // free space
8081d4d54ddSJason M. Bills               uint32_t, // last add timestamp
8091d4d54ddSJason M. Bills               uint32_t, // last erase timestamp
8101d4d54ddSJason M. Bills               uint8_t>  // operation support
8111d4d54ddSJason M. Bills     ipmiStorageGetSELInfo()
812c04e2e70SJason M. Bills {
8131d4d54ddSJason M. Bills     constexpr uint8_t selVersion = ipmi::sel::selVersion;
8141d4d54ddSJason M. Bills     uint16_t entries = countSELEntries();
8151d4d54ddSJason M. Bills     uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp(
8161d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename);
8171d4d54ddSJason M. Bills     uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get();
8181d4d54ddSJason M. Bills     constexpr uint8_t operationSupport =
8191d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selOperationSupport;
8201d4d54ddSJason M. Bills     constexpr uint16_t freeSpace =
8211d4d54ddSJason M. Bills         0xffff; // Spec indicates that more than 64kB is free
822c04e2e70SJason M. Bills 
8231d4d54ddSJason M. Bills     return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
8241d4d54ddSJason M. Bills                                  eraseTimeStamp, operationSupport);
825c04e2e70SJason M. Bills }
826c04e2e70SJason M. Bills 
8271d4d54ddSJason M. Bills using systemEventType = std::tuple<
8281d4d54ddSJason M. Bills     uint32_t, // Timestamp
8291d4d54ddSJason M. Bills     uint16_t, // Generator ID
8301d4d54ddSJason M. Bills     uint8_t,  // EvM Rev
8311d4d54ddSJason M. Bills     uint8_t,  // Sensor Type
8321d4d54ddSJason M. Bills     uint8_t,  // Sensor Number
8331d4d54ddSJason M. Bills     uint7_t,  // Event Type
8341d4d54ddSJason M. Bills     bool,     // Event Direction
8351d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
8361d4d54ddSJason M. Bills using oemTsEventType = std::tuple<
8371d4d54ddSJason M. Bills     uint32_t,                                                    // Timestamp
8381d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>;  // Event Data
8391d4d54ddSJason M. Bills using oemEventType =
8401d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>;     // Event Data
8411d4d54ddSJason M. Bills 
8421d4d54ddSJason M. Bills ipmi::RspType<uint16_t,                   // Next Record ID
8431d4d54ddSJason M. Bills               uint16_t,                   // Record ID
8441d4d54ddSJason M. Bills               uint8_t,                    // Record Type
8451d4d54ddSJason M. Bills               std::variant<systemEventType, oemTsEventType,
8461d4d54ddSJason M. Bills                            oemEventType>> // Record Content
8471d4d54ddSJason M. Bills     ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
8481d4d54ddSJason M. Bills                            uint8_t offset, uint8_t size)
849c04e2e70SJason M. Bills {
8501d4d54ddSJason M. Bills     // Only support getting the entire SEL record. If a partial size or non-zero
8511d4d54ddSJason M. Bills     // offset is requested, return an error
8521d4d54ddSJason M. Bills     if (offset != 0 || size != ipmi::sel::entireRecord)
853c04e2e70SJason M. Bills     {
8541d4d54ddSJason M. Bills         return ipmi::responseRetBytesUnavailable();
855c04e2e70SJason M. Bills     }
856c04e2e70SJason M. Bills 
8571d4d54ddSJason M. Bills     // Check the reservation ID if one is provided or required (only if the
8581d4d54ddSJason M. Bills     // offset is non-zero)
8591d4d54ddSJason M. Bills     if (reservationID != 0 || offset != 0)
860c04e2e70SJason M. Bills     {
8611d4d54ddSJason M. Bills         if (!checkSELReservation(reservationID))
862c04e2e70SJason M. Bills         {
8631d4d54ddSJason M. Bills             return ipmi::responseInvalidReservationId();
864c04e2e70SJason M. Bills         }
865c04e2e70SJason M. Bills     }
866c04e2e70SJason M. Bills 
8671d4d54ddSJason M. Bills     // Get the ipmi_sel log files
8681d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
8691d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
870c04e2e70SJason M. Bills     {
8711d4d54ddSJason M. Bills         return ipmi::responseSensorInvalid();
872c04e2e70SJason M. Bills     }
873c04e2e70SJason M. Bills 
8741d4d54ddSJason M. Bills     std::string targetEntry;
875c04e2e70SJason M. Bills 
876c04e2e70SJason M. Bills     if (targetID == ipmi::sel::firstEntry)
877c04e2e70SJason M. Bills     {
8781d4d54ddSJason M. Bills         // The first entry will be at the top of the oldest log file
8791d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.back());
8801d4d54ddSJason M. Bills         if (!logStream.is_open())
881c04e2e70SJason M. Bills         {
8821d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
883c04e2e70SJason M. Bills         }
8841d4d54ddSJason M. Bills 
8851d4d54ddSJason M. Bills         if (!std::getline(logStream, targetEntry))
8861d4d54ddSJason M. Bills         {
8871d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
888c04e2e70SJason M. Bills         }
889c04e2e70SJason M. Bills     }
890c04e2e70SJason M. Bills     else if (targetID == ipmi::sel::lastEntry)
891c04e2e70SJason M. Bills     {
8921d4d54ddSJason M. Bills         // The last entry will be at the bottom of the newest log file
8931d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.front());
8941d4d54ddSJason M. Bills         if (!logStream.is_open())
895c04e2e70SJason M. Bills         {
8961d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
897c04e2e70SJason M. Bills         }
898c04e2e70SJason M. Bills 
8991d4d54ddSJason M. Bills         std::string line;
9001d4d54ddSJason M. Bills         while (std::getline(logStream, line))
901c04e2e70SJason M. Bills         {
9021d4d54ddSJason M. Bills             targetEntry = line;
903c04e2e70SJason M. Bills         }
904c04e2e70SJason M. Bills     }
905c04e2e70SJason M. Bills     else
906c04e2e70SJason M. Bills     {
9071d4d54ddSJason M. Bills         if (!findSELEntry(targetID, selLogFiles, targetEntry))
908c04e2e70SJason M. Bills         {
9091d4d54ddSJason M. Bills             return ipmi::responseSensorInvalid();
9101d4d54ddSJason M. Bills         }
911c04e2e70SJason M. Bills     }
912c04e2e70SJason M. Bills 
91352aaa7d5SJason M. Bills     // The format of the ipmi_sel message is "<Timestamp>
91452aaa7d5SJason M. Bills     // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
91552aaa7d5SJason M. Bills     // First get the Timestamp
91652aaa7d5SJason M. Bills     size_t space = targetEntry.find_first_of(" ");
91752aaa7d5SJason M. Bills     if (space == std::string::npos)
91852aaa7d5SJason M. Bills     {
91952aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
92052aaa7d5SJason M. Bills     }
92152aaa7d5SJason M. Bills     std::string entryTimestamp = targetEntry.substr(0, space);
92252aaa7d5SJason M. Bills     // Then get the log contents
92352aaa7d5SJason M. Bills     size_t entryStart = targetEntry.find_first_not_of(" ", space);
92452aaa7d5SJason M. Bills     if (entryStart == std::string::npos)
92552aaa7d5SJason M. Bills     {
92652aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
92752aaa7d5SJason M. Bills     }
92852aaa7d5SJason M. Bills     std::string_view entry(targetEntry);
92952aaa7d5SJason M. Bills     entry.remove_prefix(entryStart);
93052aaa7d5SJason M. Bills     // Use split to separate the entry into its fields
9311d4d54ddSJason M. Bills     std::vector<std::string> targetEntryFields;
93252aaa7d5SJason M. Bills     boost::split(targetEntryFields, entry, boost::is_any_of(","),
9331d4d54ddSJason M. Bills                  boost::token_compress_on);
93452aaa7d5SJason M. Bills     if (targetEntryFields.size() < 3)
9351d4d54ddSJason M. Bills     {
9361d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
937c04e2e70SJason M. Bills     }
9381a2fbddfSJason M. Bills     std::string& recordIDStr = targetEntryFields[0];
9391a2fbddfSJason M. Bills     std::string& recordTypeStr = targetEntryFields[1];
9401a2fbddfSJason M. Bills     std::string& eventDataStr = targetEntryFields[2];
941c04e2e70SJason M. Bills 
9421a2fbddfSJason M. Bills     uint16_t recordID;
9431a2fbddfSJason M. Bills     uint8_t recordType;
9441a2fbddfSJason M. Bills     try
9451a2fbddfSJason M. Bills     {
9461a2fbddfSJason M. Bills         recordID = std::stoul(recordIDStr);
9471a2fbddfSJason M. Bills         recordType = std::stoul(recordTypeStr, nullptr, 16);
9481a2fbddfSJason M. Bills     }
9491a2fbddfSJason M. Bills     catch (const std::invalid_argument&)
9501a2fbddfSJason M. Bills     {
9511a2fbddfSJason M. Bills         return ipmi::responseUnspecifiedError();
9521a2fbddfSJason M. Bills     }
9531d4d54ddSJason M. Bills     uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
9541d4d54ddSJason M. Bills     std::vector<uint8_t> eventDataBytes;
9551a2fbddfSJason M. Bills     if (fromHexStr(eventDataStr, eventDataBytes) < 0)
9561d4d54ddSJason M. Bills     {
9571d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
9581d4d54ddSJason M. Bills     }
9591d4d54ddSJason M. Bills 
9601d4d54ddSJason M. Bills     if (recordType == intel_oem::ipmi::sel::systemEvent)
9611d4d54ddSJason M. Bills     {
9621d4d54ddSJason M. Bills         // Get the timestamp
9631d4d54ddSJason M. Bills         std::tm timeStruct = {};
96452aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
9651d4d54ddSJason M. Bills 
9661d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
9671d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
9681d4d54ddSJason M. Bills         {
9691d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
9701d4d54ddSJason M. Bills         }
9711d4d54ddSJason M. Bills 
9721d4d54ddSJason M. Bills         // Set the event message revision
9731d4d54ddSJason M. Bills         uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev;
9741d4d54ddSJason M. Bills 
9751a2fbddfSJason M. Bills         uint16_t generatorID = 0;
9761a2fbddfSJason M. Bills         uint8_t sensorType = 0;
977308c3a8bSJohnathan Mantey         uint16_t sensorAndLun = 0;
9781a2fbddfSJason M. Bills         uint8_t sensorNum = 0xFF;
9791a2fbddfSJason M. Bills         uint7_t eventType = 0;
9801a2fbddfSJason M. Bills         bool eventDir = 0;
9811a2fbddfSJason M. Bills         // System type events should have six fields
9821a2fbddfSJason M. Bills         if (targetEntryFields.size() >= 6)
9831a2fbddfSJason M. Bills         {
9841a2fbddfSJason M. Bills             std::string& generatorIDStr = targetEntryFields[3];
9851a2fbddfSJason M. Bills             std::string& sensorPath = targetEntryFields[4];
9861a2fbddfSJason M. Bills             std::string& eventDirStr = targetEntryFields[5];
9871a2fbddfSJason M. Bills 
9881a2fbddfSJason M. Bills             // Get the generator ID
9891a2fbddfSJason M. Bills             try
9901a2fbddfSJason M. Bills             {
9911a2fbddfSJason M. Bills                 generatorID = std::stoul(generatorIDStr, nullptr, 16);
9921a2fbddfSJason M. Bills             }
9931a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
9941a2fbddfSJason M. Bills             {
9951a2fbddfSJason M. Bills                 std::cerr << "Invalid Generator ID\n";
9961a2fbddfSJason M. Bills             }
9971a2fbddfSJason M. Bills 
9981d4d54ddSJason M. Bills             // Get the sensor type, sensor number, and event type for the sensor
9991a2fbddfSJason M. Bills             sensorType = getSensorTypeFromPath(sensorPath);
1000308c3a8bSJohnathan Mantey             sensorAndLun = getSensorNumberFromPath(sensorPath);
1001308c3a8bSJohnathan Mantey             sensorNum = static_cast<uint8_t>(sensorAndLun);
1002308c3a8bSJohnathan Mantey             generatorID |= sensorAndLun >> 8;
10031a2fbddfSJason M. Bills             eventType = getSensorEventTypeFromPath(sensorPath);
10041d4d54ddSJason M. Bills 
10051d4d54ddSJason M. Bills             // Get the event direction
10061a2fbddfSJason M. Bills             try
10071a2fbddfSJason M. Bills             {
10081a2fbddfSJason M. Bills                 eventDir = std::stoul(eventDirStr) ? 0 : 1;
10091a2fbddfSJason M. Bills             }
10101a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
10111a2fbddfSJason M. Bills             {
10121a2fbddfSJason M. Bills                 std::cerr << "Invalid Event Direction\n";
10131a2fbddfSJason M. Bills             }
10141a2fbddfSJason M. Bills         }
10151d4d54ddSJason M. Bills 
10161d4d54ddSJason M. Bills         // Only keep the eventData bytes that fit in the record
10171d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
10181d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10191d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10201d4d54ddSJason M. Bills                     eventData.begin());
10211d4d54ddSJason M. Bills 
10221d4d54ddSJason M. Bills         return ipmi::responseSuccess(
10231d4d54ddSJason M. Bills             nextRecordID, recordID, recordType,
10241d4d54ddSJason M. Bills             systemEventType{timestamp, generatorID, evmRev, sensorType,
10251d4d54ddSJason M. Bills                             sensorNum, eventType, eventDir, eventData});
10261d4d54ddSJason M. Bills     }
10271d4d54ddSJason M. Bills     else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst &&
10281d4d54ddSJason M. Bills              recordType <= intel_oem::ipmi::sel::oemTsEventLast)
10291d4d54ddSJason M. Bills     {
10301d4d54ddSJason M. Bills         // Get the timestamp
10311d4d54ddSJason M. Bills         std::tm timeStruct = {};
103252aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
10331d4d54ddSJason M. Bills 
10341d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
10351d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
10361d4d54ddSJason M. Bills         {
10371d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
10381d4d54ddSJason M. Bills         }
10391d4d54ddSJason M. Bills 
10401d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
10411d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{};
10421d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10431d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10441d4d54ddSJason M. Bills                     eventData.begin());
10451d4d54ddSJason M. Bills 
10461d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
10471d4d54ddSJason M. Bills                                      oemTsEventType{timestamp, eventData});
10481d4d54ddSJason M. Bills     }
1049c5136aaaSPatrick Venture     else if (recordType >= intel_oem::ipmi::sel::oemEventFirst)
10501d4d54ddSJason M. Bills     {
10511d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
10521d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{};
10531d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10541d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10551d4d54ddSJason M. Bills                     eventData.begin());
10561d4d54ddSJason M. Bills 
10571d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
10581d4d54ddSJason M. Bills                                      eventData);
10591d4d54ddSJason M. Bills     }
10601d4d54ddSJason M. Bills 
10611d4d54ddSJason M. Bills     return ipmi::responseUnspecifiedError();
1062c04e2e70SJason M. Bills }
1063c04e2e70SJason M. Bills 
10646dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(
10656dd8f047SJason M. Bills     uint16_t recordID, uint8_t recordType, uint32_t timestamp,
10666dd8f047SJason M. Bills     uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
10676dd8f047SJason M. Bills     uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
10686dd8f047SJason M. Bills     uint8_t eventData3)
1069c04e2e70SJason M. Bills {
1070c04e2e70SJason M. Bills     // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1071c04e2e70SJason M. Bills     // added
1072c04e2e70SJason M. Bills     cancelSELReservation();
1073c04e2e70SJason M. Bills 
10746dd8f047SJason M. Bills     // Send this request to the Redfish hooks to log it as a Redfish message
10756dd8f047SJason M. Bills     // instead.  There is no need to add it to the SEL, so just return success.
10766dd8f047SJason M. Bills     intel_oem::ipmi::sel::checkRedfishHooks(
10776dd8f047SJason M. Bills         recordID, recordType, timestamp, generatorID, evmRev, sensorType,
10786dd8f047SJason M. Bills         sensorNum, eventType, eventData1, eventData2, eventData3);
107999b78ec8SJason M. Bills 
10806dd8f047SJason M. Bills     uint16_t responseID = 0xFFFF;
10816dd8f047SJason M. Bills     return ipmi::responseSuccess(responseID);
1082c04e2e70SJason M. Bills }
1083c04e2e70SJason M. Bills 
1084dcff1506SVernon Mauery ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr&,
10851d4d54ddSJason M. Bills                                            uint16_t reservationID,
10861d4d54ddSJason M. Bills                                            const std::array<uint8_t, 3>& clr,
10871d4d54ddSJason M. Bills                                            uint8_t eraseOperation)
1088c04e2e70SJason M. Bills {
10891d4d54ddSJason M. Bills     if (!checkSELReservation(reservationID))
1090c04e2e70SJason M. Bills     {
10911d4d54ddSJason M. Bills         return ipmi::responseInvalidReservationId();
1092c04e2e70SJason M. Bills     }
1093c04e2e70SJason M. Bills 
10941d4d54ddSJason M. Bills     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
10951d4d54ddSJason M. Bills     if (clr != clrExpected)
1096c04e2e70SJason M. Bills     {
10971d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
1098c04e2e70SJason M. Bills     }
1099c04e2e70SJason M. Bills 
11001d4d54ddSJason M. Bills     // Erasure status cannot be fetched, so always return erasure status as
11011d4d54ddSJason M. Bills     // `erase completed`.
11021d4d54ddSJason M. Bills     if (eraseOperation == ipmi::sel::getEraseStatus)
1103c04e2e70SJason M. Bills     {
11041d4d54ddSJason M. Bills         return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1105c04e2e70SJason M. Bills     }
1106c04e2e70SJason M. Bills 
11071d4d54ddSJason M. Bills     // Check that initiate erase is correct
11081d4d54ddSJason M. Bills     if (eraseOperation != ipmi::sel::initiateErase)
11091d4d54ddSJason M. Bills     {
11101d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
11111d4d54ddSJason M. Bills     }
11121d4d54ddSJason M. Bills 
11131d4d54ddSJason M. Bills     // Per the IPMI spec, need to cancel any reservation when the SEL is
11141d4d54ddSJason M. Bills     // cleared
1115c04e2e70SJason M. Bills     cancelSELReservation();
1116c04e2e70SJason M. Bills 
11177944c307SJason M. Bills     // Save the erase time
11187944c307SJason M. Bills     intel_oem::ipmi::sel::erase_time::save();
11197944c307SJason M. Bills 
11201d4d54ddSJason M. Bills     // Clear the SEL by deleting the log files
11211d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
11221d4d54ddSJason M. Bills     if (getSELLogFiles(selLogFiles))
1123c04e2e70SJason M. Bills     {
11241d4d54ddSJason M. Bills         for (const std::filesystem::path& file : selLogFiles)
11251d4d54ddSJason M. Bills         {
11261d4d54ddSJason M. Bills             std::error_code ec;
11271d4d54ddSJason M. Bills             std::filesystem::remove(file, ec);
1128c04e2e70SJason M. Bills         }
1129c04e2e70SJason M. Bills     }
1130c04e2e70SJason M. Bills 
11311d4d54ddSJason M. Bills     // Reload rsyslog so it knows to start new log files
113215419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1133f944d2e5SPatrick Williams     sdbusplus::message_t rsyslogReload = dbus->new_method_call(
11341d4d54ddSJason M. Bills         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
11351d4d54ddSJason M. Bills         "org.freedesktop.systemd1.Manager", "ReloadUnit");
11361d4d54ddSJason M. Bills     rsyslogReload.append("rsyslog.service", "replace");
11371d4d54ddSJason M. Bills     try
11381d4d54ddSJason M. Bills     {
1139f944d2e5SPatrick Williams         sdbusplus::message_t reloadResponse = dbus->call(rsyslogReload);
11401d4d54ddSJason M. Bills     }
1141bd51e6a9SPatrick Williams     catch (const sdbusplus::exception_t& e)
11421d4d54ddSJason M. Bills     {
11431d4d54ddSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1144c04e2e70SJason M. Bills     }
1145c04e2e70SJason M. Bills 
11461d4d54ddSJason M. Bills     return ipmi::responseSuccess(ipmi::sel::eraseComplete);
11471d4d54ddSJason M. Bills }
11481d4d54ddSJason M. Bills 
11491a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
11501a47462eSJason M. Bills {
11511a47462eSJason M. Bills     struct timespec selTime = {};
11521a47462eSJason M. Bills 
11531a47462eSJason M. Bills     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
11541a47462eSJason M. Bills     {
11551a47462eSJason M. Bills         return ipmi::responseUnspecifiedError();
11561a47462eSJason M. Bills     }
11571a47462eSJason M. Bills 
11581a47462eSJason M. Bills     return ipmi::responseSuccess(selTime.tv_sec);
11591a47462eSJason M. Bills }
11601a47462eSJason M. Bills 
1161dcff1506SVernon Mauery ipmi::RspType<> ipmiStorageSetSELTime([[maybe_unused]] uint32_t selTime)
1162cac97a53SJason M. Bills {
1163cac97a53SJason M. Bills     // Set SEL Time is not supported
11641d4d54ddSJason M. Bills     return ipmi::responseInvalidCommand();
1165cac97a53SJason M. Bills }
1166cac97a53SJason M. Bills 
116774c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
116874c50c64SJames Feist {
116974c50c64SJames Feist     std::vector<uint8_t> resp;
117074c50c64SJames Feist     if (index == 0)
117174c50c64SJames Feist     {
117274c50c64SJames Feist         std::string bmcName = "Basbrd Mgmt Ctlr";
1173f4d5e05eSJohnathan Mantey         Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
117474c50c64SJames Feist         uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
117574c50c64SJames Feist         resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
117674c50c64SJames Feist     }
117774c50c64SJames Feist     else if (index == 1)
117874c50c64SJames Feist     {
117974c50c64SJames Feist         std::string meName = "Mgmt Engine";
1180f4d5e05eSJohnathan Mantey         Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
118174c50c64SJames Feist         uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
118274c50c64SJames Feist         resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
118374c50c64SJames Feist     }
118474c50c64SJames Feist     else
118574c50c64SJames Feist     {
118674c50c64SJames Feist         throw std::runtime_error("getType12SDRs:: Illegal index " +
118774c50c64SJames Feist                                  std::to_string(index));
118874c50c64SJames Feist     }
118974c50c64SJames Feist 
119074c50c64SJames Feist     return resp;
119174c50c64SJames Feist }
119274c50c64SJames Feist 
1193fee5e4c7SYong Li std::vector<uint8_t> getNMDiscoverySDR(uint16_t index, uint16_t recordId)
1194fee5e4c7SYong Li {
1195fee5e4c7SYong Li     std::vector<uint8_t> resp;
1196fee5e4c7SYong Li     if (index == 0)
1197fee5e4c7SYong Li     {
1198fee5e4c7SYong Li         NMDiscoveryRecord nm = {};
1199fee5e4c7SYong Li         nm.header.record_id_lsb = recordId;
1200fee5e4c7SYong Li         nm.header.record_id_msb = recordId >> 8;
1201fee5e4c7SYong Li         nm.header.sdr_version = ipmiSdrVersion;
1202fee5e4c7SYong Li         nm.header.record_type = 0xC0;
1203fee5e4c7SYong Li         nm.header.record_length = 0xB;
1204fee5e4c7SYong Li         nm.oemID0 = 0x57;
1205fee5e4c7SYong Li         nm.oemID1 = 0x1;
1206fee5e4c7SYong Li         nm.oemID2 = 0x0;
1207fee5e4c7SYong Li         nm.subType = 0x0D;
1208fee5e4c7SYong Li         nm.version = 0x1;
120980d4d5f9SMatt Simmering         nm.targetAddress = 0x2C;
1210fee5e4c7SYong Li         nm.channelNumber = 0x60;
1211fee5e4c7SYong Li         nm.healthEventSensor = 0x19;
1212fee5e4c7SYong Li         nm.exceptionEventSensor = 0x18;
1213fee5e4c7SYong Li         nm.operationalCapSensor = 0x1A;
1214fee5e4c7SYong Li         nm.thresholdExceededSensor = 0x1B;
1215fee5e4c7SYong Li 
1216fee5e4c7SYong Li         uint8_t* nmPtr = reinterpret_cast<uint8_t*>(&nm);
1217fee5e4c7SYong Li         resp.insert(resp.end(), nmPtr, nmPtr + sizeof(NMDiscoveryRecord));
1218fee5e4c7SYong Li     }
1219fee5e4c7SYong Li     else
1220fee5e4c7SYong Li     {
1221fee5e4c7SYong Li         throw std::runtime_error("getNMDiscoverySDR:: Illegal index " +
1222fee5e4c7SYong Li                                  std::to_string(index));
1223fee5e4c7SYong Li     }
1224fee5e4c7SYong Li 
1225fee5e4c7SYong Li     return resp;
1226fee5e4c7SYong Li }
1227fee5e4c7SYong Li 
1228e2d1aee3SJason M. Bills void registerStorageFunctions()
1229e2d1aee3SJason M. Bills {
12302569025eSJames Feist     createTimers();
1231e4f710d7SJames Feist     startMatch();
1232e4f710d7SJames Feist 
1233e2d1aee3SJason M. Bills     // <Get FRU Inventory Area Info>
1234d33acd6bSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1235d33acd6bSjayaprakash Mutyala                           ipmi::storage::cmdGetFruInventoryAreaInfo,
1236d33acd6bSjayaprakash Mutyala                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1237c04e2e70SJason M. Bills     // <READ FRU Data>
12385f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12395f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
12405f4194eeSjayaprakash Mutyala                           ipmiStorageReadFruData);
1241e2d1aee3SJason M. Bills 
1242c04e2e70SJason M. Bills     // <WRITE FRU Data>
12435f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12445f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdWriteFruData,
12455f4194eeSjayaprakash Mutyala                           ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1246c04e2e70SJason M. Bills 
1247c04e2e70SJason M. Bills     // <Get SEL Info>
12481d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1249542498e9SJason M. Bills                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1250542498e9SJason M. Bills                           ipmiStorageGetSELInfo);
1251c04e2e70SJason M. Bills 
1252c04e2e70SJason M. Bills     // <Get SEL Entry>
12531d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1254542498e9SJason M. Bills                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1255542498e9SJason M. Bills                           ipmiStorageGetSELEntry);
1256c04e2e70SJason M. Bills 
1257c04e2e70SJason M. Bills     // <Add SEL Entry>
12586dd8f047SJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
125998bbf69aSVernon Mauery                           ipmi::storage::cmdAddSelEntry,
12606dd8f047SJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1261c04e2e70SJason M. Bills 
1262c04e2e70SJason M. Bills     // <Clear SEL>
12631d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12641d4d54ddSJason M. Bills                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
12651d4d54ddSJason M. Bills                           ipmiStorageClearSEL);
1266cac97a53SJason M. Bills 
12671a47462eSJason M. Bills     // <Get SEL Time>
12681a47462eSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1269542498e9SJason M. Bills                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1270542498e9SJason M. Bills                           ipmiStorageGetSELTime);
12711a47462eSJason M. Bills 
1272cac97a53SJason M. Bills     // <Set SEL Time>
12731d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12741d4d54ddSJason M. Bills                           ipmi::storage::cmdSetSelTime,
12751d4d54ddSJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1276e2d1aee3SJason M. Bills }
12773f7c5e40SJason M. Bills } // namespace storage
12783f7c5e40SJason M. Bills } // namespace ipmi
1279