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