13f7c5e40SJason M. Bills /*
21a2fbddfSJason M. Bills // Copyright (c) 2017-2019 Intel Corporation
33f7c5e40SJason M. Bills //
43f7c5e40SJason M. Bills // Licensed under the Apache License, Version 2.0 (the "License");
53f7c5e40SJason M. Bills // you may not use this file except in compliance with the License.
63f7c5e40SJason M. Bills // You may obtain a copy of the License at
73f7c5e40SJason M. Bills //
83f7c5e40SJason M. Bills //      http://www.apache.org/licenses/LICENSE-2.0
93f7c5e40SJason M. Bills //
103f7c5e40SJason M. Bills // Unless required by applicable law or agreed to in writing, software
113f7c5e40SJason M. Bills // distributed under the License is distributed on an "AS IS" BASIS,
123f7c5e40SJason M. Bills // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f7c5e40SJason M. Bills // See the License for the specific language governing permissions and
143f7c5e40SJason M. Bills // limitations under the License.
153f7c5e40SJason M. Bills */
163f7c5e40SJason M. Bills 
171d4d54ddSJason M. Bills #include <boost/algorithm/string.hpp>
183f7c5e40SJason M. Bills #include <boost/container/flat_map.hpp>
19c04e2e70SJason M. Bills #include <boost/process.hpp>
203f7c5e40SJason M. Bills #include <commandutils.hpp>
211d4d54ddSJason M. Bills #include <filesystem>
223f7c5e40SJason M. Bills #include <iostream>
2399b78ec8SJason M. Bills #include <ipmi_to_redfish_hooks.hpp>
242a265d54SJames Feist #include <ipmid/api.hpp>
25c04e2e70SJason M. Bills #include <phosphor-ipmi-host/selutility.hpp>
263f7c5e40SJason M. Bills #include <phosphor-logging/log.hpp>
273f7c5e40SJason M. Bills #include <sdbusplus/message/types.hpp>
283f7c5e40SJason M. Bills #include <sdbusplus/timer.hpp>
29c04e2e70SJason M. Bills #include <sdrutils.hpp>
30c04e2e70SJason M. Bills #include <stdexcept>
313f7c5e40SJason M. Bills #include <storagecommands.hpp>
3252aaa7d5SJason M. Bills #include <string_view>
333f7c5e40SJason M. Bills 
341d4d54ddSJason M. Bills namespace intel_oem::ipmi::sel
351d4d54ddSJason M. Bills {
361d4d54ddSJason M. Bills static const std::filesystem::path selLogDir = "/var/log";
371d4d54ddSJason M. Bills static const std::string selLogFilename = "ipmi_sel";
381d4d54ddSJason M. Bills 
391d4d54ddSJason M. Bills static int getFileTimestamp(const std::filesystem::path& file)
401d4d54ddSJason M. Bills {
411d4d54ddSJason M. Bills     struct stat st;
421d4d54ddSJason M. Bills 
431d4d54ddSJason M. Bills     if (stat(file.c_str(), &st) >= 0)
441d4d54ddSJason M. Bills     {
451d4d54ddSJason M. Bills         return st.st_mtime;
461d4d54ddSJason M. Bills     }
471d4d54ddSJason M. Bills     return ::ipmi::sel::invalidTimeStamp;
481d4d54ddSJason M. Bills }
491d4d54ddSJason M. Bills 
501d4d54ddSJason M. Bills namespace erase_time
517944c307SJason M. Bills {
527944c307SJason M. Bills static constexpr const char* selEraseTimestamp = "/var/lib/ipmi/sel_erase_time";
537944c307SJason M. Bills 
547944c307SJason M. Bills void save()
557944c307SJason M. Bills {
567944c307SJason M. Bills     // open the file, creating it if necessary
577944c307SJason M. Bills     int fd = open(selEraseTimestamp, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
587944c307SJason M. Bills     if (fd < 0)
597944c307SJason M. Bills     {
607944c307SJason M. Bills         std::cerr << "Failed to open file\n";
617944c307SJason M. Bills         return;
627944c307SJason M. Bills     }
637944c307SJason M. Bills 
647944c307SJason M. Bills     // update the file timestamp to the current time
657944c307SJason M. Bills     if (futimens(fd, NULL) < 0)
667944c307SJason M. Bills     {
677944c307SJason M. Bills         std::cerr << "Failed to update timestamp: "
687944c307SJason M. Bills                   << std::string(strerror(errno));
697944c307SJason M. Bills     }
707944c307SJason M. Bills     close(fd);
717944c307SJason M. Bills }
727944c307SJason M. Bills 
737944c307SJason M. Bills int get()
747944c307SJason M. Bills {
751d4d54ddSJason M. Bills     return getFileTimestamp(selEraseTimestamp);
767944c307SJason M. Bills }
771d4d54ddSJason M. Bills } // namespace erase_time
781d4d54ddSJason M. Bills } // namespace intel_oem::ipmi::sel
797944c307SJason M. Bills 
803f7c5e40SJason M. Bills namespace ipmi
813f7c5e40SJason M. Bills {
823f7c5e40SJason M. Bills 
833f7c5e40SJason M. Bills namespace storage
843f7c5e40SJason M. Bills {
853f7c5e40SJason M. Bills 
86e2d1aee3SJason M. Bills constexpr static const size_t maxMessageSize = 64;
873f7c5e40SJason M. Bills constexpr static const size_t maxFruSdrNameSize = 16;
883f7c5e40SJason M. Bills using ManagedObjectType = boost::container::flat_map<
893f7c5e40SJason M. Bills     sdbusplus::message::object_path,
903f7c5e40SJason M. Bills     boost::container::flat_map<
913f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
923f7c5e40SJason M. Bills using ManagedEntry = std::pair<
933f7c5e40SJason M. Bills     sdbusplus::message::object_path,
943f7c5e40SJason M. Bills     boost::container::flat_map<
953f7c5e40SJason M. Bills         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
963f7c5e40SJason M. Bills 
973bcba457SJames Feist constexpr static const char* fruDeviceServiceName =
983bcba457SJames Feist     "xyz.openbmc_project.FruDevice";
99e2d1aee3SJason M. Bills constexpr static const size_t cacheTimeoutSeconds = 10;
1003f7c5e40SJason M. Bills 
1014ed6f2c1SJason M. Bills // event direction is bit[7] of eventType where 1b = Deassertion event
1024ed6f2c1SJason M. Bills constexpr static const uint8_t deassertionEvent = 0x80;
103c04e2e70SJason M. Bills 
1043f7c5e40SJason M. Bills static std::vector<uint8_t> fruCache;
1053f7c5e40SJason M. Bills static uint8_t cacheBus = 0xFF;
1063f7c5e40SJason M. Bills static uint8_t cacheAddr = 0XFF;
1073f7c5e40SJason M. Bills 
1083f7c5e40SJason M. Bills std::unique_ptr<phosphor::Timer> cacheTimer = nullptr;
1093f7c5e40SJason M. Bills 
1103f7c5e40SJason M. Bills // we unfortunately have to build a map of hashes in case there is a
1113f7c5e40SJason M. Bills // collision to verify our dev-id
1123f7c5e40SJason M. Bills boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
1133f7c5e40SJason M. Bills 
114e2d1aee3SJason M. Bills void registerStorageFunctions() __attribute__((constructor));
1153f7c5e40SJason M. Bills 
1163f7c5e40SJason M. Bills bool writeFru()
1173f7c5e40SJason M. Bills {
11815419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
11915419dd5SVernon Mauery     sdbusplus::message::message writeFru = dbus->new_method_call(
1203f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
1213f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "WriteFru");
1223f7c5e40SJason M. Bills     writeFru.append(cacheBus, cacheAddr, fruCache);
1233f7c5e40SJason M. Bills     try
1243f7c5e40SJason M. Bills     {
12515419dd5SVernon Mauery         sdbusplus::message::message writeFruResp = dbus->call(writeFru);
1263f7c5e40SJason M. Bills     }
1273f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
1283f7c5e40SJason M. Bills     {
1293f7c5e40SJason M. Bills         // todo: log sel?
1303f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
1313f7c5e40SJason M. Bills             "error writing fru");
1323f7c5e40SJason M. Bills         return false;
1333f7c5e40SJason M. Bills     }
1343f7c5e40SJason M. Bills     return true;
1353f7c5e40SJason M. Bills }
1363f7c5e40SJason M. Bills 
137e2d1aee3SJason M. Bills void createTimer()
138e2d1aee3SJason M. Bills {
139e2d1aee3SJason M. Bills     if (cacheTimer == nullptr)
140e2d1aee3SJason M. Bills     {
141e2d1aee3SJason M. Bills         cacheTimer = std::make_unique<phosphor::Timer>(writeFru);
142e2d1aee3SJason M. Bills     }
143e2d1aee3SJason M. Bills }
144e2d1aee3SJason M. Bills 
1453f7c5e40SJason M. Bills ipmi_ret_t replaceCacheFru(uint8_t devId)
1463f7c5e40SJason M. Bills {
1473f7c5e40SJason M. Bills     static uint8_t lastDevId = 0xFF;
1483f7c5e40SJason M. Bills 
1493f7c5e40SJason M. Bills     bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
1503f7c5e40SJason M. Bills     if (lastDevId == devId && timerRunning)
1513f7c5e40SJason M. Bills     {
1523f7c5e40SJason M. Bills         return IPMI_CC_OK; // cache already up to date
1533f7c5e40SJason M. Bills     }
1543f7c5e40SJason M. Bills     // if timer is running, stop it and writeFru manually
1553f7c5e40SJason M. Bills     else if (timerRunning)
1563f7c5e40SJason M. Bills     {
1573f7c5e40SJason M. Bills         cacheTimer->stop();
1583f7c5e40SJason M. Bills         writeFru();
1593f7c5e40SJason M. Bills     }
1603f7c5e40SJason M. Bills 
16115419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
16215419dd5SVernon Mauery     sdbusplus::message::message getObjects = dbus->new_method_call(
1633f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
1643f7c5e40SJason M. Bills         "GetManagedObjects");
1653f7c5e40SJason M. Bills     ManagedObjectType frus;
1663f7c5e40SJason M. Bills     try
1673f7c5e40SJason M. Bills     {
16815419dd5SVernon Mauery         sdbusplus::message::message resp = dbus->call(getObjects);
1693f7c5e40SJason M. Bills         resp.read(frus);
1703f7c5e40SJason M. Bills     }
1713f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
1723f7c5e40SJason M. Bills     {
1733f7c5e40SJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(
1743f7c5e40SJason M. Bills             "replaceCacheFru: error getting managed objects");
1753f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
1763f7c5e40SJason M. Bills     }
1773f7c5e40SJason M. Bills 
1783f7c5e40SJason M. Bills     deviceHashes.clear();
1793f7c5e40SJason M. Bills 
1803f7c5e40SJason M. Bills     // hash the object paths to create unique device id's. increment on
1813f7c5e40SJason M. Bills     // collision
1823f7c5e40SJason M. Bills     std::hash<std::string> hasher;
1833f7c5e40SJason M. Bills     for (const auto& fru : frus)
1843f7c5e40SJason M. Bills     {
1853f7c5e40SJason M. Bills         auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
1863f7c5e40SJason M. Bills         if (fruIface == fru.second.end())
1873f7c5e40SJason M. Bills         {
1883f7c5e40SJason M. Bills             continue;
1893f7c5e40SJason M. Bills         }
1903f7c5e40SJason M. Bills 
1913f7c5e40SJason M. Bills         auto busFind = fruIface->second.find("BUS");
1923f7c5e40SJason M. Bills         auto addrFind = fruIface->second.find("ADDRESS");
1933f7c5e40SJason M. Bills         if (busFind == fruIface->second.end() ||
1943f7c5e40SJason M. Bills             addrFind == fruIface->second.end())
1953f7c5e40SJason M. Bills         {
1963f7c5e40SJason M. Bills             phosphor::logging::log<phosphor::logging::level::INFO>(
1973f7c5e40SJason M. Bills                 "fru device missing Bus or Address",
1983f7c5e40SJason M. Bills                 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
1993f7c5e40SJason M. Bills             continue;
2003f7c5e40SJason M. Bills         }
2013f7c5e40SJason M. Bills 
2028166c8d7SVernon Mauery         uint8_t fruBus = std::get<uint32_t>(busFind->second);
2038166c8d7SVernon Mauery         uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
2043f7c5e40SJason M. Bills 
2053f7c5e40SJason M. Bills         uint8_t fruHash = 0;
2063f7c5e40SJason M. Bills         if (fruBus != 0 || fruAddr != 0)
2073f7c5e40SJason M. Bills         {
2083f7c5e40SJason M. Bills             fruHash = hasher(fru.first.str);
2093f7c5e40SJason M. Bills             // can't be 0xFF based on spec, and 0 is reserved for baseboard
2103f7c5e40SJason M. Bills             if (fruHash == 0 || fruHash == 0xFF)
2113f7c5e40SJason M. Bills             {
2123f7c5e40SJason M. Bills                 fruHash = 1;
2133f7c5e40SJason M. Bills             }
2143f7c5e40SJason M. Bills         }
2153f7c5e40SJason M. Bills         std::pair<uint8_t, uint8_t> newDev(fruBus, fruAddr);
2163f7c5e40SJason M. Bills 
2173f7c5e40SJason M. Bills         bool emplacePassed = false;
2183f7c5e40SJason M. Bills         while (!emplacePassed)
2193f7c5e40SJason M. Bills         {
2203f7c5e40SJason M. Bills             auto resp = deviceHashes.emplace(fruHash, newDev);
2213f7c5e40SJason M. Bills             emplacePassed = resp.second;
2223f7c5e40SJason M. Bills             if (!emplacePassed)
2233f7c5e40SJason M. Bills             {
2243f7c5e40SJason M. Bills                 fruHash++;
2253f7c5e40SJason M. Bills                 // can't be 0xFF based on spec, and 0 is reserved for
2263f7c5e40SJason M. Bills                 // baseboard
2273f7c5e40SJason M. Bills                 if (fruHash == 0XFF)
2283f7c5e40SJason M. Bills                 {
2293f7c5e40SJason M. Bills                     fruHash = 0x1;
2303f7c5e40SJason M. Bills                 }
2313f7c5e40SJason M. Bills             }
2323f7c5e40SJason M. Bills         }
2333f7c5e40SJason M. Bills     }
2343f7c5e40SJason M. Bills     auto deviceFind = deviceHashes.find(devId);
2353f7c5e40SJason M. Bills     if (deviceFind == deviceHashes.end())
2363f7c5e40SJason M. Bills     {
2373f7c5e40SJason M. Bills         return IPMI_CC_SENSOR_INVALID;
2383f7c5e40SJason M. Bills     }
2393f7c5e40SJason M. Bills 
2403f7c5e40SJason M. Bills     fruCache.clear();
24115419dd5SVernon Mauery     sdbusplus::message::message getRawFru = dbus->new_method_call(
2423f7c5e40SJason M. Bills         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
2433f7c5e40SJason M. Bills         "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
2443f7c5e40SJason M. Bills     cacheBus = deviceFind->second.first;
2453f7c5e40SJason M. Bills     cacheAddr = deviceFind->second.second;
2463f7c5e40SJason M. Bills     getRawFru.append(cacheBus, cacheAddr);
2473f7c5e40SJason M. Bills     try
2483f7c5e40SJason M. Bills     {
24915419dd5SVernon Mauery         sdbusplus::message::message getRawResp = dbus->call(getRawFru);
2503f7c5e40SJason M. Bills         getRawResp.read(fruCache);
2513f7c5e40SJason M. Bills     }
2523f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
2533f7c5e40SJason M. Bills     {
2543f7c5e40SJason M. Bills         lastDevId = 0xFF;
2553f7c5e40SJason M. Bills         cacheBus = 0xFF;
2563f7c5e40SJason M. Bills         cacheAddr = 0xFF;
2573f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
2583f7c5e40SJason M. Bills     }
2593f7c5e40SJason M. Bills 
2603f7c5e40SJason M. Bills     lastDevId = devId;
2613f7c5e40SJason M. Bills     return IPMI_CC_OK;
2623f7c5e40SJason M. Bills }
2633f7c5e40SJason M. Bills 
264e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageReadFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
265e2d1aee3SJason M. Bills                                   ipmi_request_t request,
266e2d1aee3SJason M. Bills                                   ipmi_response_t response,
267e2d1aee3SJason M. Bills                                   ipmi_data_len_t dataLen,
268e2d1aee3SJason M. Bills                                   ipmi_context_t context)
269e2d1aee3SJason M. Bills {
270e2d1aee3SJason M. Bills     if (*dataLen != 4)
271e2d1aee3SJason M. Bills     {
272e2d1aee3SJason M. Bills         *dataLen = 0;
273e2d1aee3SJason M. Bills         return IPMI_CC_REQ_DATA_LEN_INVALID;
274e2d1aee3SJason M. Bills     }
275e2d1aee3SJason M. Bills     *dataLen = 0; // default to 0 in case of an error
276e2d1aee3SJason M. Bills 
277e2d1aee3SJason M. Bills     auto req = static_cast<GetFRUAreaReq*>(request);
278e2d1aee3SJason M. Bills 
279e2d1aee3SJason M. Bills     if (req->countToRead > maxMessageSize - 1)
280e2d1aee3SJason M. Bills     {
281e2d1aee3SJason M. Bills         return IPMI_CC_INVALID_FIELD_REQUEST;
282e2d1aee3SJason M. Bills     }
283e2d1aee3SJason M. Bills     ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
284e2d1aee3SJason M. Bills 
285e2d1aee3SJason M. Bills     if (status != IPMI_CC_OK)
286e2d1aee3SJason M. Bills     {
287e2d1aee3SJason M. Bills         return status;
288e2d1aee3SJason M. Bills     }
289e2d1aee3SJason M. Bills 
290e2d1aee3SJason M. Bills     size_t fromFRUByteLen = 0;
291e2d1aee3SJason M. Bills     if (req->countToRead + req->fruInventoryOffset < fruCache.size())
292e2d1aee3SJason M. Bills     {
293e2d1aee3SJason M. Bills         fromFRUByteLen = req->countToRead;
294e2d1aee3SJason M. Bills     }
295e2d1aee3SJason M. Bills     else if (fruCache.size() > req->fruInventoryOffset)
296e2d1aee3SJason M. Bills     {
297e2d1aee3SJason M. Bills         fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
298e2d1aee3SJason M. Bills     }
299e2d1aee3SJason M. Bills     size_t padByteLen = req->countToRead - fromFRUByteLen;
300e2d1aee3SJason M. Bills     uint8_t* respPtr = static_cast<uint8_t*>(response);
301e2d1aee3SJason M. Bills     *respPtr = req->countToRead;
302e2d1aee3SJason M. Bills     std::copy(fruCache.begin() + req->fruInventoryOffset,
303e2d1aee3SJason M. Bills               fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
304e2d1aee3SJason M. Bills               ++respPtr);
305e2d1aee3SJason M. Bills     // if longer than the fru is requested, fill with 0xFF
306e2d1aee3SJason M. Bills     if (padByteLen)
307e2d1aee3SJason M. Bills     {
308e2d1aee3SJason M. Bills         respPtr += fromFRUByteLen;
309e2d1aee3SJason M. Bills         std::fill(respPtr, respPtr + padByteLen, 0xFF);
310e2d1aee3SJason M. Bills     }
311e2d1aee3SJason M. Bills     *dataLen = fromFRUByteLen + 1;
312e2d1aee3SJason M. Bills 
313e2d1aee3SJason M. Bills     return IPMI_CC_OK;
314e2d1aee3SJason M. Bills }
315e2d1aee3SJason M. Bills 
316e2d1aee3SJason M. Bills ipmi_ret_t ipmiStorageWriteFRUData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
317e2d1aee3SJason M. Bills                                    ipmi_request_t request,
318e2d1aee3SJason M. Bills                                    ipmi_response_t response,
319e2d1aee3SJason M. Bills                                    ipmi_data_len_t dataLen,
320e2d1aee3SJason M. Bills                                    ipmi_context_t context)
321e2d1aee3SJason M. Bills {
322e2d1aee3SJason M. Bills     if (*dataLen < 4 ||
323e2d1aee3SJason M. Bills         *dataLen >=
324e2d1aee3SJason M. Bills             0xFF + 3) // count written return is one byte, so limit to one byte
325e2d1aee3SJason M. Bills                       // of data after the three request data bytes
326e2d1aee3SJason M. Bills     {
327e2d1aee3SJason M. Bills         *dataLen = 0;
328e2d1aee3SJason M. Bills         return IPMI_CC_REQ_DATA_LEN_INVALID;
329e2d1aee3SJason M. Bills     }
330e2d1aee3SJason M. Bills 
331e2d1aee3SJason M. Bills     auto req = static_cast<WriteFRUDataReq*>(request);
332e2d1aee3SJason M. Bills     size_t writeLen = *dataLen - 3;
333e2d1aee3SJason M. Bills     *dataLen = 0; // default to 0 in case of an error
334e2d1aee3SJason M. Bills 
335e2d1aee3SJason M. Bills     ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
336e2d1aee3SJason M. Bills     if (status != IPMI_CC_OK)
337e2d1aee3SJason M. Bills     {
338e2d1aee3SJason M. Bills         return status;
339e2d1aee3SJason M. Bills     }
340e2d1aee3SJason M. Bills     int lastWriteAddr = req->fruInventoryOffset + writeLen;
341e2d1aee3SJason M. Bills     if (fruCache.size() < lastWriteAddr)
342e2d1aee3SJason M. Bills     {
343e2d1aee3SJason M. Bills         fruCache.resize(req->fruInventoryOffset + writeLen);
344e2d1aee3SJason M. Bills     }
345e2d1aee3SJason M. Bills 
346e2d1aee3SJason M. Bills     std::copy(req->data, req->data + writeLen,
347e2d1aee3SJason M. Bills               fruCache.begin() + req->fruInventoryOffset);
348e2d1aee3SJason M. Bills 
349e2d1aee3SJason M. Bills     bool atEnd = false;
350e2d1aee3SJason M. Bills 
351e2d1aee3SJason M. Bills     if (fruCache.size() >= sizeof(FRUHeader))
352e2d1aee3SJason M. Bills     {
353e2d1aee3SJason M. Bills 
354e2d1aee3SJason M. Bills         FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
355e2d1aee3SJason M. Bills 
356e2d1aee3SJason M. Bills         int lastRecordStart = std::max(
357e2d1aee3SJason M. Bills             header->internalOffset,
358e2d1aee3SJason M. Bills             std::max(header->chassisOffset,
359e2d1aee3SJason M. Bills                      std::max(header->boardOffset, header->productOffset)));
360e2d1aee3SJason M. Bills         // TODO: Handle Multi-Record FRUs?
361e2d1aee3SJason M. Bills 
362e2d1aee3SJason M. Bills         lastRecordStart *= 8; // header starts in are multiples of 8 bytes
363e2d1aee3SJason M. Bills 
364e2d1aee3SJason M. Bills         // get the length of the area in multiples of 8 bytes
365e2d1aee3SJason M. Bills         if (lastWriteAddr > (lastRecordStart + 1))
366e2d1aee3SJason M. Bills         {
367e2d1aee3SJason M. Bills             // second byte in record area is the length
368e2d1aee3SJason M. Bills             int areaLength(fruCache[lastRecordStart + 1]);
369e2d1aee3SJason M. Bills             areaLength *= 8; // it is in multiples of 8 bytes
370e2d1aee3SJason M. Bills 
371e2d1aee3SJason M. Bills             if (lastWriteAddr >= (areaLength + lastRecordStart))
372e2d1aee3SJason M. Bills             {
373e2d1aee3SJason M. Bills                 atEnd = true;
374e2d1aee3SJason M. Bills             }
375e2d1aee3SJason M. Bills         }
376e2d1aee3SJason M. Bills     }
377e2d1aee3SJason M. Bills     uint8_t* respPtr = static_cast<uint8_t*>(response);
378e2d1aee3SJason M. Bills     if (atEnd)
379e2d1aee3SJason M. Bills     {
380e2d1aee3SJason M. Bills         // cancel timer, we're at the end so might as well send it
381e2d1aee3SJason M. Bills         cacheTimer->stop();
382e2d1aee3SJason M. Bills         if (!writeFru())
383e2d1aee3SJason M. Bills         {
384e2d1aee3SJason M. Bills             return IPMI_CC_INVALID_FIELD_REQUEST;
385e2d1aee3SJason M. Bills         }
386e2d1aee3SJason M. Bills         *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
387e2d1aee3SJason M. Bills     }
388e2d1aee3SJason M. Bills     else
389e2d1aee3SJason M. Bills     {
390e2d1aee3SJason M. Bills         // start a timer, if no further data is sent in cacheTimeoutSeconds
391e2d1aee3SJason M. Bills         // seconds, check to see if it is valid
392e2d1aee3SJason M. Bills         createTimer();
393e2d1aee3SJason M. Bills         cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
394e2d1aee3SJason M. Bills             std::chrono::seconds(cacheTimeoutSeconds)));
395e2d1aee3SJason M. Bills         *respPtr = 0;
396e2d1aee3SJason M. Bills     }
397e2d1aee3SJason M. Bills 
398e2d1aee3SJason M. Bills     *dataLen = 1;
399e2d1aee3SJason M. Bills 
400e2d1aee3SJason M. Bills     return IPMI_CC_OK;
401e2d1aee3SJason M. Bills }
402e2d1aee3SJason M. Bills 
403d33acd6bSjayaprakash Mutyala /** @brief implements the get FRU inventory area info command
404d33acd6bSjayaprakash Mutyala  *  @param fruDeviceId  - FRU Device ID
405d33acd6bSjayaprakash Mutyala  *
406d33acd6bSjayaprakash Mutyala  *  @returns IPMI completion code plus response data
407d33acd6bSjayaprakash Mutyala  *   - inventorySize - Number of possible allocation units
408d33acd6bSjayaprakash Mutyala  *   - accessType    - Allocation unit size in bytes.
409d33acd6bSjayaprakash Mutyala  */
410d33acd6bSjayaprakash Mutyala ipmi::RspType<uint16_t, // inventorySize
411d33acd6bSjayaprakash Mutyala               uint8_t>  // accessType
412d33acd6bSjayaprakash Mutyala     ipmiStorageGetFruInvAreaInfo(uint8_t fruDeviceId)
413e2d1aee3SJason M. Bills {
414d33acd6bSjayaprakash Mutyala     if (fruDeviceId == 0xFF)
415e2d1aee3SJason M. Bills     {
416d33acd6bSjayaprakash Mutyala         return ipmi::responseInvalidFieldRequest();
417e2d1aee3SJason M. Bills     }
418e2d1aee3SJason M. Bills 
419d33acd6bSjayaprakash Mutyala     ipmi::Cc status = replaceCacheFru(fruDeviceId);
420e2d1aee3SJason M. Bills 
421e2d1aee3SJason M. Bills     if (status != IPMI_CC_OK)
422e2d1aee3SJason M. Bills     {
423d33acd6bSjayaprakash Mutyala         return ipmi::response(status);
424e2d1aee3SJason M. Bills     }
425e2d1aee3SJason M. Bills 
426d33acd6bSjayaprakash Mutyala     constexpr uint8_t accessType =
427d33acd6bSjayaprakash Mutyala         static_cast<uint8_t>(GetFRUAreaAccessType::byte);
428e2d1aee3SJason M. Bills 
429d33acd6bSjayaprakash Mutyala     return ipmi::responseSuccess(fruCache.size(), accessType);
430e2d1aee3SJason M. Bills }
431e2d1aee3SJason M. Bills 
4323f7c5e40SJason M. Bills ipmi_ret_t getFruSdrCount(size_t& count)
4333f7c5e40SJason M. Bills {
4343f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0);
4353f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
4363f7c5e40SJason M. Bills     {
4373f7c5e40SJason M. Bills         return ret;
4383f7c5e40SJason M. Bills     }
4393f7c5e40SJason M. Bills     count = deviceHashes.size();
4403f7c5e40SJason M. Bills     return IPMI_CC_OK;
4413f7c5e40SJason M. Bills }
4423f7c5e40SJason M. Bills 
4433f7c5e40SJason M. Bills ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
4443f7c5e40SJason M. Bills {
4453f7c5e40SJason M. Bills     ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
4463f7c5e40SJason M. Bills     if (ret != IPMI_CC_OK)
4473f7c5e40SJason M. Bills     {
4483f7c5e40SJason M. Bills         return ret;
4493f7c5e40SJason M. Bills     }
4503f7c5e40SJason M. Bills     if (deviceHashes.size() < index)
4513f7c5e40SJason M. Bills     {
4523f7c5e40SJason M. Bills         return IPMI_CC_INVALID_FIELD_REQUEST;
4533f7c5e40SJason M. Bills     }
4543f7c5e40SJason M. Bills     auto device = deviceHashes.begin() + index;
4553f7c5e40SJason M. Bills     uint8_t& bus = device->second.first;
4563f7c5e40SJason M. Bills     uint8_t& address = device->second.second;
4573f7c5e40SJason M. Bills 
4583f7c5e40SJason M. Bills     ManagedObjectType frus;
4593f7c5e40SJason M. Bills 
46015419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
46115419dd5SVernon Mauery     sdbusplus::message::message getObjects = dbus->new_method_call(
4623f7c5e40SJason M. Bills         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
4633f7c5e40SJason M. Bills         "GetManagedObjects");
4643f7c5e40SJason M. Bills     try
4653f7c5e40SJason M. Bills     {
46615419dd5SVernon Mauery         sdbusplus::message::message resp = dbus->call(getObjects);
4673f7c5e40SJason M. Bills         resp.read(frus);
4683f7c5e40SJason M. Bills     }
4693f7c5e40SJason M. Bills     catch (sdbusplus::exception_t&)
4703f7c5e40SJason M. Bills     {
4713f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
4723f7c5e40SJason M. Bills     }
4733f7c5e40SJason M. Bills     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
4743f7c5e40SJason M. Bills     auto fru =
4753f7c5e40SJason M. Bills         std::find_if(frus.begin(), frus.end(),
4763f7c5e40SJason M. Bills                      [bus, address, &fruData](ManagedEntry& entry) {
4773f7c5e40SJason M. Bills                          auto findFruDevice =
4783f7c5e40SJason M. Bills                              entry.second.find("xyz.openbmc_project.FruDevice");
4793f7c5e40SJason M. Bills                          if (findFruDevice == entry.second.end())
4803f7c5e40SJason M. Bills                          {
4813f7c5e40SJason M. Bills                              return false;
4823f7c5e40SJason M. Bills                          }
4833f7c5e40SJason M. Bills                          fruData = &(findFruDevice->second);
4843f7c5e40SJason M. Bills                          auto findBus = findFruDevice->second.find("BUS");
4853f7c5e40SJason M. Bills                          auto findAddress =
4863f7c5e40SJason M. Bills                              findFruDevice->second.find("ADDRESS");
4873f7c5e40SJason M. Bills                          if (findBus == findFruDevice->second.end() ||
4883f7c5e40SJason M. Bills                              findAddress == findFruDevice->second.end())
4893f7c5e40SJason M. Bills                          {
4903f7c5e40SJason M. Bills                              return false;
4913f7c5e40SJason M. Bills                          }
4928166c8d7SVernon Mauery                          if (std::get<uint32_t>(findBus->second) != bus)
4933f7c5e40SJason M. Bills                          {
4943f7c5e40SJason M. Bills                              return false;
4953f7c5e40SJason M. Bills                          }
4968166c8d7SVernon Mauery                          if (std::get<uint32_t>(findAddress->second) != address)
4973f7c5e40SJason M. Bills                          {
4983f7c5e40SJason M. Bills                              return false;
4993f7c5e40SJason M. Bills                          }
5003f7c5e40SJason M. Bills                          return true;
5013f7c5e40SJason M. Bills                      });
5023f7c5e40SJason M. Bills     if (fru == frus.end())
5033f7c5e40SJason M. Bills     {
5043f7c5e40SJason M. Bills         return IPMI_CC_RESPONSE_ERROR;
5053f7c5e40SJason M. Bills     }
5063f7c5e40SJason M. Bills     std::string name;
5073f7c5e40SJason M. Bills     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
5083f7c5e40SJason M. Bills     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
5093f7c5e40SJason M. Bills     if (findProductName != fruData->end())
5103f7c5e40SJason M. Bills     {
5118166c8d7SVernon Mauery         name = std::get<std::string>(findProductName->second);
5123f7c5e40SJason M. Bills     }
5133f7c5e40SJason M. Bills     else if (findBoardName != fruData->end())
5143f7c5e40SJason M. Bills     {
5158166c8d7SVernon Mauery         name = std::get<std::string>(findBoardName->second);
5163f7c5e40SJason M. Bills     }
5173f7c5e40SJason M. Bills     else
5183f7c5e40SJason M. Bills     {
5193f7c5e40SJason M. Bills         name = "UNKNOWN";
5203f7c5e40SJason M. Bills     }
5213f7c5e40SJason M. Bills     if (name.size() > maxFruSdrNameSize)
5223f7c5e40SJason M. Bills     {
5233f7c5e40SJason M. Bills         name = name.substr(0, maxFruSdrNameSize);
5243f7c5e40SJason M. Bills     }
5253f7c5e40SJason M. Bills     size_t sizeDiff = maxFruSdrNameSize - name.size();
5263f7c5e40SJason M. Bills 
5273f7c5e40SJason M. Bills     resp.header.record_id_lsb = 0x0; // calling code is to implement these
5283f7c5e40SJason M. Bills     resp.header.record_id_msb = 0x0;
5293f7c5e40SJason M. Bills     resp.header.sdr_version = ipmiSdrVersion;
5303f7c5e40SJason M. Bills     resp.header.record_type = 0x11; // FRU Device Locator
5313f7c5e40SJason M. Bills     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
5323f7c5e40SJason M. Bills     resp.key.deviceAddress = 0x20;
5333f7c5e40SJason M. Bills     resp.key.fruID = device->first;
5343f7c5e40SJason M. Bills     resp.key.accessLun = 0x80; // logical / physical fru device
5353f7c5e40SJason M. Bills     resp.key.channelNumber = 0x0;
5363f7c5e40SJason M. Bills     resp.body.reserved = 0x0;
5373f7c5e40SJason M. Bills     resp.body.deviceType = 0x10;
5384f86d1f2SJames Feist     resp.body.deviceTypeModifier = 0x0;
5393f7c5e40SJason M. Bills     resp.body.entityID = 0x0;
5403f7c5e40SJason M. Bills     resp.body.entityInstance = 0x1;
5413f7c5e40SJason M. Bills     resp.body.oem = 0x0;
5423f7c5e40SJason M. Bills     resp.body.deviceIDLen = name.size();
5433f7c5e40SJason M. Bills     name.copy(resp.body.deviceID, name.size());
5443f7c5e40SJason M. Bills 
5453f7c5e40SJason M. Bills     return IPMI_CC_OK;
5463f7c5e40SJason M. Bills }
547e2d1aee3SJason M. Bills 
5481d4d54ddSJason M. Bills static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
549c04e2e70SJason M. Bills {
5501d4d54ddSJason M. Bills     // Loop through the directory looking for ipmi_sel log files
5511d4d54ddSJason M. Bills     for (const std::filesystem::directory_entry& dirEnt :
5521d4d54ddSJason M. Bills          std::filesystem::directory_iterator(intel_oem::ipmi::sel::selLogDir))
553c04e2e70SJason M. Bills     {
5541d4d54ddSJason M. Bills         std::string filename = dirEnt.path().filename();
5551d4d54ddSJason M. Bills         if (boost::starts_with(filename, intel_oem::ipmi::sel::selLogFilename))
5561d4d54ddSJason M. Bills         {
5571d4d54ddSJason M. Bills             // If we find an ipmi_sel log file, save the path
5581d4d54ddSJason M. Bills             selLogFiles.emplace_back(intel_oem::ipmi::sel::selLogDir /
5591d4d54ddSJason M. Bills                                      filename);
560c04e2e70SJason M. Bills         }
561c04e2e70SJason M. Bills     }
5621d4d54ddSJason M. Bills     // As the log files rotate, they are appended with a ".#" that is higher for
5631d4d54ddSJason M. Bills     // the older logs. Since we don't expect more than 10 log files, we
5641d4d54ddSJason M. Bills     // can just sort the list to get them in order from newest to oldest
5651d4d54ddSJason M. Bills     std::sort(selLogFiles.begin(), selLogFiles.end());
566c04e2e70SJason M. Bills 
5671d4d54ddSJason M. Bills     return !selLogFiles.empty();
568c04e2e70SJason M. Bills }
569c04e2e70SJason M. Bills 
5701d4d54ddSJason M. Bills static int countSELEntries()
5711d4d54ddSJason M. Bills {
5721d4d54ddSJason M. Bills     // Get the list of ipmi_sel log files
5731d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
5741d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
5751d4d54ddSJason M. Bills     {
5761d4d54ddSJason M. Bills         return 0;
5771d4d54ddSJason M. Bills     }
5781d4d54ddSJason M. Bills     int numSELEntries = 0;
5791d4d54ddSJason M. Bills     // Loop through each log file and count the number of logs
5801d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
5811d4d54ddSJason M. Bills     {
5821d4d54ddSJason M. Bills         std::ifstream logStream(file);
5831d4d54ddSJason M. Bills         if (!logStream.is_open())
5841d4d54ddSJason M. Bills         {
5851d4d54ddSJason M. Bills             continue;
5861d4d54ddSJason M. Bills         }
5871d4d54ddSJason M. Bills 
5881d4d54ddSJason M. Bills         std::string line;
5891d4d54ddSJason M. Bills         while (std::getline(logStream, line))
5901d4d54ddSJason M. Bills         {
5911d4d54ddSJason M. Bills             numSELEntries++;
5921d4d54ddSJason M. Bills         }
5931d4d54ddSJason M. Bills     }
5941d4d54ddSJason M. Bills     return numSELEntries;
5951d4d54ddSJason M. Bills }
5961d4d54ddSJason M. Bills 
5971d4d54ddSJason M. Bills static bool findSELEntry(const int recordID,
5981d4d54ddSJason M. Bills                          const std::vector<std::filesystem::path> selLogFiles,
5991d4d54ddSJason M. Bills                          std::string& entry)
6001d4d54ddSJason M. Bills {
6011d4d54ddSJason M. Bills     // Record ID is the first entry field following the timestamp. It is
6021d4d54ddSJason M. Bills     // preceded by a space and followed by a comma
6031d4d54ddSJason M. Bills     std::string search = " " + std::to_string(recordID) + ",";
6041d4d54ddSJason M. Bills 
6051d4d54ddSJason M. Bills     // Loop through the ipmi_sel log entries
6061d4d54ddSJason M. Bills     for (const std::filesystem::path& file : selLogFiles)
6071d4d54ddSJason M. Bills     {
6081d4d54ddSJason M. Bills         std::ifstream logStream(file);
6091d4d54ddSJason M. Bills         if (!logStream.is_open())
6101d4d54ddSJason M. Bills         {
6111d4d54ddSJason M. Bills             continue;
6121d4d54ddSJason M. Bills         }
6131d4d54ddSJason M. Bills 
6141d4d54ddSJason M. Bills         while (std::getline(logStream, entry))
6151d4d54ddSJason M. Bills         {
6161d4d54ddSJason M. Bills             // Check if the record ID matches
6171d4d54ddSJason M. Bills             if (entry.find(search) != std::string::npos)
6181d4d54ddSJason M. Bills             {
6191d4d54ddSJason M. Bills                 return true;
6201d4d54ddSJason M. Bills             }
6211d4d54ddSJason M. Bills         }
6221d4d54ddSJason M. Bills     }
6231d4d54ddSJason M. Bills     return false;
6241d4d54ddSJason M. Bills }
6251d4d54ddSJason M. Bills 
6261d4d54ddSJason M. Bills static uint16_t
6271d4d54ddSJason M. Bills     getNextRecordID(const uint16_t recordID,
6281d4d54ddSJason M. Bills                     const std::vector<std::filesystem::path> selLogFiles)
6291d4d54ddSJason M. Bills {
6301d4d54ddSJason M. Bills     uint16_t nextRecordID = recordID + 1;
6311d4d54ddSJason M. Bills     std::string entry;
6321d4d54ddSJason M. Bills     if (findSELEntry(nextRecordID, selLogFiles, entry))
6331d4d54ddSJason M. Bills     {
6341d4d54ddSJason M. Bills         return nextRecordID;
6351d4d54ddSJason M. Bills     }
6361d4d54ddSJason M. Bills     else
6371d4d54ddSJason M. Bills     {
6381d4d54ddSJason M. Bills         return ipmi::sel::lastEntry;
6391d4d54ddSJason M. Bills     }
640c04e2e70SJason M. Bills }
641c04e2e70SJason M. Bills 
642c04e2e70SJason M. Bills static int fromHexStr(const std::string hexStr, std::vector<uint8_t>& data)
643c04e2e70SJason M. Bills {
644c04e2e70SJason M. Bills     for (unsigned int i = 0; i < hexStr.size(); i += 2)
645c04e2e70SJason M. Bills     {
646c04e2e70SJason M. Bills         try
647c04e2e70SJason M. Bills         {
648c04e2e70SJason M. Bills             data.push_back(static_cast<uint8_t>(
649c04e2e70SJason M. Bills                 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
650c04e2e70SJason M. Bills         }
651c04e2e70SJason M. Bills         catch (std::invalid_argument& e)
652c04e2e70SJason M. Bills         {
653c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
654c04e2e70SJason M. Bills             return -1;
655c04e2e70SJason M. Bills         }
656c04e2e70SJason M. Bills         catch (std::out_of_range& e)
657c04e2e70SJason M. Bills         {
658c04e2e70SJason M. Bills             phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
659c04e2e70SJason M. Bills             return -1;
660c04e2e70SJason M. Bills         }
661c04e2e70SJason M. Bills     }
662c04e2e70SJason M. Bills     return 0;
663c04e2e70SJason M. Bills }
664c04e2e70SJason M. Bills 
6651d4d54ddSJason M. Bills ipmi::RspType<uint8_t,  // SEL version
6661d4d54ddSJason M. Bills               uint16_t, // SEL entry count
6671d4d54ddSJason M. Bills               uint16_t, // free space
6681d4d54ddSJason M. Bills               uint32_t, // last add timestamp
6691d4d54ddSJason M. Bills               uint32_t, // last erase timestamp
6701d4d54ddSJason M. Bills               uint8_t>  // operation support
6711d4d54ddSJason M. Bills     ipmiStorageGetSELInfo()
672c04e2e70SJason M. Bills {
6731d4d54ddSJason M. Bills     constexpr uint8_t selVersion = ipmi::sel::selVersion;
6741d4d54ddSJason M. Bills     uint16_t entries = countSELEntries();
6751d4d54ddSJason M. Bills     uint32_t addTimeStamp = intel_oem::ipmi::sel::getFileTimestamp(
6761d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selLogDir / intel_oem::ipmi::sel::selLogFilename);
6771d4d54ddSJason M. Bills     uint32_t eraseTimeStamp = intel_oem::ipmi::sel::erase_time::get();
6781d4d54ddSJason M. Bills     constexpr uint8_t operationSupport =
6791d4d54ddSJason M. Bills         intel_oem::ipmi::sel::selOperationSupport;
6801d4d54ddSJason M. Bills     constexpr uint16_t freeSpace =
6811d4d54ddSJason M. Bills         0xffff; // Spec indicates that more than 64kB is free
682c04e2e70SJason M. Bills 
6831d4d54ddSJason M. Bills     return ipmi::responseSuccess(selVersion, entries, freeSpace, addTimeStamp,
6841d4d54ddSJason M. Bills                                  eraseTimeStamp, operationSupport);
685c04e2e70SJason M. Bills }
686c04e2e70SJason M. Bills 
6871d4d54ddSJason M. Bills using systemEventType = std::tuple<
6881d4d54ddSJason M. Bills     uint32_t, // Timestamp
6891d4d54ddSJason M. Bills     uint16_t, // Generator ID
6901d4d54ddSJason M. Bills     uint8_t,  // EvM Rev
6911d4d54ddSJason M. Bills     uint8_t,  // Sensor Type
6921d4d54ddSJason M. Bills     uint8_t,  // Sensor Number
6931d4d54ddSJason M. Bills     uint7_t,  // Event Type
6941d4d54ddSJason M. Bills     bool,     // Event Direction
6951d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize>>; // Event Data
6961d4d54ddSJason M. Bills using oemTsEventType = std::tuple<
6971d4d54ddSJason M. Bills     uint32_t,                                                   // Timestamp
6981d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize>>; // Event Data
6991d4d54ddSJason M. Bills using oemEventType =
7001d4d54ddSJason M. Bills     std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize>; // Event Data
7011d4d54ddSJason M. Bills 
7021d4d54ddSJason M. Bills ipmi::RspType<uint16_t, // Next Record ID
7031d4d54ddSJason M. Bills               uint16_t, // Record ID
7041d4d54ddSJason M. Bills               uint8_t,  // Record Type
7051d4d54ddSJason M. Bills               std::variant<systemEventType, oemTsEventType,
7061d4d54ddSJason M. Bills                            oemEventType>> // Record Content
7071d4d54ddSJason M. Bills     ipmiStorageGetSELEntry(uint16_t reservationID, uint16_t targetID,
7081d4d54ddSJason M. Bills                            uint8_t offset, uint8_t size)
709c04e2e70SJason M. Bills {
7101d4d54ddSJason M. Bills     // Only support getting the entire SEL record. If a partial size or non-zero
7111d4d54ddSJason M. Bills     // offset is requested, return an error
7121d4d54ddSJason M. Bills     if (offset != 0 || size != ipmi::sel::entireRecord)
713c04e2e70SJason M. Bills     {
7141d4d54ddSJason M. Bills         return ipmi::responseRetBytesUnavailable();
715c04e2e70SJason M. Bills     }
716c04e2e70SJason M. Bills 
7171d4d54ddSJason M. Bills     // Check the reservation ID if one is provided or required (only if the
7181d4d54ddSJason M. Bills     // offset is non-zero)
7191d4d54ddSJason M. Bills     if (reservationID != 0 || offset != 0)
720c04e2e70SJason M. Bills     {
7211d4d54ddSJason M. Bills         if (!checkSELReservation(reservationID))
722c04e2e70SJason M. Bills         {
7231d4d54ddSJason M. Bills             return ipmi::responseInvalidReservationId();
724c04e2e70SJason M. Bills         }
725c04e2e70SJason M. Bills     }
726c04e2e70SJason M. Bills 
7271d4d54ddSJason M. Bills     // Get the ipmi_sel log files
7281d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
7291d4d54ddSJason M. Bills     if (!getSELLogFiles(selLogFiles))
730c04e2e70SJason M. Bills     {
7311d4d54ddSJason M. Bills         return ipmi::responseSensorInvalid();
732c04e2e70SJason M. Bills     }
733c04e2e70SJason M. Bills 
7341d4d54ddSJason M. Bills     std::string targetEntry;
735c04e2e70SJason M. Bills 
736c04e2e70SJason M. Bills     if (targetID == ipmi::sel::firstEntry)
737c04e2e70SJason M. Bills     {
7381d4d54ddSJason M. Bills         // The first entry will be at the top of the oldest log file
7391d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.back());
7401d4d54ddSJason M. Bills         if (!logStream.is_open())
741c04e2e70SJason M. Bills         {
7421d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
743c04e2e70SJason M. Bills         }
7441d4d54ddSJason M. Bills 
7451d4d54ddSJason M. Bills         if (!std::getline(logStream, targetEntry))
7461d4d54ddSJason M. Bills         {
7471d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
748c04e2e70SJason M. Bills         }
749c04e2e70SJason M. Bills     }
750c04e2e70SJason M. Bills     else if (targetID == ipmi::sel::lastEntry)
751c04e2e70SJason M. Bills     {
7521d4d54ddSJason M. Bills         // The last entry will be at the bottom of the newest log file
7531d4d54ddSJason M. Bills         std::ifstream logStream(selLogFiles.front());
7541d4d54ddSJason M. Bills         if (!logStream.is_open())
755c04e2e70SJason M. Bills         {
7561d4d54ddSJason M. Bills             return ipmi::responseUnspecifiedError();
757c04e2e70SJason M. Bills         }
758c04e2e70SJason M. Bills 
7591d4d54ddSJason M. Bills         std::string line;
7601d4d54ddSJason M. Bills         while (std::getline(logStream, line))
761c04e2e70SJason M. Bills         {
7621d4d54ddSJason M. Bills             targetEntry = line;
763c04e2e70SJason M. Bills         }
764c04e2e70SJason M. Bills     }
765c04e2e70SJason M. Bills     else
766c04e2e70SJason M. Bills     {
7671d4d54ddSJason M. Bills         if (!findSELEntry(targetID, selLogFiles, targetEntry))
768c04e2e70SJason M. Bills         {
7691d4d54ddSJason M. Bills             return ipmi::responseSensorInvalid();
7701d4d54ddSJason M. Bills         }
771c04e2e70SJason M. Bills     }
772c04e2e70SJason M. Bills 
77352aaa7d5SJason M. Bills     // The format of the ipmi_sel message is "<Timestamp>
77452aaa7d5SJason M. Bills     // <ID>,<Type>,<EventData>,[<Generator ID>,<Path>,<Direction>]".
77552aaa7d5SJason M. Bills     // First get the Timestamp
77652aaa7d5SJason M. Bills     size_t space = targetEntry.find_first_of(" ");
77752aaa7d5SJason M. Bills     if (space == std::string::npos)
77852aaa7d5SJason M. Bills     {
77952aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
78052aaa7d5SJason M. Bills     }
78152aaa7d5SJason M. Bills     std::string entryTimestamp = targetEntry.substr(0, space);
78252aaa7d5SJason M. Bills     // Then get the log contents
78352aaa7d5SJason M. Bills     size_t entryStart = targetEntry.find_first_not_of(" ", space);
78452aaa7d5SJason M. Bills     if (entryStart == std::string::npos)
78552aaa7d5SJason M. Bills     {
78652aaa7d5SJason M. Bills         return ipmi::responseUnspecifiedError();
78752aaa7d5SJason M. Bills     }
78852aaa7d5SJason M. Bills     std::string_view entry(targetEntry);
78952aaa7d5SJason M. Bills     entry.remove_prefix(entryStart);
79052aaa7d5SJason M. Bills     // Use split to separate the entry into its fields
7911d4d54ddSJason M. Bills     std::vector<std::string> targetEntryFields;
79252aaa7d5SJason M. Bills     boost::split(targetEntryFields, entry, boost::is_any_of(","),
7931d4d54ddSJason M. Bills                  boost::token_compress_on);
79452aaa7d5SJason M. Bills     if (targetEntryFields.size() < 3)
7951d4d54ddSJason M. Bills     {
7961d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
797c04e2e70SJason M. Bills     }
7981a2fbddfSJason M. Bills     std::string& recordIDStr = targetEntryFields[0];
7991a2fbddfSJason M. Bills     std::string& recordTypeStr = targetEntryFields[1];
8001a2fbddfSJason M. Bills     std::string& eventDataStr = targetEntryFields[2];
801c04e2e70SJason M. Bills 
8021a2fbddfSJason M. Bills     uint16_t recordID;
8031a2fbddfSJason M. Bills     uint8_t recordType;
8041a2fbddfSJason M. Bills     try
8051a2fbddfSJason M. Bills     {
8061a2fbddfSJason M. Bills         recordID = std::stoul(recordIDStr);
8071a2fbddfSJason M. Bills         recordType = std::stoul(recordTypeStr, nullptr, 16);
8081a2fbddfSJason M. Bills     }
8091a2fbddfSJason M. Bills     catch (const std::invalid_argument&)
8101a2fbddfSJason M. Bills     {
8111a2fbddfSJason M. Bills         return ipmi::responseUnspecifiedError();
8121a2fbddfSJason M. Bills     }
8131d4d54ddSJason M. Bills     uint16_t nextRecordID = getNextRecordID(recordID, selLogFiles);
8141d4d54ddSJason M. Bills     std::vector<uint8_t> eventDataBytes;
8151a2fbddfSJason M. Bills     if (fromHexStr(eventDataStr, eventDataBytes) < 0)
8161d4d54ddSJason M. Bills     {
8171d4d54ddSJason M. Bills         return ipmi::responseUnspecifiedError();
8181d4d54ddSJason M. Bills     }
8191d4d54ddSJason M. Bills 
8201d4d54ddSJason M. Bills     if (recordType == intel_oem::ipmi::sel::systemEvent)
8211d4d54ddSJason M. Bills     {
8221d4d54ddSJason M. Bills         // Get the timestamp
8231d4d54ddSJason M. Bills         std::tm timeStruct = {};
82452aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
8251d4d54ddSJason M. Bills 
8261d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
8271d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
8281d4d54ddSJason M. Bills         {
8291d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
8301d4d54ddSJason M. Bills         }
8311d4d54ddSJason M. Bills 
8321d4d54ddSJason M. Bills         // Set the event message revision
8331d4d54ddSJason M. Bills         uint8_t evmRev = intel_oem::ipmi::sel::eventMsgRev;
8341d4d54ddSJason M. Bills 
8351a2fbddfSJason M. Bills         uint16_t generatorID = 0;
8361a2fbddfSJason M. Bills         uint8_t sensorType = 0;
8371a2fbddfSJason M. Bills         uint8_t sensorNum = 0xFF;
8381a2fbddfSJason M. Bills         uint7_t eventType = 0;
8391a2fbddfSJason M. Bills         bool eventDir = 0;
8401a2fbddfSJason M. Bills         // System type events should have six fields
8411a2fbddfSJason M. Bills         if (targetEntryFields.size() >= 6)
8421a2fbddfSJason M. Bills         {
8431a2fbddfSJason M. Bills             std::string& generatorIDStr = targetEntryFields[3];
8441a2fbddfSJason M. Bills             std::string& sensorPath = targetEntryFields[4];
8451a2fbddfSJason M. Bills             std::string& eventDirStr = targetEntryFields[5];
8461a2fbddfSJason M. Bills 
8471a2fbddfSJason M. Bills             // Get the generator ID
8481a2fbddfSJason M. Bills             try
8491a2fbddfSJason M. Bills             {
8501a2fbddfSJason M. Bills                 generatorID = std::stoul(generatorIDStr, nullptr, 16);
8511a2fbddfSJason M. Bills             }
8521a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
8531a2fbddfSJason M. Bills             {
8541a2fbddfSJason M. Bills                 std::cerr << "Invalid Generator ID\n";
8551a2fbddfSJason M. Bills             }
8561a2fbddfSJason M. Bills 
8571d4d54ddSJason M. Bills             // Get the sensor type, sensor number, and event type for the sensor
8581a2fbddfSJason M. Bills             sensorType = getSensorTypeFromPath(sensorPath);
8591a2fbddfSJason M. Bills             sensorNum = getSensorNumberFromPath(sensorPath);
8601a2fbddfSJason M. Bills             eventType = getSensorEventTypeFromPath(sensorPath);
8611d4d54ddSJason M. Bills 
8621d4d54ddSJason M. Bills             // Get the event direction
8631a2fbddfSJason M. Bills             try
8641a2fbddfSJason M. Bills             {
8651a2fbddfSJason M. Bills                 eventDir = std::stoul(eventDirStr) ? 0 : 1;
8661a2fbddfSJason M. Bills             }
8671a2fbddfSJason M. Bills             catch (const std::invalid_argument&)
8681a2fbddfSJason M. Bills             {
8691a2fbddfSJason M. Bills                 std::cerr << "Invalid Event Direction\n";
8701a2fbddfSJason M. Bills             }
8711a2fbddfSJason M. Bills         }
8721d4d54ddSJason M. Bills 
8731d4d54ddSJason M. Bills         // Only keep the eventData bytes that fit in the record
8741d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::systemEventSize> eventData{};
8751d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
8761d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
8771d4d54ddSJason M. Bills                     eventData.begin());
8781d4d54ddSJason M. Bills 
8791d4d54ddSJason M. Bills         return ipmi::responseSuccess(
8801d4d54ddSJason M. Bills             nextRecordID, recordID, recordType,
8811d4d54ddSJason M. Bills             systemEventType{timestamp, generatorID, evmRev, sensorType,
8821d4d54ddSJason M. Bills                             sensorNum, eventType, eventDir, eventData});
8831d4d54ddSJason M. Bills     }
8841d4d54ddSJason M. Bills     else if (recordType >= intel_oem::ipmi::sel::oemTsEventFirst &&
8851d4d54ddSJason M. Bills              recordType <= intel_oem::ipmi::sel::oemTsEventLast)
8861d4d54ddSJason M. Bills     {
8871d4d54ddSJason M. Bills         // Get the timestamp
8881d4d54ddSJason M. Bills         std::tm timeStruct = {};
88952aaa7d5SJason M. Bills         std::istringstream entryStream(entryTimestamp);
8901d4d54ddSJason M. Bills 
8911d4d54ddSJason M. Bills         uint32_t timestamp = ipmi::sel::invalidTimeStamp;
8921d4d54ddSJason M. Bills         if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
8931d4d54ddSJason M. Bills         {
8941d4d54ddSJason M. Bills             timestamp = std::mktime(&timeStruct);
8951d4d54ddSJason M. Bills         }
8961d4d54ddSJason M. Bills 
8971d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
8981d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemTsEventSize> eventData{};
8991d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
9001d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
9011d4d54ddSJason M. Bills                     eventData.begin());
9021d4d54ddSJason M. Bills 
9031d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
9041d4d54ddSJason M. Bills                                      oemTsEventType{timestamp, eventData});
9051d4d54ddSJason M. Bills     }
9061d4d54ddSJason M. Bills     else if (recordType >= intel_oem::ipmi::sel::oemEventFirst &&
9071d4d54ddSJason M. Bills              recordType <= intel_oem::ipmi::sel::oemEventLast)
9081d4d54ddSJason M. Bills     {
9091d4d54ddSJason M. Bills         // Only keep the bytes that fit in the record
9101d4d54ddSJason M. Bills         std::array<uint8_t, intel_oem::ipmi::sel::oemEventSize> eventData{};
9111d4d54ddSJason M. Bills         std::copy_n(eventDataBytes.begin(),
9121d4d54ddSJason M. Bills                     std::min(eventDataBytes.size(), eventData.size()),
9131d4d54ddSJason M. Bills                     eventData.begin());
9141d4d54ddSJason M. Bills 
9151d4d54ddSJason M. Bills         return ipmi::responseSuccess(nextRecordID, recordID, recordType,
9161d4d54ddSJason M. Bills                                      eventData);
9171d4d54ddSJason M. Bills     }
9181d4d54ddSJason M. Bills 
9191d4d54ddSJason M. Bills     return ipmi::responseUnspecifiedError();
920c04e2e70SJason M. Bills }
921c04e2e70SJason M. Bills 
9226dd8f047SJason M. Bills ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(
9236dd8f047SJason M. Bills     uint16_t recordID, uint8_t recordType, uint32_t timestamp,
9246dd8f047SJason M. Bills     uint16_t generatorID, uint8_t evmRev, uint8_t sensorType, uint8_t sensorNum,
9256dd8f047SJason M. Bills     uint8_t eventType, uint8_t eventData1, uint8_t eventData2,
9266dd8f047SJason M. Bills     uint8_t eventData3)
927c04e2e70SJason M. Bills {
928c04e2e70SJason M. Bills     // Per the IPMI spec, need to cancel any reservation when a SEL entry is
929c04e2e70SJason M. Bills     // added
930c04e2e70SJason M. Bills     cancelSELReservation();
931c04e2e70SJason M. Bills 
9326dd8f047SJason M. Bills     // Send this request to the Redfish hooks to log it as a Redfish message
9336dd8f047SJason M. Bills     // instead.  There is no need to add it to the SEL, so just return success.
9346dd8f047SJason M. Bills     intel_oem::ipmi::sel::checkRedfishHooks(
9356dd8f047SJason M. Bills         recordID, recordType, timestamp, generatorID, evmRev, sensorType,
9366dd8f047SJason M. Bills         sensorNum, eventType, eventData1, eventData2, eventData3);
93799b78ec8SJason M. Bills 
9386dd8f047SJason M. Bills     uint16_t responseID = 0xFFFF;
9396dd8f047SJason M. Bills     return ipmi::responseSuccess(responseID);
940c04e2e70SJason M. Bills }
941c04e2e70SJason M. Bills 
9421d4d54ddSJason M. Bills ipmi::RspType<uint8_t> ipmiStorageClearSEL(ipmi::Context::ptr ctx,
9431d4d54ddSJason M. Bills                                            uint16_t reservationID,
9441d4d54ddSJason M. Bills                                            const std::array<uint8_t, 3>& clr,
9451d4d54ddSJason M. Bills                                            uint8_t eraseOperation)
946c04e2e70SJason M. Bills {
9471d4d54ddSJason M. Bills     if (!checkSELReservation(reservationID))
948c04e2e70SJason M. Bills     {
9491d4d54ddSJason M. Bills         return ipmi::responseInvalidReservationId();
950c04e2e70SJason M. Bills     }
951c04e2e70SJason M. Bills 
9521d4d54ddSJason M. Bills     static constexpr std::array<uint8_t, 3> clrExpected = {'C', 'L', 'R'};
9531d4d54ddSJason M. Bills     if (clr != clrExpected)
954c04e2e70SJason M. Bills     {
9551d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
956c04e2e70SJason M. Bills     }
957c04e2e70SJason M. Bills 
9581d4d54ddSJason M. Bills     // Erasure status cannot be fetched, so always return erasure status as
9591d4d54ddSJason M. Bills     // `erase completed`.
9601d4d54ddSJason M. Bills     if (eraseOperation == ipmi::sel::getEraseStatus)
961c04e2e70SJason M. Bills     {
9621d4d54ddSJason M. Bills         return ipmi::responseSuccess(ipmi::sel::eraseComplete);
963c04e2e70SJason M. Bills     }
964c04e2e70SJason M. Bills 
9651d4d54ddSJason M. Bills     // Check that initiate erase is correct
9661d4d54ddSJason M. Bills     if (eraseOperation != ipmi::sel::initiateErase)
9671d4d54ddSJason M. Bills     {
9681d4d54ddSJason M. Bills         return ipmi::responseInvalidFieldRequest();
9691d4d54ddSJason M. Bills     }
9701d4d54ddSJason M. Bills 
9711d4d54ddSJason M. Bills     // Per the IPMI spec, need to cancel any reservation when the SEL is
9721d4d54ddSJason M. Bills     // cleared
973c04e2e70SJason M. Bills     cancelSELReservation();
974c04e2e70SJason M. Bills 
9757944c307SJason M. Bills     // Save the erase time
9767944c307SJason M. Bills     intel_oem::ipmi::sel::erase_time::save();
9777944c307SJason M. Bills 
9781d4d54ddSJason M. Bills     // Clear the SEL by deleting the log files
9791d4d54ddSJason M. Bills     std::vector<std::filesystem::path> selLogFiles;
9801d4d54ddSJason M. Bills     if (getSELLogFiles(selLogFiles))
981c04e2e70SJason M. Bills     {
9821d4d54ddSJason M. Bills         for (const std::filesystem::path& file : selLogFiles)
9831d4d54ddSJason M. Bills         {
9841d4d54ddSJason M. Bills             std::error_code ec;
9851d4d54ddSJason M. Bills             std::filesystem::remove(file, ec);
986c04e2e70SJason M. Bills         }
987c04e2e70SJason M. Bills     }
988c04e2e70SJason M. Bills 
9891d4d54ddSJason M. Bills     // Reload rsyslog so it knows to start new log files
99015419dd5SVernon Mauery     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
99115419dd5SVernon Mauery     sdbusplus::message::message rsyslogReload = dbus->new_method_call(
9921d4d54ddSJason M. Bills         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
9931d4d54ddSJason M. Bills         "org.freedesktop.systemd1.Manager", "ReloadUnit");
9941d4d54ddSJason M. Bills     rsyslogReload.append("rsyslog.service", "replace");
9951d4d54ddSJason M. Bills     try
9961d4d54ddSJason M. Bills     {
99715419dd5SVernon Mauery         sdbusplus::message::message reloadResponse = dbus->call(rsyslogReload);
9981d4d54ddSJason M. Bills     }
9991d4d54ddSJason M. Bills     catch (sdbusplus::exception_t& e)
10001d4d54ddSJason M. Bills     {
10011d4d54ddSJason M. Bills         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1002c04e2e70SJason M. Bills     }
1003c04e2e70SJason M. Bills 
10041d4d54ddSJason M. Bills     return ipmi::responseSuccess(ipmi::sel::eraseComplete);
10051d4d54ddSJason M. Bills }
10061d4d54ddSJason M. Bills 
10071a47462eSJason M. Bills ipmi::RspType<uint32_t> ipmiStorageGetSELTime()
10081a47462eSJason M. Bills {
10091a47462eSJason M. Bills     struct timespec selTime = {};
10101a47462eSJason M. Bills 
10111a47462eSJason M. Bills     if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
10121a47462eSJason M. Bills     {
10131a47462eSJason M. Bills         return ipmi::responseUnspecifiedError();
10141a47462eSJason M. Bills     }
10151a47462eSJason M. Bills 
10161a47462eSJason M. Bills     return ipmi::responseSuccess(selTime.tv_sec);
10171a47462eSJason M. Bills }
10181a47462eSJason M. Bills 
10191d4d54ddSJason M. Bills ipmi::RspType<> ipmiStorageSetSELTime(uint32_t selTime)
1020cac97a53SJason M. Bills {
1021cac97a53SJason M. Bills     // Set SEL Time is not supported
10221d4d54ddSJason M. Bills     return ipmi::responseInvalidCommand();
1023cac97a53SJason M. Bills }
1024cac97a53SJason M. Bills 
1025*74c50c64SJames Feist std::vector<uint8_t> getType12SDRs(uint16_t index, uint16_t recordId)
1026*74c50c64SJames Feist {
1027*74c50c64SJames Feist     std::vector<uint8_t> resp;
1028*74c50c64SJames Feist     if (index == 0)
1029*74c50c64SJames Feist     {
1030*74c50c64SJames Feist         Type12Record bmc = {};
1031*74c50c64SJames Feist         bmc.header.record_id_lsb = recordId;
1032*74c50c64SJames Feist         bmc.header.record_id_msb = recordId >> 8;
1033*74c50c64SJames Feist         bmc.header.sdr_version = ipmiSdrVersion;
1034*74c50c64SJames Feist         bmc.header.record_type = 0x12;
1035*74c50c64SJames Feist         bmc.header.record_length = 0x1b;
1036*74c50c64SJames Feist         bmc.slaveAddress = 0x20;
1037*74c50c64SJames Feist         bmc.channelNumber = 0;
1038*74c50c64SJames Feist         bmc.powerStateNotification = 0;
1039*74c50c64SJames Feist         bmc.deviceCapabilities = 0xBF;
1040*74c50c64SJames Feist         bmc.reserved = 0;
1041*74c50c64SJames Feist         bmc.entityID = 0x2E;
1042*74c50c64SJames Feist         bmc.entityInstance = 1;
1043*74c50c64SJames Feist         bmc.oem = 0;
1044*74c50c64SJames Feist         bmc.typeLengthCode = 0xD0;
1045*74c50c64SJames Feist         std::string bmcName = "Basbrd Mgmt Ctlr";
1046*74c50c64SJames Feist         std::copy(bmcName.begin(), bmcName.end(), bmc.name);
1047*74c50c64SJames Feist         uint8_t* bmcPtr = reinterpret_cast<uint8_t*>(&bmc);
1048*74c50c64SJames Feist         resp.insert(resp.end(), bmcPtr, bmcPtr + sizeof(Type12Record));
1049*74c50c64SJames Feist     }
1050*74c50c64SJames Feist     else if (index == 1)
1051*74c50c64SJames Feist     {
1052*74c50c64SJames Feist         Type12Record me = {};
1053*74c50c64SJames Feist         me.header.record_id_lsb = recordId;
1054*74c50c64SJames Feist         me.header.record_id_msb = recordId >> 8;
1055*74c50c64SJames Feist         me.header.sdr_version = ipmiSdrVersion;
1056*74c50c64SJames Feist         me.header.record_type = 0x12;
1057*74c50c64SJames Feist         me.header.record_length = 0x16;
1058*74c50c64SJames Feist         me.slaveAddress = 0x2C;
1059*74c50c64SJames Feist         me.channelNumber = 6;
1060*74c50c64SJames Feist         me.powerStateNotification = 0x24;
1061*74c50c64SJames Feist         me.deviceCapabilities = 0x21;
1062*74c50c64SJames Feist         me.reserved = 0;
1063*74c50c64SJames Feist         me.entityID = 0x2E;
1064*74c50c64SJames Feist         me.entityInstance = 2;
1065*74c50c64SJames Feist         me.oem = 0;
1066*74c50c64SJames Feist         me.typeLengthCode = 0xCB;
1067*74c50c64SJames Feist         std::string meName = "Mgmt Engine";
1068*74c50c64SJames Feist         std::copy(meName.begin(), meName.end(), me.name);
1069*74c50c64SJames Feist         uint8_t* mePtr = reinterpret_cast<uint8_t*>(&me);
1070*74c50c64SJames Feist         resp.insert(resp.end(), mePtr, mePtr + sizeof(Type12Record));
1071*74c50c64SJames Feist     }
1072*74c50c64SJames Feist     else
1073*74c50c64SJames Feist     {
1074*74c50c64SJames Feist         throw std::runtime_error("getType12SDRs:: Illegal index " +
1075*74c50c64SJames Feist                                  std::to_string(index));
1076*74c50c64SJames Feist     }
1077*74c50c64SJames Feist 
1078*74c50c64SJames Feist     return resp;
1079*74c50c64SJames Feist }
1080*74c50c64SJames Feist 
1081e2d1aee3SJason M. Bills void registerStorageFunctions()
1082e2d1aee3SJason M. Bills {
1083e2d1aee3SJason M. Bills     // <Get FRU Inventory Area Info>
1084d33acd6bSjayaprakash Mutyala     ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnStorage,
1085d33acd6bSjayaprakash Mutyala                           ipmi::storage::cmdGetFruInventoryAreaInfo,
1086d33acd6bSjayaprakash Mutyala                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
1087c04e2e70SJason M. Bills     // <READ FRU Data>
1088e2d1aee3SJason M. Bills     ipmiPrintAndRegister(
1089e2d1aee3SJason M. Bills         NETFUN_STORAGE,
1090e2d1aee3SJason M. Bills         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1091542498e9SJason M. Bills         ipmiStorageReadFRUData, PRIVILEGE_USER);
1092e2d1aee3SJason M. Bills 
1093c04e2e70SJason M. Bills     // <WRITE FRU Data>
1094e2d1aee3SJason M. Bills     ipmiPrintAndRegister(
1095e2d1aee3SJason M. Bills         NETFUN_STORAGE,
1096e2d1aee3SJason M. Bills         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1097e2d1aee3SJason M. Bills         NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1098c04e2e70SJason M. Bills 
1099c04e2e70SJason M. Bills     // <Get SEL Info>
11001d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1101542498e9SJason M. Bills                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
1102542498e9SJason M. Bills                           ipmiStorageGetSELInfo);
1103c04e2e70SJason M. Bills 
1104c04e2e70SJason M. Bills     // <Get SEL Entry>
11051d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1106542498e9SJason M. Bills                           ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
1107542498e9SJason M. Bills                           ipmiStorageGetSELEntry);
1108c04e2e70SJason M. Bills 
1109c04e2e70SJason M. Bills     // <Add SEL Entry>
11106dd8f047SJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
11116dd8f047SJason M. Bills                           static_cast<ipmi::Cmd>(ipmi::storage::cmdAddSelEntry),
11126dd8f047SJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
1113c04e2e70SJason M. Bills 
1114c04e2e70SJason M. Bills     // <Clear SEL>
11151d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
11161d4d54ddSJason M. Bills                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
11171d4d54ddSJason M. Bills                           ipmiStorageClearSEL);
1118cac97a53SJason M. Bills 
11191a47462eSJason M. Bills     // <Get SEL Time>
11201a47462eSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
1121542498e9SJason M. Bills                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
1122542498e9SJason M. Bills                           ipmiStorageGetSELTime);
11231a47462eSJason M. Bills 
1124cac97a53SJason M. Bills     // <Set SEL Time>
11251d4d54ddSJason M. Bills     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
11261d4d54ddSJason M. Bills                           ipmi::storage::cmdSetSelTime,
11271d4d54ddSJason M. Bills                           ipmi::Privilege::Operator, ipmiStorageSetSELTime);
1128e2d1aee3SJason M. Bills }
11293f7c5e40SJason M. Bills } // namespace storage
11303f7c5e40SJason M. Bills } // namespace ipmi
1131