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>
27ee27fe09SJohnathan 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>
36ee27fe09SJohnathan Mantey #include <functional>
37fcd2d3a9SJames Feist #include <iostream>
38c04e2e70SJason M. Bills #include <stdexcept>
39ee27fe09SJohnathan 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 
getFileTimestamp(const std::filesystem::path & file)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 
save()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 
get()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 
writeFru()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 
createTimers()1572569025eSJames Feist void createTimers()
158e2d1aee3SJason M. Bills {
159f0feb49cSPatrick Williams     writeTimer = std::make_unique<sdbusplus::Timer>(writeFru);
160e2d1aee3SJason M. Bills }
161e2d1aee3SJason M. Bills 
recalculateHashes()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 
replaceCacheFru(const std::shared_ptr<sdbusplus::asio::connection> & bus,boost::asio::yield_context & yield)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 
232ee27fe09SJohnathan Mantey     frus = bus->yield_method_call<ManagedObjectType>(
233ee27fe09SJohnathan Mantey         yield, ec, fruDeviceServiceName, "/",
234ee27fe09SJohnathan Mantey         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
235e4f710d7SJames Feist     if (ec)
236e4f710d7SJames Feist     {
237e4f710d7SJames Feist         phosphor::logging::log<phosphor::logging::level::ERR>(
238ee27fe09SJohnathan 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 
getFru(ipmi::Context::ptr & ctx,uint8_t devId)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);
254ee27fe09SJohnathan 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>>(
273ee27fe09SJohnathan Mantey         ctx->yield, ec, fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
2742569025eSJames Feist         "xyz.openbmc_project.FruDeviceManager", "GetRawFru", cacheBus,
2752569025eSJames Feist         cacheAddr);
276ee27fe09SJohnathan 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 
writeFruIfRunning()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 
startMatch(void)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();
310*1bcced08SPatrick Williams     fruMatches.emplace_back(
311*1bcced08SPatrick Williams         *bus,
312e4f710d7SJames Feist         "type='signal',arg0path='/xyz/openbmc_project/"
313e4f710d7SJames Feist         "FruDevice/',member='InterfacesAdded'",
314f944d2e5SPatrick Williams         [](sdbusplus::message_t& message) {
315e4f710d7SJames Feist             sdbusplus::message::object_path path;
316e4f710d7SJames Feist             ObjectType object;
317e4f710d7SJames Feist             try
318e4f710d7SJames Feist             {
319e4f710d7SJames Feist                 message.read(path, object);
320e4f710d7SJames Feist             }
321bd51e6a9SPatrick Williams             catch (const sdbusplus::exception_t&)
322e4f710d7SJames Feist             {
323e4f710d7SJames Feist                 return;
324e4f710d7SJames Feist             }
325b37abfb2SPatrick Williams             auto findType = object.find("xyz.openbmc_project.FruDevice");
326e4f710d7SJames Feist             if (findType == object.end())
327e4f710d7SJames Feist             {
328e4f710d7SJames Feist                 return;
329e4f710d7SJames Feist             }
330e4f710d7SJames Feist             writeFruIfRunning();
331e4f710d7SJames Feist             frus[path] = object;
332e4f710d7SJames Feist             recalculateHashes();
333e4f710d7SJames Feist             lastDevId = 0xFF;
334e4f710d7SJames Feist         });
335e4f710d7SJames Feist 
336*1bcced08SPatrick Williams     fruMatches.emplace_back(
337*1bcced08SPatrick Williams         *bus,
338e4f710d7SJames Feist         "type='signal',arg0path='/xyz/openbmc_project/"
339e4f710d7SJames Feist         "FruDevice/',member='InterfacesRemoved'",
340f944d2e5SPatrick Williams         [](sdbusplus::message_t& message) {
341e4f710d7SJames Feist             sdbusplus::message::object_path path;
342e4f710d7SJames Feist             std::set<std::string> interfaces;
343e4f710d7SJames Feist             try
344e4f710d7SJames Feist             {
345e4f710d7SJames Feist                 message.read(path, interfaces);
346e4f710d7SJames Feist             }
347bd51e6a9SPatrick Williams             catch (const sdbusplus::exception_t&)
348e4f710d7SJames Feist             {
349e4f710d7SJames Feist                 return;
350e4f710d7SJames Feist             }
351b37abfb2SPatrick Williams             auto findType = interfaces.find("xyz.openbmc_project.FruDevice");
352e4f710d7SJames Feist             if (findType == interfaces.end())
353e4f710d7SJames Feist             {
354e4f710d7SJames Feist                 return;
355e4f710d7SJames Feist             }
356e4f710d7SJames Feist             writeFruIfRunning();
357e4f710d7SJames Feist             frus.erase(path);
358e4f710d7SJames Feist             recalculateHashes();
359e4f710d7SJames Feist             lastDevId = 0xFF;
360e4f710d7SJames Feist         });
361e4f710d7SJames Feist 
362e4f710d7SJames Feist     // call once to populate
363e4f710d7SJames Feist     boost::asio::spawn(*getIoContext(), [](boost::asio::yield_context yield) {
364e4f710d7SJames Feist         replaceCacheFru(getSdBus(), yield);
365e4f710d7SJames Feist     });
366e4f710d7SJames Feist }
367e4f710d7SJames Feist 
3685f4194eeSjayaprakash Mutyala /** @brief implements the read FRU data command
3695f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
3705f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
3715f4194eeSjayaprakash Mutyala  *  @param countToRead        - Count to read
3725f4194eeSjayaprakash Mutyala  *
3735f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
3745f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
3755f4194eeSjayaprakash Mutyala  */
3765f4194eeSjayaprakash Mutyala ipmi::RspType<uint8_t,             // Count
3775f4194eeSjayaprakash Mutyala               std::vector<uint8_t> // Requested data
3785f4194eeSjayaprakash Mutyala               >
ipmiStorageReadFruData(ipmi::Context::ptr & ctx,uint8_t fruDeviceId,uint16_t fruInventoryOffset,uint8_t countToRead)379dcff1506SVernon Mauery     ipmiStorageReadFruData(ipmi::Context::ptr& ctx, uint8_t fruDeviceId,
3802569025eSJames Feist                            uint16_t fruInventoryOffset, uint8_t countToRead)
381e2d1aee3SJason M. Bills {
3825f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
383e2d1aee3SJason M. Bills     {
3845f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
385e2d1aee3SJason M. Bills     }
386e2d1aee3SJason M. Bills 
387e4f710d7SJames Feist     ipmi::Cc status = getFru(ctx, fruDeviceId);
388e2d1aee3SJason M. Bills 
3895f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
3905f4194eeSjayaprakash Mutyala     {
3915f4194eeSjayaprakash Mutyala         return ipmi::response(status);
392e2d1aee3SJason M. Bills     }
393e2d1aee3SJason M. Bills 
3945f4194eeSjayaprakash Mutyala     size_t fromFruByteLen = 0;
3955f4194eeSjayaprakash Mutyala     if (countToRead + fruInventoryOffset < fruCache.size())
396e2d1aee3SJason M. Bills     {
3975f4194eeSjayaprakash Mutyala         fromFruByteLen = countToRead;
3985f4194eeSjayaprakash Mutyala     }
3995f4194eeSjayaprakash Mutyala     else if (fruCache.size() > fruInventoryOffset)
400e2d1aee3SJason M. Bills     {
4015f4194eeSjayaprakash Mutyala         fromFruByteLen = fruCache.size() - fruInventoryOffset;
4025f4194eeSjayaprakash Mutyala     }
4035f4194eeSjayaprakash Mutyala     else
4045f4194eeSjayaprakash Mutyala     {
4059210838eSsrikanta mondal         return ipmi::responseReqDataLenExceeded();
406e2d1aee3SJason M. Bills     }
407e2d1aee3SJason M. Bills 
4085f4194eeSjayaprakash Mutyala     std::vector<uint8_t> requestedData;
409e2d1aee3SJason M. Bills 
4105f4194eeSjayaprakash Mutyala     requestedData.insert(
4115f4194eeSjayaprakash Mutyala         requestedData.begin(), fruCache.begin() + fruInventoryOffset,
4125f4194eeSjayaprakash Mutyala         fruCache.begin() + fruInventoryOffset + fromFruByteLen);
4135f4194eeSjayaprakash Mutyala 
41470b17f93SPatrick Venture     return ipmi::responseSuccess(static_cast<uint8_t>(requestedData.size()),
41570b17f93SPatrick Venture                                  requestedData);
416e2d1aee3SJason M. Bills }
4175f4194eeSjayaprakash Mutyala 
4185f4194eeSjayaprakash Mutyala /** @brief implements the write FRU data command
4195f4194eeSjayaprakash Mutyala  *  @param fruDeviceId        - FRU Device ID
4205f4194eeSjayaprakash Mutyala  *  @param fruInventoryOffset - FRU Inventory Offset to write
4215f4194eeSjayaprakash Mutyala  *  @param dataToWrite        - Data to write
4225f4194eeSjayaprakash Mutyala  *
4235f4194eeSjayaprakash Mutyala  *  @returns ipmi completion code plus response data
4245f4194eeSjayaprakash Mutyala  *   - countWritten  - Count written
4255f4194eeSjayaprakash Mutyala  */
ipmiStorageWriteFruData(ipmi::Context::ptr & ctx,uint8_t fruDeviceId,uint16_t fruInventoryOffset,std::vector<uint8_t> & dataToWrite)426*1bcced08SPatrick Williams ipmi::RspType<uint8_t> ipmiStorageWriteFruData(
427*1bcced08SPatrick Williams     ipmi::Context::ptr& ctx, uint8_t fruDeviceId, uint16_t fruInventoryOffset,
4285f4194eeSjayaprakash Mutyala     std::vector<uint8_t>& dataToWrite)
4295f4194eeSjayaprakash Mutyala {
4305f4194eeSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
4315f4194eeSjayaprakash Mutyala     {
4325f4194eeSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
4335f4194eeSjayaprakash Mutyala     }
4345f4194eeSjayaprakash Mutyala 
4355f4194eeSjayaprakash Mutyala     size_t writeLen = dataToWrite.size();
4365f4194eeSjayaprakash Mutyala 
437e4f710d7SJames Feist     ipmi::Cc status = getFru(ctx, fruDeviceId);
4385f4194eeSjayaprakash Mutyala     if (status != ipmi::ccSuccess)
4395f4194eeSjayaprakash Mutyala     {
4405f4194eeSjayaprakash Mutyala         return ipmi::response(status);
4415f4194eeSjayaprakash Mutyala     }
442dcff1506SVernon Mauery     size_t lastWriteAddr = fruInventoryOffset + writeLen;
443e2d1aee3SJason M. Bills     if (fruCache.size() < lastWriteAddr)
444e2d1aee3SJason M. Bills     {
4455f4194eeSjayaprakash Mutyala         fruCache.resize(fruInventoryOffset + writeLen);
446e2d1aee3SJason M. Bills     }
447e2d1aee3SJason M. Bills 
4485f4194eeSjayaprakash Mutyala     std::copy(dataToWrite.begin(), dataToWrite.begin() + writeLen,
4495f4194eeSjayaprakash Mutyala               fruCache.begin() + fruInventoryOffset);
450e2d1aee3SJason M. Bills 
45121a1b5f8SPeter Foley     bool atEnd = validateBasicFruContent(fruCache, lastWriteAddr);
4525f4194eeSjayaprakash Mutyala     uint8_t countWritten = 0;
4532569025eSJames Feist 
4542569025eSJames Feist     writeBus = cacheBus;
4552569025eSJames Feist     writeAddr = cacheAddr;
456e2d1aee3SJason M. Bills     if (atEnd)
457e2d1aee3SJason M. Bills     {
458e2d1aee3SJason M. Bills         // cancel timer, we're at the end so might as well send it
4592569025eSJames Feist         writeTimer->stop();
460e2d1aee3SJason M. Bills         if (!writeFru())
461e2d1aee3SJason M. Bills         {
4625f4194eeSjayaprakash Mutyala             return ipmi::responseInvalidFieldRequest();
463e2d1aee3SJason M. Bills         }
4645f4194eeSjayaprakash Mutyala         countWritten = std::min(fruCache.size(), static_cast<size_t>(0xFF));
465e2d1aee3SJason M. Bills     }
466e2d1aee3SJason M. Bills     else
467e2d1aee3SJason M. Bills     {
4682569025eSJames Feist         // start a timer, if no further data is sent  to check to see if it is
4692569025eSJames Feist         // valid
4702569025eSJames Feist         writeTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
4712569025eSJames Feist             std::chrono::seconds(writeTimeoutSeconds)));
4725f4194eeSjayaprakash Mutyala         countWritten = 0;
473e2d1aee3SJason M. Bills     }
474e2d1aee3SJason M. Bills 
4755f4194eeSjayaprakash Mutyala     return ipmi::responseSuccess(countWritten);
476e2d1aee3SJason M. Bills }
477e2d1aee3SJason M. Bills 
478d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command
479d33acd6bSjayaprakash Mutyala  *  @param fruDeviceId  - FRU Device ID
480d33acd6bSjayaprakash Mutyala  *
481d33acd6bSjayaprakash Mutyala  *  @returns IPMI completion code plus response data
482d33acd6bSjayaprakash Mutyala  *   - inventorySize - Number of possible allocation units
483d33acd6bSjayaprakash Mutyala  *   - accessType    - Allocation unit size in bytes.
484d33acd6bSjayaprakash Mutyala  */
485d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize
486d33acd6bSjayaprakash Mutyala               uint8_t>  // accessType
ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr & ctx,uint8_t fruDeviceId)487dcff1506SVernon Mauery     ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr& ctx, uint8_t fruDeviceId)
488e2d1aee3SJason M. Bills {
489d33acd6bSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
490e2d1aee3SJason M. Bills     {
491d33acd6bSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
492e2d1aee3SJason M. Bills     }
493e2d1aee3SJason M. Bills 
4941e2ab061SJayaprakash Mutyala     ipmi::Cc ret = getFru(ctx, fruDeviceId);
4951e2ab061SJayaprakash Mutyala     if (ret != ipmi::ccSuccess)
4961e2ab061SJayaprakash Mutyala     {
4971e2ab061SJayaprakash Mutyala         return ipmi::response(ret);
4981e2ab061SJayaprakash Mutyala     }
499e2d1aee3SJason M. Bills 
500d33acd6bSjayaprakash Mutyala     constexpr uint8_t accessType =
501d33acd6bSjayaprakash Mutyala         static_cast<uint8_t>(GetFRUAreaAccessType::byte);
502e2d1aee3SJason M. Bills 
503d33acd6bSjayaprakash Mutyala     return ipmi::responseSuccess(fruCache.size(), accessType);
504e2d1aee3SJason M. Bills }
505e2d1aee3SJason M. Bills 
getFruSdrCount(ipmi::Context::ptr &,size_t & count)506dcff1506SVernon Mauery ipmi::Cc getFruSdrCount(ipmi::Context::ptr&, size_t& count)
5073f7c5e40SJason M. Bills {
5083f7c5e40SJason M. Bills     count = deviceHashes.size();
509dcff1506SVernon Mauery     return ipmi::ccSuccess;
5103f7c5e40SJason M. Bills }
5113f7c5e40SJason M. Bills 
getFruSdrs(ipmi::Context::ptr & ctx,size_t index,get_sdr::SensorDataFruRecord & resp)512dcff1506SVernon Mauery ipmi::Cc getFruSdrs(ipmi::Context::ptr& ctx, size_t index,
5132569025eSJames Feist                     get_sdr::SensorDataFruRecord& resp)
5143f7c5e40SJason M. Bills {
5153f7c5e40SJason M. Bills     if (deviceHashes.size() < index)
5163f7c5e40SJason M. Bills     {
517dcff1506SVernon Mauery         return ipmi::ccInvalidFieldRequest;
5183f7c5e40SJason M. Bills     }
5193f7c5e40SJason M. Bills     auto device = deviceHashes.begin() + index;
52007574006SJohnathan Mantey     uint16_t& bus = device->second.first;
5213f7c5e40SJason M. Bills     uint8_t& address = device->second.second;
5223f7c5e40SJason M. Bills 
5233f7c5e40SJason M. Bills     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
524*1bcced08SPatrick Williams     auto fru = std::find_if(
525*1bcced08SPatrick Williams         frus.begin(), frus.end(),
5263f7c5e40SJason M. Bills         [bus, address, &fruData](ManagedEntry& entry) {
527*1bcced08SPatrick Williams             auto findFruDevice =
528*1bcced08SPatrick Williams                 entry.second.find("xyz.openbmc_project.FruDevice");
5293f7c5e40SJason M. Bills             if (findFruDevice == entry.second.end())
5303f7c5e40SJason M. Bills             {
5313f7c5e40SJason M. Bills                 return false;
5323f7c5e40SJason M. Bills             }
5333f7c5e40SJason M. Bills             fruData = &(findFruDevice->second);
5343f7c5e40SJason M. Bills             auto findBus = findFruDevice->second.find("BUS");
535b37abfb2SPatrick Williams             auto findAddress = findFruDevice->second.find("ADDRESS");
5363f7c5e40SJason M. Bills             if (findBus == findFruDevice->second.end() ||
5373f7c5e40SJason M. Bills                 findAddress == findFruDevice->second.end())
5383f7c5e40SJason M. Bills             {
5393f7c5e40SJason M. Bills                 return false;
5403f7c5e40SJason M. Bills             }
5418166c8d7SVernon Mauery             if (std::get<uint32_t>(findBus->second) != bus)
5423f7c5e40SJason M. Bills             {
5433f7c5e40SJason M. Bills                 return false;
5443f7c5e40SJason M. Bills             }
5458166c8d7SVernon Mauery             if (std::get<uint32_t>(findAddress->second) != address)
5463f7c5e40SJason M. Bills             {
5473f7c5e40SJason M. Bills                 return false;
5483f7c5e40SJason M. Bills             }
5493f7c5e40SJason M. Bills             return true;
5503f7c5e40SJason M. Bills         });
5513f7c5e40SJason M. Bills     if (fru == frus.end())
5523f7c5e40SJason M. Bills     {
553dcff1506SVernon Mauery         return ipmi::ccResponseError;
5543f7c5e40SJason M. Bills     }
5559ce789fcSPatrick Venture 
5562569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS
5572569025eSJames Feist 
5589ce789fcSPatrick Venture     boost::container::flat_map<std::string, DbusVariant>* entityData = nullptr;
5599ce789fcSPatrick Venture 
5602569025eSJames Feist     // todo: this should really use caching, this is a very inefficient lookup
5612569025eSJames Feist     boost::system::error_code ec;
5622569025eSJames Feist     ManagedObjectType entities = ctx->bus->yield_method_call<ManagedObjectType>(
5639d2894d9SNan Zhou         ctx->yield, ec, "xyz.openbmc_project.EntityManager",
5649d2894d9SNan Zhou         "/xyz/openbmc_project/inventory", "org.freedesktop.DBus.ObjectManager",
5659d2894d9SNan Zhou         "GetManagedObjects");
5662569025eSJames Feist 
5672569025eSJames Feist     if (ec)
5689ce789fcSPatrick Venture     {
5692569025eSJames Feist         phosphor::logging::log<phosphor::logging::level::ERR>(
5702569025eSJames Feist             "GetMangagedObjects for getSensorMap failed",
5712569025eSJames Feist             phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
5729ce789fcSPatrick Venture 
5732569025eSJames Feist         return ipmi::ccResponseError;
5742569025eSJames Feist     }
5759ce789fcSPatrick Venture 
576*1bcced08SPatrick Williams     auto entity = std::find_if(
577*1bcced08SPatrick Williams         entities.begin(), entities.end(),
5789ce789fcSPatrick Venture         [bus, address, &entityData](ManagedEntry& entry) {
5799ce789fcSPatrick Venture             auto findFruDevice = entry.second.find(
5809ce789fcSPatrick Venture                 "xyz.openbmc_project.Inventory.Decorator.FruDevice");
5819ce789fcSPatrick Venture             if (findFruDevice == entry.second.end())
5829ce789fcSPatrick Venture             {
5839ce789fcSPatrick Venture                 return false;
5849ce789fcSPatrick Venture             }
5859ce789fcSPatrick Venture 
5869ce789fcSPatrick Venture             // Integer fields added via Entity-Manager json are uint64_ts by
5879ce789fcSPatrick Venture             // default.
5889ce789fcSPatrick Venture             auto findBus = findFruDevice->second.find("Bus");
5899ce789fcSPatrick Venture             auto findAddress = findFruDevice->second.find("Address");
5909ce789fcSPatrick Venture 
5919ce789fcSPatrick Venture             if (findBus == findFruDevice->second.end() ||
5929ce789fcSPatrick Venture                 findAddress == findFruDevice->second.end())
5939ce789fcSPatrick Venture             {
5949ce789fcSPatrick Venture                 return false;
5959ce789fcSPatrick Venture             }
5969ce789fcSPatrick Venture             if ((std::get<uint64_t>(findBus->second) != bus) ||
5979ce789fcSPatrick Venture                 (std::get<uint64_t>(findAddress->second) != address))
5989ce789fcSPatrick Venture             {
5999ce789fcSPatrick Venture                 return false;
6009ce789fcSPatrick Venture             }
6019ce789fcSPatrick Venture 
602b4b020c4SPatrick Venture             // At this point we found the device entry and should return
603b4b020c4SPatrick Venture             // true.
604*1bcced08SPatrick Williams             auto findIpmiDevice = entry.second.find(
605*1bcced08SPatrick Williams                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
606b4b020c4SPatrick Venture             if (findIpmiDevice != entry.second.end())
6079ce789fcSPatrick Venture             {
608b4b020c4SPatrick Venture                 entityData = &(findIpmiDevice->second);
6099ce789fcSPatrick Venture             }
6109ce789fcSPatrick Venture 
6119ce789fcSPatrick Venture             return true;
6129ce789fcSPatrick Venture         });
6139ce789fcSPatrick Venture 
6149ce789fcSPatrick Venture     if (entity == entities.end())
6159ce789fcSPatrick Venture     {
6169ce789fcSPatrick Venture         if constexpr (DEBUG)
6179ce789fcSPatrick Venture         {
6189ce789fcSPatrick Venture             std::fprintf(stderr, "Ipmi or FruDevice Decorator interface "
6199ce789fcSPatrick Venture                                  "not found for Fru\n");
6209ce789fcSPatrick Venture         }
6219ce789fcSPatrick Venture     }
6222569025eSJames Feist 
6232569025eSJames Feist #endif
6249ce789fcSPatrick Venture 
6253f7c5e40SJason M. Bills     std::string name;
6263f7c5e40SJason M. Bills     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
6273f7c5e40SJason M. Bills     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
6283f7c5e40SJason M. Bills     if (findProductName != fruData->end())
6293f7c5e40SJason M. Bills     {
6308166c8d7SVernon Mauery         name = std::get<std::string>(findProductName->second);
6313f7c5e40SJason M. Bills     }
6323f7c5e40SJason M. Bills     else if (findBoardName != fruData->end())
6333f7c5e40SJason M. Bills     {
6348166c8d7SVernon Mauery         name = std::get<std::string>(findBoardName->second);
6353f7c5e40SJason M. Bills     }
6363f7c5e40SJason M. Bills     else
6373f7c5e40SJason M. Bills     {
6383f7c5e40SJason M. Bills         name = "UNKNOWN";
6393f7c5e40SJason M. Bills     }
6403f7c5e40SJason M. Bills     if (name.size() > maxFruSdrNameSize)
6413f7c5e40SJason M. Bills     {
6423f7c5e40SJason M. Bills         name = name.substr(0, maxFruSdrNameSize);
6433f7c5e40SJason M. Bills     }
6443f7c5e40SJason M. Bills     size_t sizeDiff = maxFruSdrNameSize - name.size();
6453f7c5e40SJason M. Bills 
6463f7c5e40SJason M. Bills     resp.header.record_id_lsb = 0x0; // calling code is to implement these
6473f7c5e40SJason M. Bills     resp.header.record_id_msb = 0x0;
6483f7c5e40SJason M. Bills     resp.header.sdr_version = ipmiSdrVersion;
64973d0135dSPatrick Venture     resp.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
6503f7c5e40SJason M. Bills     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
6513f7c5e40SJason M. Bills     resp.key.deviceAddress = 0x20;
6523f7c5e40SJason M. Bills     resp.key.fruID = device->first;
6533f7c5e40SJason M. Bills     resp.key.accessLun = 0x80; // logical / physical fru device
6543f7c5e40SJason M. Bills     resp.key.channelNumber = 0x0;
6553f7c5e40SJason M. Bills     resp.body.reserved = 0x0;
6563f7c5e40SJason M. Bills     resp.body.deviceType = 0x10;
6574f86d1f2SJames Feist     resp.body.deviceTypeModifier = 0x0;
6589ce789fcSPatrick Venture 
6599ce789fcSPatrick Venture     uint8_t entityID = 0;
6609ce789fcSPatrick Venture     uint8_t entityInstance = 0x1;
6619ce789fcSPatrick Venture 
6622569025eSJames Feist #ifdef USING_ENTITY_MANAGER_DECORATORS
6639ce789fcSPatrick Venture     if (entityData)
6649ce789fcSPatrick Venture     {
6659ce789fcSPatrick Venture         auto entityIdProperty = entityData->find("EntityId");
6669ce789fcSPatrick Venture         auto entityInstanceProperty = entityData->find("EntityInstance");
6679ce789fcSPatrick Venture 
6689ce789fcSPatrick Venture         if (entityIdProperty != entityData->end())
6699ce789fcSPatrick Venture         {
6709ce789fcSPatrick Venture             entityID = static_cast<uint8_t>(
6719ce789fcSPatrick Venture                 std::get<uint64_t>(entityIdProperty->second));
6729ce789fcSPatrick Venture         }
6739ce789fcSPatrick Venture         if (entityInstanceProperty != entityData->end())
6749ce789fcSPatrick Venture         {
6759ce789fcSPatrick Venture             entityInstance = static_cast<uint8_t>(
6769ce789fcSPatrick Venture                 std::get<uint64_t>(entityInstanceProperty->second));
6779ce789fcSPatrick Venture         }
6789ce789fcSPatrick Venture     }
6792569025eSJames Feist #endif
6809ce789fcSPatrick Venture 
6819ce789fcSPatrick Venture     resp.body.entityID = entityID;
6829ce789fcSPatrick Venture     resp.body.entityInstance = entityInstance;
6839ce789fcSPatrick Venture 
6843f7c5e40SJason M. Bills     resp.body.oem = 0x0;
6853f7c5e40SJason M. Bills     resp.body.deviceIDLen = name.size();
6863f7c5e40SJason M. Bills     name.copy(resp.body.deviceID, name.size());
6873f7c5e40SJason M. Bills 
688dcff1506SVernon Mauery     return ipmi::ccSuccess;
6893f7c5e40SJason M. Bills }
690e2d1aee3SJason M. Bills 
getSELLogFiles(std::vector<std::filesystem::path> & selLogFiles)6911d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
692c04e2e70SJason M. Bills {
6931d4d54ddSJason M. Bills     // Loop through the directory looking for ipmi_sel log files
6941d4d54ddSJason M. Bills     for (const std::filesystem::directory_entry& dirEnt :
6951d4d54ddSJason M. Bills          std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir))
696c04e2e70SJason M. Bills     {
6971d4d54ddSJason M. Bills         std::string filename = dirEnt.path().filename();
6981d4d54ddSJason M. Bills         if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename))
6991d4d54ddSJason M. Bills         {
7001d4d54ddSJason M. Bills             // If we find an ipmi_sel log file, save the path
701*1bcced08SPatrick Williams             selLogFiles.emplace_back(
702*1bcced08SPatrick Williams                 intel_oem::ipmi::sel::selLogDir / filename);
703c04e2e70SJason M. Bills         }
704c04e2e70SJason M. Bills     }
7051d4d54ddSJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
7061d4d54ddSJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
7071d4d54ddSJason M. Bills     // can just sort the list to get them in order from newest to oldest
7081d4d54ddSJason M. Bills     std::sort(selLogFiles.begin(), selLogFiles.end());
709c04e2e70SJason M. Bills 
7101d4d54ddSJason M. Bills     return !selLogFiles.empty();
711c04e2e70SJason M. Bills }
712c04e2e70SJason M. Bills 
countSELEntries()7131d4d54ddSJason M. Bills static int countSELEntries()
7141d4d54ddSJason M. Bills {
7151d4d54ddSJason M. Bills     // Get the list of ipmi_sel log files
7161d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
7171d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
7181d4d54ddSJason M. Bills     {
7191d4d54ddSJason M. Bills         return 0;
7201d4d54ddSJason M. Bills     }
7211d4d54ddSJason M. Bills     int numSELEntries = 0;
7221d4d54ddSJason M. Bills     // Loop through each log file and count the number of logs
7231d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
7241d4d54ddSJason M. Bills     {
7251d4d54ddSJason M. Bills         std::ifstream logStream(file);
7261d4d54ddSJason M. Bills         if (!logStream.is_open())
7271d4d54ddSJason M. Bills         {
7281d4d54ddSJason M. Bills             continue;
7291d4d54ddSJason M. Bills         }
7301d4d54ddSJason M. Bills 
7311d4d54ddSJason M. Bills         std::string line;
7321d4d54ddSJason M. Bills         while (std::getline(logStream, line))
7331d4d54ddSJason M. Bills         {
7341d4d54ddSJason M. Bills             numSELEntries++;
7351d4d54ddSJason M. Bills         }
7361d4d54ddSJason M. Bills     }
7371d4d54ddSJason M. Bills     return numSELEntries;
7381d4d54ddSJason M. Bills }
7391d4d54ddSJason M. Bills 
findSELEntry(const int recordID,const std::vector<std::filesystem::path> & selLogFiles,std::string & entry)7401d4d54ddSJason M. Bills static bool findSELEntry(const int recordID,
741ff7e15b2SPatrick Venture                          const std::vector<std::filesystem::path>& selLogFiles,
7421d4d54ddSJason M. Bills                          std::string& entry)
7431d4d54ddSJason M. Bills {
7441d4d54ddSJason M. Bills     // Record ID is the first entry field following the timestamp. It is
7451d4d54ddSJason M. Bills     // preceded by a space and followed by a comma
7461d4d54ddSJason M. Bills     std::string search = " " + std::to_string(recordID) + ",";
7471d4d54ddSJason M. Bills 
7481d4d54ddSJason M. Bills     // Loop through the ipmi_sel log entries
7491d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
7501d4d54ddSJason M. Bills     {
7511d4d54ddSJason M. Bills         std::ifstream logStream(file);
7521d4d54ddSJason M. Bills         if (!logStream.is_open())
7531d4d54ddSJason M. Bills         {
7541d4d54ddSJason M. Bills             continue;
7551d4d54ddSJason M. Bills         }
7561d4d54ddSJason M. Bills 
7571d4d54ddSJason M. Bills         while (std::getline(logStream, entry))
7581d4d54ddSJason M. Bills         {
7591d4d54ddSJason M. Bills             // Check if the record ID matches
7601d4d54ddSJason M. Bills             if (entry.find(search) != std::string::npos)
7611d4d54ddSJason M. Bills             {
7621d4d54ddSJason M. Bills                 return true;
7631d4d54ddSJason M. Bills             }
7641d4d54ddSJason M. Bills         }
7651d4d54ddSJason M. Bills     }
7661d4d54ddSJason M. Bills     return false;
7671d4d54ddSJason M. Bills }
7681d4d54ddSJason M. Bills 
7691d4d54ddSJason M. Bills static uint16_t
getNextRecordID(const uint16_t recordID,const std::vector<std::filesystem::path> & selLogFiles)7701d4d54ddSJason M. Bills     getNextRecordID(const uint16_t recordID,
771ff7e15b2SPatrick Venture                     const std::vector<std::filesystem::path>& selLogFiles)
7721d4d54ddSJason M. Bills {
7731d4d54ddSJason M. Bills     uint16_t nextRecordID = recordID + 1;
7741d4d54ddSJason M. Bills     std::string entry;
7751d4d54ddSJason M. Bills     if (findSELEntry(nextRecordID, selLogFiles, entry))
7761d4d54ddSJason M. Bills     {
7771d4d54ddSJason M. Bills         return nextRecordID;
7781d4d54ddSJason M. Bills     }
7791d4d54ddSJason M. Bills     else
7801d4d54ddSJason M. Bills     {
7811d4d54ddSJason M. Bills         return ipmi::sel::lastEntry;
7821d4d54ddSJason M. Bills     }
783c04e2e70SJason M. Bills }
784c04e2e70SJason M. Bills 
fromHexStr(const std::string & hexStr,std::vector<uint8_t> & data)785ff7e15b2SPatrick Venture static int fromHexStr(const std::string& hexStr, std::vector<uint8_t>& data)
786c04e2e70SJason M. Bills {
787c04e2e70SJason M. Bills     for (unsigned int i = 0; i < hexStr.size(); i += 2)
788c04e2e70SJason M. Bills     {
789c04e2e70SJason M. Bills         try
790c04e2e70SJason M. Bills         {
791c04e2e70SJason M. Bills             data.push_back(static_cast<uint8_t>(
792c04e2e70SJason M. Bills                 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
793c04e2e70SJason M. Bills         }
794bd51e6a9SPatrick Williams         catch (const std::invalid_argument& e)
795c04e2e70SJason M. Bills         {
796c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
797c04e2e70SJason M. Bills             return -1;
798c04e2e70SJason M. Bills         }
799bd51e6a9SPatrick Williams         catch (const std::out_of_range& e)
800c04e2e70SJason M. Bills         {
801c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
802c04e2e70SJason M. Bills             return -1;
803c04e2e70SJason M. Bills         }
804c04e2e70SJason M. Bills     }
805c04e2e70SJason M. Bills     return 0;
806c04e2e70SJason M. Bills }
807c04e2e70SJason M. Bills 
8081d4d54ddSJason M. Bills ipmi::RspType<uint8_t,  // SEL version
8091d4d54ddSJason M. Bills               uint16_t, // SEL entry count
8101d4d54ddSJason M. Bills               uint16_t, // free space
8111d4d54ddSJason M. Bills               uint32_t, // last add timestamp
8121d4d54ddSJason M. Bills               uint32_t, // last erase timestamp
8131d4d54ddSJason M. Bills               uint8_t>  // operation support
ipmiStorageGetSELInfo()8141d4d54ddSJason M. Bills     ipmiStorageGetSELInfo()
815c04e2e70SJason M. Bills {
8161d4d54ddSJason M. Bills     constexpr uint8_t selVersion = ipmi::sel::selVersion;
8171d4d54ddSJason M. Bills     uint16_t entries = countSELEntries();
8181d4d54ddSJason M. Bills     uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp(
8191d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename);
8201d4d54ddSJason M. Bills     uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get();
8211d4d54ddSJason M. Bills     constexpr uint8_t operationSupport =
8221d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selOperationSupport;
8231d4d54ddSJason M. Bills     constexpr uint16_t freeSpace =
8241d4d54ddSJason M. Bills         0xffff; // Spec indicates that more than 64kB is free
825c04e2e70SJason M. Bills 
8261d4d54ddSJason M. Bills     return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
8271d4d54ddSJason M. Bills                                  eraseTimeStamp, operationSupport);
828c04e2e70SJason M. Bills }
829c04e2e70SJason M. Bills 
8301d4d54ddSJason M. Bills using systemEventType = std::tuple<
8311d4d54ddSJason M. Bills     uint32_t, // Timestamp
8321d4d54ddSJason M. Bills     uint16_t, // Generator ID
8331d4d54ddSJason M. Bills     uint8_t,  // EvM Rev
8341d4d54ddSJason M. Bills     uint8_t,  // Sensor Type
8351d4d54ddSJason M. Bills     uint8_t,  // Sensor Number
8361d4d54ddSJason M. Bills     uint7_t,  // Event Type
8371d4d54ddSJason M. Bills     bool,     // Event Direction
8381d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
8391d4d54ddSJason M. Bills using oemTsEventType = std::tuple<
8401d4d54ddSJason M. Bills     uint32_t,                                                    // Timestamp
8411d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>;  // Event Data
8421d4d54ddSJason M. Bills using oemEventType =
8431d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>;     // Event Data
8441d4d54ddSJason M. Bills 
8451d4d54ddSJason M. Bills ipmi::RspType<uint16_t,                   // Next Record ID
8461d4d54ddSJason M. Bills               uint16_t,                   // Record ID
8471d4d54ddSJason M. Bills               uint8_t,                    // Record Type
8481d4d54ddSJason M. Bills               std::variant<systemEventType, oemTsEventType,
8491d4d54ddSJason M. Bills                            oemEventType>> // Record Content
ipmiStorageGetSELEntry(uint16_t reservationID,uint16_t targetID,uint8_t offset,uint8_t size)8501d4d54ddSJason M. Bills     ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
8511d4d54ddSJason M. Bills                            uint8_t offset, uint8_t size)
852c04e2e70SJason M. Bills {
8531d4d54ddSJason M. Bills     // Only support getting the entire SEL record. If a partial size or non-zero
8541d4d54ddSJason M. Bills     // offset is requested, return an error
8551d4d54ddSJason M. Bills     if (offset != 0 || size != ipmi::sel::entireRecord)
856c04e2e70SJason M. Bills     {
8571d4d54ddSJason M. Bills         return ipmi::responseRetBytesUnavailable();
858c04e2e70SJason M. Bills     }
859c04e2e70SJason M. Bills 
8601d4d54ddSJason M. Bills     // Check the reservation ID if one is provided or required (only if the
8611d4d54ddSJason M. Bills     // offset is non-zero)
8621d4d54ddSJason M. Bills     if (reservationID != 0 || offset != 0)
863c04e2e70SJason M. Bills     {
8641d4d54ddSJason M. Bills         if (!checkSELReservation(reservationID))
865c04e2e70SJason M. Bills         {
8661d4d54ddSJason M. Bills             return ipmi::responseInvalidReservationId();
867c04e2e70SJason M. Bills         }
868c04e2e70SJason M. Bills     }
869c04e2e70SJason M. Bills 
8701d4d54ddSJason M. Bills     // Get the ipmi_sel log files
8711d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
8721d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
873c04e2e70SJason M. Bills     {
8741d4d54ddSJason M. Bills         return ipmi::responseSensorInvalid();
875c04e2e70SJason M. Bills     }
876c04e2e70SJason M. Bills 
8771d4d54ddSJason M. Bills     std::string targetEntry;
878c04e2e70SJason M. Bills 
879c04e2e70SJason M. Bills     if (targetID == ipmi::sel::firstEntry)
880c04e2e70SJason M. Bills     {
8811d4d54ddSJason M. Bills         // The first entry will be at the top of the oldest log file
8821d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.back());
8831d4d54ddSJason M. Bills         if (!logStream.is_open())
884c04e2e70SJason M. Bills         {
8851d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
886c04e2e70SJason M. Bills         }
8871d4d54ddSJason M. Bills 
8881d4d54ddSJason M. Bills         if (!std::getline(logStream, targetEntry))
8891d4d54ddSJason M. Bills         {
8901d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
891c04e2e70SJason M. Bills         }
892c04e2e70SJason M. Bills     }
893c04e2e70SJason M. Bills     else if (targetID == ipmi::sel::lastEntry)
894c04e2e70SJason M. Bills     {
8951d4d54ddSJason M. Bills         // The last entry will be at the bottom of the newest log file
8961d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.front());
8971d4d54ddSJason M. Bills         if (!logStream.is_open())
898c04e2e70SJason M. Bills         {
8991d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
900c04e2e70SJason M. Bills         }
901c04e2e70SJason M. Bills 
9021d4d54ddSJason M. Bills         std::string line;
9031d4d54ddSJason M. Bills         while (std::getline(logStream, line))
904c04e2e70SJason M. Bills         {
9051d4d54ddSJason M. Bills             targetEntry = line;
906c04e2e70SJason M. Bills         }
907c04e2e70SJason M. Bills     }
908c04e2e70SJason M. Bills     else
909c04e2e70SJason M. Bills     {
9101d4d54ddSJason M. Bills         if (!findSELEntry(targetID, selLogFiles, targetEntry))
911c04e2e70SJason M. Bills         {
9121d4d54ddSJason M. Bills             return ipmi::responseSensorInvalid();
9131d4d54ddSJason M. Bills         }
914c04e2e70SJason M. Bills     }
915c04e2e70SJason M. Bills 
91652aaa7d5SJason M. Bills     // The format of the ipmi_sel message is "<Timestamp>
91752aaa7d5SJason M. Bills     // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
91852aaa7d5SJason M. Bills     // First get the Timestamp
91952aaa7d5SJason M. Bills     size_t space = targetEntry.find_first_of(" ");
92052aaa7d5SJason M. Bills     if (space == std::string::npos)
92152aaa7d5SJason M. Bills     {
92252aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
92352aaa7d5SJason M. Bills     }
92452aaa7d5SJason M. Bills     std::string entryTimestamp = targetEntry.substr(0, space);
92552aaa7d5SJason M. Bills     // Then get the log contents
92652aaa7d5SJason M. Bills     size_t entryStart = targetEntry.find_first_not_of(" ", space);
92752aaa7d5SJason M. Bills     if (entryStart == std::string::npos)
92852aaa7d5SJason M. Bills     {
92952aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
93052aaa7d5SJason M. Bills     }
93152aaa7d5SJason M. Bills     std::string_view entry(targetEntry);
93252aaa7d5SJason M. Bills     entry.remove_prefix(entryStart);
93352aaa7d5SJason M. Bills     // Use split to separate the entry into its fields
9341d4d54ddSJason M. Bills     std::vector<std::string> targetEntryFields;
93552aaa7d5SJason M. Bills     boost::split(targetEntryFields, entry, boost::is_any_of(","),
9361d4d54ddSJason M. Bills                  boost::token_compress_on);
93752aaa7d5SJason M. Bills     if (targetEntryFields.size() < 3)
9381d4d54ddSJason M. Bills     {
9391d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
940c04e2e70SJason M. Bills     }
9411a2fbddfSJason M. Bills     std::string& recordIDStr = targetEntryFields[0];
9421a2fbddfSJason M. Bills     std::string& recordTypeStr = targetEntryFields[1];
9431a2fbddfSJason M. Bills     std::string& eventDataStr = targetEntryFields[2];
944c04e2e70SJason M. Bills 
9451a2fbddfSJason M. Bills     uint16_t recordID;
9461a2fbddfSJason M. Bills     uint8_t recordType;
9471a2fbddfSJason M. Bills     try
9481a2fbddfSJason M. Bills     {
9491a2fbddfSJason M. Bills         recordID = std::stoul(recordIDStr);
9501a2fbddfSJason M. Bills         recordType = std::stoul(recordTypeStr, nullptr, 16);
9511a2fbddfSJason M. Bills     }
9521a2fbddfSJason M. Bills     catch (const std::invalid_argument&)
9531a2fbddfSJason M. Bills     {
9541a2fbddfSJason M. Bills         return ipmi::responseUnspecifiedError();
9551a2fbddfSJason M. Bills     }
9561d4d54ddSJason M. Bills     uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
9571d4d54ddSJason M. Bills     std::vector<uint8_t> eventDataBytes;
9581a2fbddfSJason M. Bills     if (fromHexStr(eventDataStr, eventDataBytes) < 0)
9591d4d54ddSJason M. Bills     {
9601d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
9611d4d54ddSJason M. Bills     }
9621d4d54ddSJason M. Bills 
9631d4d54ddSJason M. Bills     if (recordType == intel_oem::ipmi::sel::systemEvent)
9641d4d54ddSJason M. Bills     {
9651d4d54ddSJason M. Bills         // Get the timestamp
9661d4d54ddSJason M. Bills         std::tm timeStruct = {};
96752aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
9681d4d54ddSJason M. Bills 
9691d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
9701d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
9711d4d54ddSJason M. Bills         {
9721d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
9731d4d54ddSJason M. Bills         }
9741d4d54ddSJason M. Bills 
9751d4d54ddSJason M. Bills         // Set the event message revision
9761d4d54ddSJason M. Bills         uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev;
9771d4d54ddSJason M. Bills 
9781a2fbddfSJason M. Bills         uint16_t generatorID = 0;
9791a2fbddfSJason M. Bills         uint8_t sensorType = 0;
980308c3a8bSJohnathan Mantey         uint16_t sensorAndLun = 0;
9811a2fbddfSJason M. Bills         uint8_t sensorNum = 0xFF;
9821a2fbddfSJason M. Bills         uint7_t eventType = 0;
9831a2fbddfSJason M. Bills         bool eventDir = 0;
9841a2fbddfSJason M. Bills         // System type events should have six fields
9851a2fbddfSJason M. Bills         if (targetEntryFields.size() >= 6)
9861a2fbddfSJason M. Bills         {
9871a2fbddfSJason M. Bills             std::string& generatorIDStr = targetEntryFields[3];
9881a2fbddfSJason M. Bills             std::string& sensorPath = targetEntryFields[4];
9891a2fbddfSJason M. Bills             std::string& eventDirStr = targetEntryFields[5];
9901a2fbddfSJason M. Bills 
9911a2fbddfSJason M. Bills             // Get the generator ID
9921a2fbddfSJason M. Bills             try
9931a2fbddfSJason M. Bills             {
9941a2fbddfSJason M. Bills                 generatorID = std::stoul(generatorIDStr, nullptr, 16);
9951a2fbddfSJason M. Bills             }
9961a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
9971a2fbddfSJason M. Bills             {
9981a2fbddfSJason M. Bills                 std::cerr << "Invalid Generator ID\n";
9991a2fbddfSJason M. Bills             }
10001a2fbddfSJason M. Bills 
10011d4d54ddSJason M. Bills             // Get the sensor type, sensor number, and event type for the sensor
10021a2fbddfSJason M. Bills             sensorType = getSensorTypeFromPath(sensorPath);
1003308c3a8bSJohnathan Mantey             sensorAndLun = getSensorNumberFromPath(sensorPath);
1004308c3a8bSJohnathan Mantey             sensorNum = static_cast<uint8_t>(sensorAndLun);
1005308c3a8bSJohnathan Mantey             generatorID |= sensorAndLun >> 8;
10061a2fbddfSJason M. Bills             eventType = getSensorEventTypeFromPath(sensorPath);
10071d4d54ddSJason M. Bills 
10081d4d54ddSJason M. Bills             // Get the event direction
10091a2fbddfSJason M. Bills             try
10101a2fbddfSJason M. Bills             {
10111a2fbddfSJason M. Bills                 eventDir = std::stoul(eventDirStr) ? 0 : 1;
10121a2fbddfSJason M. Bills             }
10131a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
10141a2fbddfSJason M. Bills             {
10151a2fbddfSJason M. Bills                 std::cerr << "Invalid Event Direction\n";
10161a2fbddfSJason M. Bills             }
10171a2fbddfSJason M. Bills         }
10181d4d54ddSJason M. Bills 
10191d4d54ddSJason M. Bills         // Only keep the eventData bytes that fit in the record
10201d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
10211d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10221d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10231d4d54ddSJason M. Bills                     eventData.begin());
10241d4d54ddSJason M. Bills 
10251d4d54ddSJason M. Bills         return ipmi::responseSuccess(
10261d4d54ddSJason M. Bills             nextRecordID, recordID, recordType,
10271d4d54ddSJason M. Bills             systemEventType{timestamp, generatorID, evmRev, sensorType,
10281d4d54ddSJason M. Bills                             sensorNum, eventType, eventDir, eventData});
10291d4d54ddSJason M. Bills     }
10301d4d54ddSJason M. Bills     else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst &&
10311d4d54ddSJason M. Bills              recordType <= intel_oem::ipmi::sel::oemTsEventLast)
10321d4d54ddSJason M. Bills     {
10331d4d54ddSJason M. Bills         // Get the timestamp
10341d4d54ddSJason M. Bills         std::tm timeStruct = {};
103552aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
10361d4d54ddSJason M. Bills 
10371d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
10381d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
10391d4d54ddSJason M. Bills         {
10401d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
10411d4d54ddSJason M. Bills         }
10421d4d54ddSJason M. Bills 
10431d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
10441d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{};
10451d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10461d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10471d4d54ddSJason M. Bills                     eventData.begin());
10481d4d54ddSJason M. Bills 
10491d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
10501d4d54ddSJason M. Bills                                      oemTsEventType{timestamp, eventData});
10511d4d54ddSJason M. Bills     }
1052c5136aaaSPatrick Venture     else if (recordType >= intel_oem::ipmi::sel::oemEventFirst)
10531d4d54ddSJason M. Bills     {
10541d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
10551d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{};
10561d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
10571d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
10581d4d54ddSJason M. Bills                     eventData.begin());
10591d4d54ddSJason M. Bills 
10601d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
10611d4d54ddSJason M. Bills                                      eventData);
10621d4d54ddSJason M. Bills     }
10631d4d54ddSJason M. Bills 
10641d4d54ddSJason M. Bills     return ipmi::responseUnspecifiedError();
1065c04e2e70SJason M. Bills }
1066c04e2e70SJason M. Bills 
ipmiStorageAddSELEntry(uint16_t recordID,uint8_t recordType,uint32_t timestamp,uint16_t generatorID,uint8_t evmRev,uint8_t sensorType,uint8_t sensorNum,uint8_t eventType,uint8_t eventData1,uint8_t eventData2,uint8_t eventData3)10676dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(
10686dd8f047SJason M. Bills     uint16_t recordID, uint8_t recordType, uint32_t timestamp,
10696dd8f047SJason M. Bills     uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
10706dd8f047SJason M. Bills     uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
10716dd8f047SJason M. Bills     uint8_t eventData3)
1072c04e2e70SJason M. Bills {
1073c04e2e70SJason M. Bills     // Per the IPMI spec, need to cancel any reservation when a SEL entry is
1074c04e2e70SJason M. Bills     // added
1075c04e2e70SJason M. Bills     cancelSELReservation();
1076c04e2e70SJason M. Bills 
10776dd8f047SJason M. Bills     // Send this request to the Redfish hooks to log it as a Redfish message
10786dd8f047SJason M. Bills     // instead.  There is no need to add it to the SEL, so just return success.
10796dd8f047SJason M. Bills     intel_oem::ipmi::sel::checkRedfishHooks(
10806dd8f047SJason M. Bills         recordID, recordType, timestamp, generatorID, evmRev, sensorType,
10816dd8f047SJason M. Bills         sensorNum, eventType, eventData1, eventData2, eventData3);
108299b78ec8SJason M. Bills 
10836dd8f047SJason M. Bills     uint16_t responseID = 0xFFFF;
10846dd8f047SJason M. Bills     return ipmi::responseSuccess(responseID);
1085c04e2e70SJason M. Bills }
1086c04e2e70SJason M. Bills 
ipmiStorageClearSEL(ipmi::Context::ptr &,uint16_t reservationID,const std::array<uint8_t,3> & clr,uint8_t eraseOperation)1087*1bcced08SPatrick Williams ipmi::RspType<uint8_t> ipmiStorageClearSEL(
1088*1bcced08SPatrick Williams     ipmi::Context::ptr&, uint16_t reservationID,
1089*1bcced08SPatrick Williams     const std::array<uint8_t, 3>& clr, uint8_t eraseOperation)
1090c04e2e70SJason M. Bills {
10911d4d54ddSJason M. Bills     if (!checkSELReservation(reservationID))
1092c04e2e70SJason M. Bills     {
10931d4d54ddSJason M. Bills         return ipmi::responseInvalidReservationId();
1094c04e2e70SJason M. Bills     }
1095c04e2e70SJason M. Bills 
10961d4d54ddSJason M. Bills     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
10971d4d54ddSJason M. Bills     if (clr != clrExpected)
1098c04e2e70SJason M. Bills     {
10991d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
1100c04e2e70SJason M. Bills     }
1101c04e2e70SJason M. Bills 
11021d4d54ddSJason M. Bills     // Erasure status cannot be fetched, so always return erasure status as
11031d4d54ddSJason M. Bills     // `erase completed`.
11041d4d54ddSJason M. Bills     if (eraseOperation == ipmi::sel::getEraseStatus)
1105c04e2e70SJason M. Bills     {
11061d4d54ddSJason M. Bills         return ipmi::responseSuccess(ipmi::sel::eraseComplete);
1107c04e2e70SJason M. Bills     }
1108c04e2e70SJason M. Bills 
11091d4d54ddSJason M. Bills     // Check that initiate erase is correct
11101d4d54ddSJason M. Bills     if (eraseOperation != ipmi::sel::initiateErase)
11111d4d54ddSJason M. Bills     {
11121d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
11131d4d54ddSJason M. Bills     }
11141d4d54ddSJason M. Bills 
11151d4d54ddSJason M. Bills     // Per the IPMI spec, need to cancel any reservation when the SEL is
11161d4d54ddSJason M. Bills     // cleared
1117c04e2e70SJason M. Bills     cancelSELReservation();
1118c04e2e70SJason M. Bills 
11197944c307SJason M. Bills     // Save the erase time
11207944c307SJason M. Bills     intel_oem::ipmi::sel::erase_time::save();
11217944c307SJason M. Bills 
11221d4d54ddSJason M. Bills     // Clear the SEL by deleting the log files
11231d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
11241d4d54ddSJason M. Bills     if (getSELLogFiles(selLogFiles))
1125c04e2e70SJason M. Bills     {
11261d4d54ddSJason M. Bills         for (const std::filesystem::path& file : selLogFiles)
11271d4d54ddSJason M. Bills         {
11281d4d54ddSJason M. Bills             std::error_code ec;
11291d4d54ddSJason M. Bills             std::filesystem::remove(file, ec);
1130c04e2e70SJason M. Bills         }
1131c04e2e70SJason M. Bills     }
1132c04e2e70SJason M. Bills 
11331d4d54ddSJason M. Bills     // Reload rsyslog so it knows to start new log files
113415419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
1135f944d2e5SPatrick Williams     sdbusplus::message_t rsyslogReload = dbus->new_method_call(
11361d4d54ddSJason M. Bills         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
11371d4d54ddSJason M. Bills         "org.freedesktop.systemd1.Manager", "ReloadUnit");
11381d4d54ddSJason M. Bills     rsyslogReload.append("rsyslog.service", "replace");
11391d4d54ddSJason M. Bills     try
11401d4d54ddSJason M. Bills     {
1141f944d2e5SPatrick Williams         sdbusplus::message_t reloadResponse = dbus->call(rsyslogReload);
11421d4d54ddSJason M. Bills     }
1143bd51e6a9SPatrick Williams     catch (const sdbusplus::exception_t& e)
11441d4d54ddSJason M. Bills     {
11451d4d54ddSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1146c04e2e70SJason M. Bills     }
1147c04e2e70SJason M. Bills 
11481d4d54ddSJason M. Bills     return ipmi::responseSuccess(ipmi::sel::eraseComplete);
11491d4d54ddSJason M. Bills }
11501d4d54ddSJason M. Bills 
ipmiStorageGetSELTime()11511a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
11521a47462eSJason M. Bills {
11531a47462eSJason M. Bills     struct timespec selTime = {};
11541a47462eSJason M. Bills 
11551a47462eSJason M. Bills     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
11561a47462eSJason M. Bills     {
11571a47462eSJason M. Bills         return ipmi::responseUnspecifiedError();
11581a47462eSJason M. Bills     }
11591a47462eSJason M. Bills 
11601a47462eSJason M. Bills     return ipmi::responseSuccess(selTime.tv_sec);
11611a47462eSJason M. Bills }
11621a47462eSJason M. Bills 
ipmiStorageSetSELTime(uint32_t selTime)1163dcff1506SVernon Mauery ipmi::RspType<> ipmiStorageSetSELTime([[maybe_unused]] uint32_t selTime)
1164cac97a53SJason M. Bills {
1165cac97a53SJason M. Bills     // Set SEL Time is not supported
11661d4d54ddSJason M. Bills     return ipmi::responseInvalidCommand();
1167cac97a53SJason M. Bills }
1168cac97a53SJason M. Bills 
getType12SDRs(uint16_t index,uint16_t recordId)116974c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
117074c50c64SJames Feist {
117174c50c64SJames Feist     std::vector<uint8_t> resp;
117274c50c64SJames Feist     if (index == 0)
117374c50c64SJames Feist     {
117474c50c64SJames Feist         std::string bmcName = "Basbrd Mgmt Ctlr";
1175f4d5e05eSJohnathan Mantey         Type12Record bmc(recordId, 0x20, 0, 0, 0xbf, 0x2e, 1, 0, bmcName);
117674c50c64SJames Feist         uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
117774c50c64SJames Feist         resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
117874c50c64SJames Feist     }
117974c50c64SJames Feist     else if (index == 1)
118074c50c64SJames Feist     {
118174c50c64SJames Feist         std::string meName = "Mgmt Engine";
1182f4d5e05eSJohnathan Mantey         Type12Record me(recordId, 0x2c, 6, 0x24, 0x21, 0x2e, 2, 0, meName);
118374c50c64SJames Feist         uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
118474c50c64SJames Feist         resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
118574c50c64SJames Feist     }
118674c50c64SJames Feist     else
118774c50c64SJames Feist     {
1188*1bcced08SPatrick Williams         throw std::runtime_error(
1189*1bcced08SPatrick Williams             "getType12SDRs:: Illegal index " + std::to_string(index));
119074c50c64SJames Feist     }
119174c50c64SJames Feist 
119274c50c64SJames Feist     return resp;
119374c50c64SJames Feist }
119474c50c64SJames Feist 
getNMDiscoverySDR(uint16_t index,uint16_t recordId)1195fee5e4c7SYong Li std::vector<uint8_t> getNMDiscoverySDR(uint16_t index, uint16_t recordId)
1196fee5e4c7SYong Li {
1197fee5e4c7SYong Li     std::vector<uint8_t> resp;
1198fee5e4c7SYong Li     if (index == 0)
1199fee5e4c7SYong Li     {
1200fee5e4c7SYong Li         NMDiscoveryRecord nm = {};
1201fee5e4c7SYong Li         nm.header.record_id_lsb = recordId;
1202fee5e4c7SYong Li         nm.header.record_id_msb = recordId >> 8;
1203fee5e4c7SYong Li         nm.header.sdr_version = ipmiSdrVersion;
1204fee5e4c7SYong Li         nm.header.record_type = 0xC0;
1205fee5e4c7SYong Li         nm.header.record_length = 0xB;
1206fee5e4c7SYong Li         nm.oemID0 = 0x57;
1207fee5e4c7SYong Li         nm.oemID1 = 0x1;
1208fee5e4c7SYong Li         nm.oemID2 = 0x0;
1209fee5e4c7SYong Li         nm.subType = 0x0D;
1210fee5e4c7SYong Li         nm.version = 0x1;
121180d4d5f9SMatt Simmering         nm.targetAddress = 0x2C;
1212fee5e4c7SYong Li         nm.channelNumber = 0x60;
1213fee5e4c7SYong Li         nm.healthEventSensor = 0x19;
1214fee5e4c7SYong Li         nm.exceptionEventSensor = 0x18;
1215fee5e4c7SYong Li         nm.operationalCapSensor = 0x1A;
1216fee5e4c7SYong Li         nm.thresholdExceededSensor = 0x1B;
1217fee5e4c7SYong Li 
1218fee5e4c7SYong Li         uint8_t* nmPtr = reinterpret_cast<uint8_t*>(&nm);
1219fee5e4c7SYong Li         resp.insert(resp.end(), nmPtr, nmPtr + sizeof(NMDiscoveryRecord));
1220fee5e4c7SYong Li     }
1221fee5e4c7SYong Li     else
1222fee5e4c7SYong Li     {
1223*1bcced08SPatrick Williams         throw std::runtime_error(
1224*1bcced08SPatrick Williams             "getNMDiscoverySDR:: Illegal index " + std::to_string(index));
1225fee5e4c7SYong Li     }
1226fee5e4c7SYong Li 
1227fee5e4c7SYong Li     return resp;
1228fee5e4c7SYong Li }
1229fee5e4c7SYong Li 
registerStorageFunctions()1230e2d1aee3SJason M. Bills void registerStorageFunctions()
1231e2d1aee3SJason M. Bills {
12322569025eSJames Feist     createTimers();
1233e4f710d7SJames Feist     startMatch();
1234e4f710d7SJames Feist 
1235e2d1aee3SJason M. Bills     // <Get FRU Inventory Area Info>
1236d33acd6bSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1237d33acd6bSjayaprakash Mutyala                           ipmi::storage::cmdGetFruInventoryAreaInfo,
1238d33acd6bSjayaprakash Mutyala                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1239c04e2e70SJason M. Bills     // <READ FRU Data>
12405f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12415f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdReadFruData, ipmi::Privilege::User,
12425f4194eeSjayaprakash Mutyala                           ipmiStorageReadFruData);
1243e2d1aee3SJason M. Bills 
1244c04e2e70SJason M. Bills     // <WRITE FRU Data>
12455f4194eeSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12465f4194eeSjayaprakash Mutyala                           ipmi::storage::cmdWriteFruData,
12475f4194eeSjayaprakash Mutyala                           ipmi::Privilege::Operator, ipmiStorageWriteFruData);
1248c04e2e70SJason M. Bills 
1249c04e2e70SJason M. Bills     // <Get SEL Info>
12501d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1251542498e9SJason M. Bills                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1252542498e9SJason M. Bills                           ipmiStorageGetSELInfo);
1253c04e2e70SJason M. Bills 
1254c04e2e70SJason M. Bills     // <Get SEL Entry>
12551d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1256542498e9SJason M. Bills                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1257542498e9SJason M. Bills                           ipmiStorageGetSELEntry);
1258c04e2e70SJason M. Bills 
1259c04e2e70SJason M. Bills     // <Add SEL Entry>
12606dd8f047SJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
126198bbf69aSVernon Mauery                           ipmi::storage::cmdAddSelEntry,
12626dd8f047SJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1263c04e2e70SJason M. Bills 
1264c04e2e70SJason M. Bills     // <Clear SEL>
12651d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12661d4d54ddSJason M. Bills                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
12671d4d54ddSJason M. Bills                           ipmiStorageClearSEL);
1268cac97a53SJason M. Bills 
12691a47462eSJason M. Bills     // <Get SEL Time>
12701a47462eSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1271542498e9SJason M. Bills                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1272542498e9SJason M. Bills                           ipmiStorageGetSELTime);
12731a47462eSJason M. Bills 
1274cac97a53SJason M. Bills     // <Set SEL Time>
12751d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
12761d4d54ddSJason M. Bills                           ipmi::storage::cmdSetSelTime,
12771d4d54ddSJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1278e2d1aee3SJason M. Bills }
12793f7c5e40SJason M. Bills } // namespace storage
12803f7c5e40SJason M. Bills } // namespace ipmi
1281