xref: /openbmc/fb-ipmi-oem/src/storagecommands.cpp (revision 010dee0487db34138e6311a5bbb51e0f7683de30)
1e7d23d0eSVijay Khemka /*
2e7d23d0eSVijay Khemka  * Copyright (c)  2018 Intel Corporation.
3e7d23d0eSVijay Khemka  * Copyright (c)  2018-present Facebook.
4e7d23d0eSVijay Khemka  *
5e7d23d0eSVijay Khemka  * Licensed under the Apache License, Version 2.0 (the "License");
6e7d23d0eSVijay Khemka  * you may not use this file except in compliance with the License.
7e7d23d0eSVijay Khemka  * You may obtain a copy of the License at
8e7d23d0eSVijay Khemka  *
9e7d23d0eSVijay Khemka  *      http://www.apache.org/licenses/LICENSE-2.0
10e7d23d0eSVijay Khemka  *
11e7d23d0eSVijay Khemka  * Unless required by applicable law or agreed to in writing, software
12e7d23d0eSVijay Khemka  * distributed under the License is distributed on an "AS IS" BASIS,
13e7d23d0eSVijay Khemka  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14e7d23d0eSVijay Khemka  * See the License for the specific language governing permissions and
15e7d23d0eSVijay Khemka  * limitations under the License.
16e7d23d0eSVijay Khemka  */
17e7d23d0eSVijay Khemka 
18e7d23d0eSVijay Khemka #include <ipmid/api.h>
19e7d23d0eSVijay Khemka 
20e7d23d0eSVijay Khemka #include <boost/container/flat_map.hpp>
21e7d23d0eSVijay Khemka #include <commandutils.hpp>
221b6fae3fSVijay Khemka #include <ipmid/utils.hpp>
23e7d23d0eSVijay Khemka #include <phosphor-logging/log.hpp>
24e7d23d0eSVijay Khemka #include <sdbusplus/message/types.hpp>
25e7d23d0eSVijay Khemka #include <sdbusplus/timer.hpp>
26e7d23d0eSVijay Khemka #include <sensorutils.hpp>
27e7d23d0eSVijay Khemka #include <storagecommands.hpp>
28e7d23d0eSVijay Khemka 
2963c99be4SVijay Khemka #include <iostream>
302405ae98SPatrick Williams #include <unordered_map>
3163c99be4SVijay Khemka 
32e7d23d0eSVijay Khemka namespace ipmi
33e7d23d0eSVijay Khemka {
34e7d23d0eSVijay Khemka 
35e7d23d0eSVijay Khemka namespace storage
36e7d23d0eSVijay Khemka {
37e7d23d0eSVijay Khemka void registerStorageFunctions() __attribute__((constructor));
38e7d23d0eSVijay Khemka 
39e7d23d0eSVijay Khemka constexpr static const size_t maxMessageSize = 64;
40e7d23d0eSVijay Khemka constexpr static const size_t maxFruSdrNameSize = 16;
41e7d23d0eSVijay Khemka static constexpr int sensorMapUpdatePeriod = 2;
42e7d23d0eSVijay Khemka using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
43e7d23d0eSVijay Khemka 
44e7d23d0eSVijay Khemka using ManagedObjectSensor =
45e7d23d0eSVijay Khemka     std::map<sdbusplus::message::object_path,
46e7d23d0eSVijay Khemka              std::map<std::string, std::map<std::string, DbusVariant>>>;
47e7d23d0eSVijay Khemka 
48e7d23d0eSVijay Khemka static uint16_t sdrReservationID;
49e7d23d0eSVijay Khemka 
50e7d23d0eSVijay Khemka static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
51e7d23d0eSVijay Khemka static SensorSubTree sensorTree;
52e7d23d0eSVijay Khemka 
53e7d23d0eSVijay Khemka void registerSensorFunctions() __attribute__((constructor));
54e7d23d0eSVijay Khemka using ManagedObjectType = boost::container::flat_map<
55e7d23d0eSVijay Khemka     sdbusplus::message::object_path,
56e7d23d0eSVijay Khemka     boost::container::flat_map<
57e7d23d0eSVijay Khemka         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
58e7d23d0eSVijay Khemka using ManagedEntry = std::pair<
59e7d23d0eSVijay Khemka     sdbusplus::message::object_path,
60e7d23d0eSVijay Khemka     boost::container::flat_map<
61e7d23d0eSVijay Khemka         std::string, boost::container::flat_map<std::string, DbusVariant>>>;
62e7d23d0eSVijay Khemka 
63e7d23d0eSVijay Khemka constexpr static const char* fruDeviceServiceName =
64e7d23d0eSVijay Khemka     "xyz.openbmc_project.FruDevice";
65e7d23d0eSVijay Khemka constexpr static const size_t cacheTimeoutSeconds = 10;
66e7d23d0eSVijay Khemka 
67e7d23d0eSVijay Khemka static std::vector<uint8_t> fruCache;
684de58763SPotin Lai static uint16_t cacheBus = 0xFFFF;
69e7d23d0eSVijay Khemka static uint8_t cacheAddr = 0XFF;
70e7d23d0eSVijay Khemka 
71d79f89e7SPatrick Williams std::unique_ptr<sdbusplus::Timer> cacheTimer = nullptr;
72e7d23d0eSVijay Khemka 
73e7d23d0eSVijay Khemka // we unfortunately have to build a map of hashes in case there is a
74e7d23d0eSVijay Khemka // collision to verify our dev-id
75e7d23d0eSVijay Khemka boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
76e7d23d0eSVijay Khemka 
77cd315e07SPatrick Williams static sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
78e7d23d0eSVijay Khemka 
792ca4aa0eSDelphine CC Chiu using InterfaceName = std::string;
802ca4aa0eSDelphine CC Chiu using PropertyName = std::string;
812ca4aa0eSDelphine CC Chiu using ThresholdStr = std::string;
822ca4aa0eSDelphine CC Chiu 
832ca4aa0eSDelphine CC Chiu enum class AlarmType
842ca4aa0eSDelphine CC Chiu {
852ca4aa0eSDelphine CC Chiu     low,
862ca4aa0eSDelphine CC Chiu     high
872ca4aa0eSDelphine CC Chiu };
882ca4aa0eSDelphine CC Chiu 
892ca4aa0eSDelphine CC Chiu struct Property
902ca4aa0eSDelphine CC Chiu {
912ca4aa0eSDelphine CC Chiu     PropertyName name;
922ca4aa0eSDelphine CC Chiu     ThresholdStr threshold;
932ca4aa0eSDelphine CC Chiu };
942ca4aa0eSDelphine CC Chiu 
952ca4aa0eSDelphine CC Chiu const std::vector<InterfaceName> thresholdCheckedOrder{
962ca4aa0eSDelphine CC Chiu     "xyz.openbmc_project.Sensor.Threshold.HardShutdown",
972ca4aa0eSDelphine CC Chiu     "xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
982ca4aa0eSDelphine CC Chiu     "xyz.openbmc_project.Sensor.Threshold.Critical",
992ca4aa0eSDelphine CC Chiu     "xyz.openbmc_project.Sensor.Threshold.Warning"};
1002ca4aa0eSDelphine CC Chiu 
1012ca4aa0eSDelphine CC Chiu const std::unordered_map<std::string, std::map<AlarmType, Property>>
1022ca4aa0eSDelphine CC Chiu     alarmProperties{
1032ca4aa0eSDelphine CC Chiu         {"xyz.openbmc_project.Sensor.Threshold.HardShutdown",
1042ca4aa0eSDelphine CC Chiu          {{AlarmType::low, Property{"HardShutdownAlarmLow", "LNR"}},
1052ca4aa0eSDelphine CC Chiu           {AlarmType::high, Property{"HardShutdownAlarmHigh", "UNR"}}}},
1062ca4aa0eSDelphine CC Chiu 
1072ca4aa0eSDelphine CC Chiu         {"xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
1082ca4aa0eSDelphine CC Chiu          {{AlarmType::low, Property{"SoftShutdownAlarmLow", "LNR"}},
1092ca4aa0eSDelphine CC Chiu           {AlarmType::high, Property{"SoftShutdownAlarmHigh", "UNR"}}}},
1102ca4aa0eSDelphine CC Chiu 
1112ca4aa0eSDelphine CC Chiu         {"xyz.openbmc_project.Sensor.Threshold.Critical",
1122ca4aa0eSDelphine CC Chiu          {{AlarmType::low, Property{"CriticalAlarmLow", "LCR"}},
1132ca4aa0eSDelphine CC Chiu           {AlarmType::high, Property{"CriticalAlarmHigh", "UCR"}}}},
1142ca4aa0eSDelphine CC Chiu 
1152ca4aa0eSDelphine CC Chiu         {"xyz.openbmc_project.Sensor.Threshold.Warning",
1162ca4aa0eSDelphine CC Chiu          {{AlarmType::low, Property{"WarningAlarmLow", "LNC"}},
1172ca4aa0eSDelphine CC Chiu           {AlarmType::high, Property{"WarningAlarmHigh", "UNC"}}}},
1182ca4aa0eSDelphine CC Chiu     };
1192ca4aa0eSDelphine CC Chiu 
getSensorMap(std::string sensorConnection,std::string sensorPath,SensorMap & sensorMap)120e7d23d0eSVijay Khemka static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
121e7d23d0eSVijay Khemka                          SensorMap& sensorMap)
122e7d23d0eSVijay Khemka {
123e7d23d0eSVijay Khemka     static boost::container::flat_map<
124e7d23d0eSVijay Khemka         std::string, std::chrono::time_point<std::chrono::steady_clock>>
125e7d23d0eSVijay Khemka         updateTimeMap;
126e7d23d0eSVijay Khemka 
127e7d23d0eSVijay Khemka     auto updateFind = updateTimeMap.find(sensorConnection);
128e7d23d0eSVijay Khemka     auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
129e7d23d0eSVijay Khemka     if (updateFind != updateTimeMap.end())
130e7d23d0eSVijay Khemka     {
131e7d23d0eSVijay Khemka         lastUpdate = updateFind->second;
132e7d23d0eSVijay Khemka     }
133e7d23d0eSVijay Khemka 
134e7d23d0eSVijay Khemka     auto now = std::chrono::steady_clock::now();
135e7d23d0eSVijay Khemka 
136e7d23d0eSVijay Khemka     if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
137e7d23d0eSVijay Khemka             .count() > sensorMapUpdatePeriod)
138e7d23d0eSVijay Khemka     {
139e7d23d0eSVijay Khemka         updateTimeMap[sensorConnection] = now;
140e7d23d0eSVijay Khemka 
141e7d23d0eSVijay Khemka         auto managedObj = dbus.new_method_call(
1428ee95d69SPotin Lai             sensorConnection.c_str(), "/xyz/openbmc_project/sensors",
1438ee95d69SPotin Lai             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
144e7d23d0eSVijay Khemka 
145e7d23d0eSVijay Khemka         ManagedObjectSensor managedObjects;
146e7d23d0eSVijay Khemka         try
147e7d23d0eSVijay Khemka         {
148e7d23d0eSVijay Khemka             auto reply = dbus.call(managedObj);
149e7d23d0eSVijay Khemka             reply.read(managedObjects);
150e7d23d0eSVijay Khemka         }
15135d12546SPatrick Williams         catch (const sdbusplus::exception_t&)
152e7d23d0eSVijay Khemka         {
153e7d23d0eSVijay Khemka             phosphor::logging::log<phosphor::logging::level::ERR>(
154e7d23d0eSVijay Khemka                 "Error getting managed objects from connection",
155e7d23d0eSVijay Khemka                 phosphor::logging::entry("CONNECTION=%s",
156e7d23d0eSVijay Khemka                                          sensorConnection.c_str()));
157e7d23d0eSVijay Khemka             return false;
158e7d23d0eSVijay Khemka         }
159e7d23d0eSVijay Khemka 
160e7d23d0eSVijay Khemka         SensorCache[sensorConnection] = managedObjects;
161e7d23d0eSVijay Khemka     }
162e7d23d0eSVijay Khemka     auto connection = SensorCache.find(sensorConnection);
163e7d23d0eSVijay Khemka     if (connection == SensorCache.end())
164e7d23d0eSVijay Khemka     {
165e7d23d0eSVijay Khemka         return false;
166e7d23d0eSVijay Khemka     }
167e7d23d0eSVijay Khemka     auto path = connection->second.find(sensorPath);
168e7d23d0eSVijay Khemka     if (path == connection->second.end())
169e7d23d0eSVijay Khemka     {
170e7d23d0eSVijay Khemka         return false;
171e7d23d0eSVijay Khemka     }
172e7d23d0eSVijay Khemka     sensorMap = path->second;
173e7d23d0eSVijay Khemka 
174e7d23d0eSVijay Khemka     return true;
175e7d23d0eSVijay Khemka }
176e7d23d0eSVijay Khemka 
writeFru()177e7d23d0eSVijay Khemka bool writeFru()
178e7d23d0eSVijay Khemka {
179cd315e07SPatrick Williams     sdbusplus::message_t writeFru = dbus.new_method_call(
180e7d23d0eSVijay Khemka         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
181e7d23d0eSVijay Khemka         "xyz.openbmc_project.FruDeviceManager", "WriteFru");
182e7d23d0eSVijay Khemka     writeFru.append(cacheBus, cacheAddr, fruCache);
183e7d23d0eSVijay Khemka     try
184e7d23d0eSVijay Khemka     {
185cd315e07SPatrick Williams         sdbusplus::message_t writeFruResp = dbus.call(writeFru);
186e7d23d0eSVijay Khemka     }
18735d12546SPatrick Williams     catch (const sdbusplus::exception_t&)
188e7d23d0eSVijay Khemka     {
189e7d23d0eSVijay Khemka         // todo: log sel?
190e7d23d0eSVijay Khemka         phosphor::logging::log<phosphor::logging::level::ERR>(
191e7d23d0eSVijay Khemka             "error writing fru");
192e7d23d0eSVijay Khemka         return false;
193e7d23d0eSVijay Khemka     }
194e7d23d0eSVijay Khemka     return true;
195e7d23d0eSVijay Khemka }
196e7d23d0eSVijay Khemka 
createTimer()197e7d23d0eSVijay Khemka void createTimer()
198e7d23d0eSVijay Khemka {
199e7d23d0eSVijay Khemka     if (cacheTimer == nullptr)
200e7d23d0eSVijay Khemka     {
201d79f89e7SPatrick Williams         cacheTimer = std::make_unique<sdbusplus::Timer>(writeFru);
202e7d23d0eSVijay Khemka     }
203e7d23d0eSVijay Khemka }
204e7d23d0eSVijay Khemka 
replaceCacheFru(uint8_t devId)205e7d23d0eSVijay Khemka ipmi_ret_t replaceCacheFru(uint8_t devId)
206e7d23d0eSVijay Khemka {
207e7d23d0eSVijay Khemka     static uint8_t lastDevId = 0xFF;
208e7d23d0eSVijay Khemka 
209e7d23d0eSVijay Khemka     bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
210e7d23d0eSVijay Khemka     if (lastDevId == devId && timerRunning)
211e7d23d0eSVijay Khemka     {
212e7d23d0eSVijay Khemka         return IPMI_CC_OK; // cache already up to date
213e7d23d0eSVijay Khemka     }
214e7d23d0eSVijay Khemka     // if timer is running, stop it and writeFru manually
215e7d23d0eSVijay Khemka     else if (timerRunning)
216e7d23d0eSVijay Khemka     {
217e7d23d0eSVijay Khemka         cacheTimer->stop();
218e7d23d0eSVijay Khemka         writeFru();
219e7d23d0eSVijay Khemka     }
220e7d23d0eSVijay Khemka 
221cd315e07SPatrick Williams     sdbusplus::message_t getObjects = dbus.new_method_call(
222e7d23d0eSVijay Khemka         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
223e7d23d0eSVijay Khemka         "GetManagedObjects");
224e7d23d0eSVijay Khemka     ManagedObjectType frus;
225e7d23d0eSVijay Khemka     try
226e7d23d0eSVijay Khemka     {
227cd315e07SPatrick Williams         sdbusplus::message_t resp = dbus.call(getObjects);
228e7d23d0eSVijay Khemka         resp.read(frus);
229e7d23d0eSVijay Khemka     }
23035d12546SPatrick Williams     catch (const sdbusplus::exception_t&)
231e7d23d0eSVijay Khemka     {
232e7d23d0eSVijay Khemka         phosphor::logging::log<phosphor::logging::level::ERR>(
233e7d23d0eSVijay Khemka             "replaceCacheFru: error getting managed objects");
234e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
235e7d23d0eSVijay Khemka     }
236e7d23d0eSVijay Khemka 
237e7d23d0eSVijay Khemka     deviceHashes.clear();
238e7d23d0eSVijay Khemka 
239b2ae88b4Scchoux     uint8_t fruHash = 0;
240b2ae88b4Scchoux     uint8_t mbFruBus = 0, mbFruAddr = 0;
241b2ae88b4Scchoux 
242b2ae88b4Scchoux     auto device = getMbFruDevice();
243b2ae88b4Scchoux     if (device)
244b2ae88b4Scchoux     {
245b2ae88b4Scchoux         std::tie(mbFruBus, mbFruAddr) = *device;
246b2ae88b4Scchoux         deviceHashes.emplace(0, std::make_pair(mbFruBus, mbFruAddr));
247b2ae88b4Scchoux         fruHash++;
248b2ae88b4Scchoux     }
249b2ae88b4Scchoux 
250e7d23d0eSVijay Khemka     for (const auto& fru : frus)
251e7d23d0eSVijay Khemka     {
252e7d23d0eSVijay Khemka         auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
253e7d23d0eSVijay Khemka         if (fruIface == fru.second.end())
254e7d23d0eSVijay Khemka         {
255e7d23d0eSVijay Khemka             continue;
256e7d23d0eSVijay Khemka         }
257e7d23d0eSVijay Khemka 
258e7d23d0eSVijay Khemka         auto busFind = fruIface->second.find("BUS");
259e7d23d0eSVijay Khemka         auto addrFind = fruIface->second.find("ADDRESS");
260e7d23d0eSVijay Khemka         if (busFind == fruIface->second.end() ||
261e7d23d0eSVijay Khemka             addrFind == fruIface->second.end())
262e7d23d0eSVijay Khemka         {
263e7d23d0eSVijay Khemka             phosphor::logging::log<phosphor::logging::level::INFO>(
264e7d23d0eSVijay Khemka                 "fru device missing Bus or Address",
265e7d23d0eSVijay Khemka                 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
266e7d23d0eSVijay Khemka             continue;
267e7d23d0eSVijay Khemka         }
268e7d23d0eSVijay Khemka 
269ef0efbc4SPatrick Williams         uint8_t fruBus = std::get<uint32_t>(busFind->second);
270ef0efbc4SPatrick Williams         uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
271b2ae88b4Scchoux         if (fruBus != mbFruBus || fruAddr != mbFruAddr)
272e7d23d0eSVijay Khemka         {
273b2ae88b4Scchoux             deviceHashes.emplace(fruHash, std::make_pair(fruBus, fruAddr));
274e7d23d0eSVijay Khemka             fruHash++;
275e7d23d0eSVijay Khemka         }
276e7d23d0eSVijay Khemka     }
277e7d23d0eSVijay Khemka     auto deviceFind = deviceHashes.find(devId);
278e7d23d0eSVijay Khemka     if (deviceFind == deviceHashes.end())
279e7d23d0eSVijay Khemka     {
280e7d23d0eSVijay Khemka         return IPMI_CC_SENSOR_INVALID;
281e7d23d0eSVijay Khemka     }
282e7d23d0eSVijay Khemka 
283e7d23d0eSVijay Khemka     fruCache.clear();
284cd315e07SPatrick Williams     sdbusplus::message_t getRawFru = dbus.new_method_call(
285e7d23d0eSVijay Khemka         fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
286e7d23d0eSVijay Khemka         "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
287e7d23d0eSVijay Khemka     cacheBus = deviceFind->second.first;
288e7d23d0eSVijay Khemka     cacheAddr = deviceFind->second.second;
289e7d23d0eSVijay Khemka     getRawFru.append(cacheBus, cacheAddr);
290e7d23d0eSVijay Khemka     try
291e7d23d0eSVijay Khemka     {
292cd315e07SPatrick Williams         sdbusplus::message_t getRawResp = dbus.call(getRawFru);
293e7d23d0eSVijay Khemka         getRawResp.read(fruCache);
294e7d23d0eSVijay Khemka     }
29535d12546SPatrick Williams     catch (const sdbusplus::exception_t&)
296e7d23d0eSVijay Khemka     {
297e7d23d0eSVijay Khemka         lastDevId = 0xFF;
2984de58763SPotin Lai         cacheBus = 0xFFFF;
299e7d23d0eSVijay Khemka         cacheAddr = 0xFF;
300e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
301e7d23d0eSVijay Khemka     }
302e7d23d0eSVijay Khemka 
303e7d23d0eSVijay Khemka     lastDevId = devId;
304e7d23d0eSVijay Khemka     return IPMI_CC_OK;
305e7d23d0eSVijay Khemka }
306e7d23d0eSVijay Khemka 
ipmiStorageReadFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)307*010dee04SPatrick Williams ipmi_ret_t ipmiStorageReadFRUData(
308*010dee04SPatrick Williams     ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
309e39f9393SWilly Tu     ipmi_data_len_t dataLen, ipmi_context_t)
310e7d23d0eSVijay Khemka {
311e7d23d0eSVijay Khemka     if (*dataLen != 4)
312e7d23d0eSVijay Khemka     {
313e7d23d0eSVijay Khemka         *dataLen = 0;
314e7d23d0eSVijay Khemka         return IPMI_CC_REQ_DATA_LEN_INVALID;
315e7d23d0eSVijay Khemka     }
316e7d23d0eSVijay Khemka     *dataLen = 0; // default to 0 in case of an error
317e7d23d0eSVijay Khemka 
318e7d23d0eSVijay Khemka     auto req = static_cast<GetFRUAreaReq*>(request);
319e7d23d0eSVijay Khemka 
320e7d23d0eSVijay Khemka     if (req->countToRead > maxMessageSize - 1)
321e7d23d0eSVijay Khemka     {
322e7d23d0eSVijay Khemka         return IPMI_CC_INVALID_FIELD_REQUEST;
323e7d23d0eSVijay Khemka     }
324e7d23d0eSVijay Khemka     ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325e7d23d0eSVijay Khemka 
326e7d23d0eSVijay Khemka     if (status != IPMI_CC_OK)
327e7d23d0eSVijay Khemka     {
328e7d23d0eSVijay Khemka         return status;
329e7d23d0eSVijay Khemka     }
330e7d23d0eSVijay Khemka 
331e7d23d0eSVijay Khemka     size_t fromFRUByteLen = 0;
332e7d23d0eSVijay Khemka     if (req->countToRead + req->fruInventoryOffset < fruCache.size())
333e7d23d0eSVijay Khemka     {
334e7d23d0eSVijay Khemka         fromFRUByteLen = req->countToRead;
335e7d23d0eSVijay Khemka     }
336e7d23d0eSVijay Khemka     else if (fruCache.size() > req->fruInventoryOffset)
337e7d23d0eSVijay Khemka     {
338e7d23d0eSVijay Khemka         fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
339e7d23d0eSVijay Khemka     }
340e7d23d0eSVijay Khemka     size_t padByteLen = req->countToRead - fromFRUByteLen;
341e7d23d0eSVijay Khemka     uint8_t* respPtr = static_cast<uint8_t*>(response);
342e7d23d0eSVijay Khemka     *respPtr = req->countToRead;
343e7d23d0eSVijay Khemka     std::copy(fruCache.begin() + req->fruInventoryOffset,
344e7d23d0eSVijay Khemka               fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
345e7d23d0eSVijay Khemka               ++respPtr);
346e7d23d0eSVijay Khemka     // if longer than the fru is requested, fill with 0xFF
347e7d23d0eSVijay Khemka     if (padByteLen)
348e7d23d0eSVijay Khemka     {
349e7d23d0eSVijay Khemka         respPtr += fromFRUByteLen;
350e7d23d0eSVijay Khemka         std::fill(respPtr, respPtr + padByteLen, 0xFF);
351e7d23d0eSVijay Khemka     }
352e7d23d0eSVijay Khemka     *dataLen = fromFRUByteLen + 1;
353e7d23d0eSVijay Khemka 
354e7d23d0eSVijay Khemka     return IPMI_CC_OK;
355e7d23d0eSVijay Khemka }
356e7d23d0eSVijay Khemka 
ipmiStorageWriteFRUData(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)357*010dee04SPatrick Williams ipmi_ret_t ipmiStorageWriteFRUData(
358*010dee04SPatrick Williams     ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
359e39f9393SWilly Tu     ipmi_data_len_t dataLen, ipmi_context_t)
360e7d23d0eSVijay Khemka {
361e7d23d0eSVijay Khemka     if (*dataLen < 4 ||
362e7d23d0eSVijay Khemka         *dataLen >=
363e7d23d0eSVijay Khemka             0xFF + 3) // count written return is one byte, so limit to one
364e7d23d0eSVijay Khemka                       // byte of data after the three request data bytes
365e7d23d0eSVijay Khemka     {
366e7d23d0eSVijay Khemka         *dataLen = 0;
367e7d23d0eSVijay Khemka         return IPMI_CC_REQ_DATA_LEN_INVALID;
368e7d23d0eSVijay Khemka     }
369e7d23d0eSVijay Khemka 
370e7d23d0eSVijay Khemka     auto req = static_cast<WriteFRUDataReq*>(request);
371e7d23d0eSVijay Khemka     size_t writeLen = *dataLen - 3;
372e7d23d0eSVijay Khemka     *dataLen = 0; // default to 0 in case of an error
373e7d23d0eSVijay Khemka 
374e7d23d0eSVijay Khemka     ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
375e7d23d0eSVijay Khemka     if (status != IPMI_CC_OK)
376e7d23d0eSVijay Khemka     {
377e7d23d0eSVijay Khemka         return status;
378e7d23d0eSVijay Khemka     }
379e39f9393SWilly Tu     size_t lastWriteAddr = req->fruInventoryOffset + writeLen;
380e7d23d0eSVijay Khemka     if (fruCache.size() < lastWriteAddr)
381e7d23d0eSVijay Khemka     {
382e7d23d0eSVijay Khemka         fruCache.resize(req->fruInventoryOffset + writeLen);
383e7d23d0eSVijay Khemka     }
384e7d23d0eSVijay Khemka 
385e7d23d0eSVijay Khemka     std::copy(req->data, req->data + writeLen,
386e7d23d0eSVijay Khemka               fruCache.begin() + req->fruInventoryOffset);
387e7d23d0eSVijay Khemka 
388e7d23d0eSVijay Khemka     bool atEnd = false;
389e7d23d0eSVijay Khemka 
390e7d23d0eSVijay Khemka     if (fruCache.size() >= sizeof(FRUHeader))
391e7d23d0eSVijay Khemka     {
392e7d23d0eSVijay Khemka         FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
393e7d23d0eSVijay Khemka 
394e39f9393SWilly Tu         size_t lastRecordStart = std::max(
395e7d23d0eSVijay Khemka             header->internalOffset,
396e7d23d0eSVijay Khemka             std::max(header->chassisOffset,
397e7d23d0eSVijay Khemka                      std::max(header->boardOffset, header->productOffset)));
398e7d23d0eSVijay Khemka         // TODO: Handle Multi-Record FRUs?
399e7d23d0eSVijay Khemka 
400e7d23d0eSVijay Khemka         lastRecordStart *= 8; // header starts in are multiples of 8 bytes
401e7d23d0eSVijay Khemka 
402e7d23d0eSVijay Khemka         // get the length of the area in multiples of 8 bytes
403e7d23d0eSVijay Khemka         if (lastWriteAddr > (lastRecordStart + 1))
404e7d23d0eSVijay Khemka         {
405e7d23d0eSVijay Khemka             // second byte in record area is the length
406e7d23d0eSVijay Khemka             int areaLength(fruCache[lastRecordStart + 1]);
407e7d23d0eSVijay Khemka             areaLength *= 8; // it is in multiples of 8 bytes
408e7d23d0eSVijay Khemka 
409e7d23d0eSVijay Khemka             if (lastWriteAddr >= (areaLength + lastRecordStart))
410e7d23d0eSVijay Khemka             {
411e7d23d0eSVijay Khemka                 atEnd = true;
412e7d23d0eSVijay Khemka             }
413e7d23d0eSVijay Khemka         }
414e7d23d0eSVijay Khemka     }
415e7d23d0eSVijay Khemka     uint8_t* respPtr = static_cast<uint8_t*>(response);
416e7d23d0eSVijay Khemka     if (atEnd)
417e7d23d0eSVijay Khemka     {
418e7d23d0eSVijay Khemka         // cancel timer, we're at the end so might as well send it
419e7d23d0eSVijay Khemka         cacheTimer->stop();
420e7d23d0eSVijay Khemka         if (!writeFru())
421e7d23d0eSVijay Khemka         {
422e7d23d0eSVijay Khemka             return IPMI_CC_INVALID_FIELD_REQUEST;
423e7d23d0eSVijay Khemka         }
424e7d23d0eSVijay Khemka         *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
425e7d23d0eSVijay Khemka     }
426e7d23d0eSVijay Khemka     else
427e7d23d0eSVijay Khemka     {
428e7d23d0eSVijay Khemka         // start a timer, if no further data is sent in cacheTimeoutSeconds
429e7d23d0eSVijay Khemka         // seconds, check to see if it is valid
430e7d23d0eSVijay Khemka         createTimer();
431e7d23d0eSVijay Khemka         cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
432e7d23d0eSVijay Khemka             std::chrono::seconds(cacheTimeoutSeconds)));
433e7d23d0eSVijay Khemka         *respPtr = 0;
434e7d23d0eSVijay Khemka     }
435e7d23d0eSVijay Khemka 
436e7d23d0eSVijay Khemka     *dataLen = 1;
437e7d23d0eSVijay Khemka 
438e7d23d0eSVijay Khemka     return IPMI_CC_OK;
439e7d23d0eSVijay Khemka }
440e7d23d0eSVijay Khemka 
getFruSdrCount(size_t & count)441e7d23d0eSVijay Khemka ipmi_ret_t getFruSdrCount(size_t& count)
442e7d23d0eSVijay Khemka {
443e7d23d0eSVijay Khemka     ipmi_ret_t ret = replaceCacheFru(0);
444e7d23d0eSVijay Khemka     if (ret != IPMI_CC_OK)
445e7d23d0eSVijay Khemka     {
446e7d23d0eSVijay Khemka         return ret;
447e7d23d0eSVijay Khemka     }
448e7d23d0eSVijay Khemka     count = deviceHashes.size();
449e7d23d0eSVijay Khemka     return IPMI_CC_OK;
450e7d23d0eSVijay Khemka }
451e7d23d0eSVijay Khemka 
getFruSdrs(size_t index,get_sdr::SensorDataFruRecord & resp)452e7d23d0eSVijay Khemka ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
453e7d23d0eSVijay Khemka {
454e7d23d0eSVijay Khemka     ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
455e7d23d0eSVijay Khemka     if (ret != IPMI_CC_OK)
456e7d23d0eSVijay Khemka     {
457e7d23d0eSVijay Khemka         return ret;
458e7d23d0eSVijay Khemka     }
459e7d23d0eSVijay Khemka     if (deviceHashes.size() < index)
460e7d23d0eSVijay Khemka     {
461e7d23d0eSVijay Khemka         return IPMI_CC_INVALID_FIELD_REQUEST;
462e7d23d0eSVijay Khemka     }
463e7d23d0eSVijay Khemka     auto device = deviceHashes.begin() + index;
464e7d23d0eSVijay Khemka     uint8_t& bus = device->second.first;
465e7d23d0eSVijay Khemka     uint8_t& address = device->second.second;
466e7d23d0eSVijay Khemka 
467e7d23d0eSVijay Khemka     ManagedObjectType frus;
468e7d23d0eSVijay Khemka 
469cd315e07SPatrick Williams     sdbusplus::message_t getObjects = dbus.new_method_call(
470e7d23d0eSVijay Khemka         fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
471e7d23d0eSVijay Khemka         "GetManagedObjects");
472e7d23d0eSVijay Khemka     try
473e7d23d0eSVijay Khemka     {
474cd315e07SPatrick Williams         sdbusplus::message_t resp = dbus.call(getObjects);
475e7d23d0eSVijay Khemka         resp.read(frus);
476e7d23d0eSVijay Khemka     }
47735d12546SPatrick Williams     catch (const sdbusplus::exception_t&)
478e7d23d0eSVijay Khemka     {
479e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
480e7d23d0eSVijay Khemka     }
481e7d23d0eSVijay Khemka     boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
482*010dee04SPatrick Williams     auto fru = std::find_if(
483*010dee04SPatrick Williams         frus.begin(), frus.end(),
484e7d23d0eSVijay Khemka         [bus, address, &fruData](ManagedEntry& entry) {
485*010dee04SPatrick Williams             auto findFruDevice =
486*010dee04SPatrick Williams                 entry.second.find("xyz.openbmc_project.FruDevice");
487e7d23d0eSVijay Khemka             if (findFruDevice == entry.second.end())
488e7d23d0eSVijay Khemka             {
489e7d23d0eSVijay Khemka                 return false;
490e7d23d0eSVijay Khemka             }
491e7d23d0eSVijay Khemka             fruData = &(findFruDevice->second);
492e7d23d0eSVijay Khemka             auto findBus = findFruDevice->second.find("BUS");
4932405ae98SPatrick Williams             auto findAddress = findFruDevice->second.find("ADDRESS");
494e7d23d0eSVijay Khemka             if (findBus == findFruDevice->second.end() ||
495e7d23d0eSVijay Khemka                 findAddress == findFruDevice->second.end())
496e7d23d0eSVijay Khemka             {
497e7d23d0eSVijay Khemka                 return false;
498e7d23d0eSVijay Khemka             }
499ef0efbc4SPatrick Williams             if (std::get<uint32_t>(findBus->second) != bus)
500e7d23d0eSVijay Khemka             {
501e7d23d0eSVijay Khemka                 return false;
502e7d23d0eSVijay Khemka             }
503ef0efbc4SPatrick Williams             if (std::get<uint32_t>(findAddress->second) != address)
504e7d23d0eSVijay Khemka             {
505e7d23d0eSVijay Khemka                 return false;
506e7d23d0eSVijay Khemka             }
507e7d23d0eSVijay Khemka             return true;
508e7d23d0eSVijay Khemka         });
509e7d23d0eSVijay Khemka     if (fru == frus.end())
510e7d23d0eSVijay Khemka     {
511e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
512e7d23d0eSVijay Khemka     }
513e7d23d0eSVijay Khemka     std::string name;
514e7d23d0eSVijay Khemka     auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
515e7d23d0eSVijay Khemka     auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
516e7d23d0eSVijay Khemka     if (findProductName != fruData->end())
517e7d23d0eSVijay Khemka     {
518ef0efbc4SPatrick Williams         name = std::get<std::string>(findProductName->second);
519e7d23d0eSVijay Khemka     }
520e7d23d0eSVijay Khemka     else if (findBoardName != fruData->end())
521e7d23d0eSVijay Khemka     {
522ef0efbc4SPatrick Williams         name = std::get<std::string>(findBoardName->second);
523e7d23d0eSVijay Khemka     }
524e7d23d0eSVijay Khemka     else
525e7d23d0eSVijay Khemka     {
526e7d23d0eSVijay Khemka         name = "UNKNOWN";
527e7d23d0eSVijay Khemka     }
528e7d23d0eSVijay Khemka     if (name.size() > maxFruSdrNameSize)
529e7d23d0eSVijay Khemka     {
530e7d23d0eSVijay Khemka         name = name.substr(0, maxFruSdrNameSize);
531e7d23d0eSVijay Khemka     }
532e7d23d0eSVijay Khemka     size_t sizeDiff = maxFruSdrNameSize - name.size();
533e7d23d0eSVijay Khemka 
534e7d23d0eSVijay Khemka     resp.header.record_id_lsb = 0x0; // calling code is to implement these
535e7d23d0eSVijay Khemka     resp.header.record_id_msb = 0x0;
536e7d23d0eSVijay Khemka     resp.header.sdr_version = ipmiSdrVersion;
537e7d23d0eSVijay Khemka     resp.header.record_type = 0x11; // FRU Device Locator
538e7d23d0eSVijay Khemka     resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
539e7d23d0eSVijay Khemka     resp.key.deviceAddress = 0x20;
540e7d23d0eSVijay Khemka     resp.key.fruID = device->first;
541e7d23d0eSVijay Khemka     resp.key.accessLun = 0x80; // logical / physical fru device
542e7d23d0eSVijay Khemka     resp.key.channelNumber = 0x0;
543e7d23d0eSVijay Khemka     resp.body.reserved = 0x0;
544e7d23d0eSVijay Khemka     resp.body.deviceType = 0x10;
545e7d23d0eSVijay Khemka     resp.body.entityID = 0x0;
546e7d23d0eSVijay Khemka     resp.body.entityInstance = 0x1;
547e7d23d0eSVijay Khemka     resp.body.oem = 0x0;
548e7d23d0eSVijay Khemka     resp.body.deviceIDLen = name.size();
549e7d23d0eSVijay Khemka     name.copy(resp.body.deviceID, name.size());
550e7d23d0eSVijay Khemka 
551e7d23d0eSVijay Khemka     return IPMI_CC_OK;
552e7d23d0eSVijay Khemka }
553e7d23d0eSVijay Khemka 
ipmiStorageReserveSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)554e7d23d0eSVijay Khemka ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
555e39f9393SWilly Tu                                  ipmi_request_t, ipmi_response_t response,
556e39f9393SWilly Tu                                  ipmi_data_len_t dataLen, ipmi_context_t)
557e7d23d0eSVijay Khemka {
558e7d23d0eSVijay Khemka     printCommand(+netfn, +cmd);
559e7d23d0eSVijay Khemka 
560e7d23d0eSVijay Khemka     if (*dataLen)
561e7d23d0eSVijay Khemka     {
562e7d23d0eSVijay Khemka         *dataLen = 0;
563e7d23d0eSVijay Khemka         return IPMI_CC_REQ_DATA_LEN_INVALID;
564e7d23d0eSVijay Khemka     }
565e7d23d0eSVijay Khemka     *dataLen = 0; // default to 0 in case of an error
566e7d23d0eSVijay Khemka     sdrReservationID++;
567e7d23d0eSVijay Khemka     if (sdrReservationID == 0)
568e7d23d0eSVijay Khemka     {
569e7d23d0eSVijay Khemka         sdrReservationID++;
570e7d23d0eSVijay Khemka     }
571e7d23d0eSVijay Khemka     *dataLen = 2;
572e7d23d0eSVijay Khemka     auto resp = static_cast<uint8_t*>(response);
573e7d23d0eSVijay Khemka     resp[0] = sdrReservationID & 0xFF;
574e7d23d0eSVijay Khemka     resp[1] = sdrReservationID >> 8;
575e7d23d0eSVijay Khemka 
576e7d23d0eSVijay Khemka     return IPMI_CC_OK;
577e7d23d0eSVijay Khemka }
578e7d23d0eSVijay Khemka 
ipmiStorageGetSDR(ipmi_netfn_t netfn,ipmi_cmd_t cmd,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)579e7d23d0eSVijay Khemka ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
580e7d23d0eSVijay Khemka                              ipmi_request_t request, ipmi_response_t response,
581e39f9393SWilly Tu                              ipmi_data_len_t dataLen, ipmi_context_t)
582e7d23d0eSVijay Khemka {
583e7d23d0eSVijay Khemka     printCommand(+netfn, +cmd);
584e7d23d0eSVijay Khemka 
585e7d23d0eSVijay Khemka     if (*dataLen != 6)
586e7d23d0eSVijay Khemka     {
587e7d23d0eSVijay Khemka         *dataLen = 0;
588e7d23d0eSVijay Khemka         return IPMI_CC_REQ_DATA_LEN_INVALID;
589e7d23d0eSVijay Khemka     }
590e7d23d0eSVijay Khemka     auto requestedSize = *dataLen;
591e7d23d0eSVijay Khemka     *dataLen = 0; // default to 0 in case of an error
592e7d23d0eSVijay Khemka 
593e7d23d0eSVijay Khemka     constexpr uint16_t lastRecordIndex = 0xFFFF;
594e7d23d0eSVijay Khemka     auto req = static_cast<GetSDRReq*>(request);
595e7d23d0eSVijay Khemka 
596e7d23d0eSVijay Khemka     // reservation required for partial reads with non zero offset into
597e7d23d0eSVijay Khemka     // record
598e7d23d0eSVijay Khemka     if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
599e7d23d0eSVijay Khemka         req->offset)
600e7d23d0eSVijay Khemka     {
601e7d23d0eSVijay Khemka         return IPMI_CC_INVALID_RESERVATION_ID;
602e7d23d0eSVijay Khemka     }
603e7d23d0eSVijay Khemka 
6045a7a04daSPotin Lai     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
605e7d23d0eSVijay Khemka     {
606e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
607e7d23d0eSVijay Khemka     }
608e7d23d0eSVijay Khemka 
609e7d23d0eSVijay Khemka     size_t fruCount = 0;
610e7d23d0eSVijay Khemka     ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
611e7d23d0eSVijay Khemka     if (ret != IPMI_CC_OK)
612e7d23d0eSVijay Khemka     {
613e7d23d0eSVijay Khemka         return ret;
614e7d23d0eSVijay Khemka     }
615e7d23d0eSVijay Khemka 
616e7d23d0eSVijay Khemka     size_t lastRecord = sensorTree.size() + fruCount - 1;
617e7d23d0eSVijay Khemka     if (req->recordID == lastRecordIndex)
618e7d23d0eSVijay Khemka     {
619e7d23d0eSVijay Khemka         req->recordID = lastRecord;
620e7d23d0eSVijay Khemka     }
621e7d23d0eSVijay Khemka     if (req->recordID > lastRecord)
622e7d23d0eSVijay Khemka     {
623e7d23d0eSVijay Khemka         return IPMI_CC_INVALID_FIELD_REQUEST;
624e7d23d0eSVijay Khemka     }
625e7d23d0eSVijay Khemka 
6268713427cScchoux     uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
627e39f9393SWilly Tu                               ? req->recordID + 1
628e39f9393SWilly Tu                               : 0XFFFF;
629e7d23d0eSVijay Khemka 
630e7d23d0eSVijay Khemka     auto responseClear = static_cast<uint8_t*>(response);
631e7d23d0eSVijay Khemka     std::fill(responseClear, responseClear + requestedSize, 0);
632e7d23d0eSVijay Khemka 
633e7d23d0eSVijay Khemka     auto resp = static_cast<get_sdr::GetSdrResp*>(response);
634e7d23d0eSVijay Khemka     resp->next_record_id_lsb = nextRecord & 0xFF;
635e7d23d0eSVijay Khemka     resp->next_record_id_msb = nextRecord >> 8;
636e7d23d0eSVijay Khemka 
637e7d23d0eSVijay Khemka     if (req->recordID >= sensorTree.size())
638e7d23d0eSVijay Khemka     {
639e7d23d0eSVijay Khemka         size_t fruIndex = req->recordID - sensorTree.size();
640e7d23d0eSVijay Khemka         if (fruIndex >= fruCount)
641e7d23d0eSVijay Khemka         {
642e7d23d0eSVijay Khemka             return IPMI_CC_INVALID_FIELD_REQUEST;
643e7d23d0eSVijay Khemka         }
644e7d23d0eSVijay Khemka         get_sdr::SensorDataFruRecord data;
645e7d23d0eSVijay Khemka         if (req->offset > sizeof(data))
646e7d23d0eSVijay Khemka         {
647e7d23d0eSVijay Khemka             return IPMI_CC_INVALID_FIELD_REQUEST;
648e7d23d0eSVijay Khemka         }
649e7d23d0eSVijay Khemka         ret = ipmi::storage::getFruSdrs(fruIndex, data);
650e7d23d0eSVijay Khemka         if (ret != IPMI_CC_OK)
651e7d23d0eSVijay Khemka         {
652e7d23d0eSVijay Khemka             return ret;
653e7d23d0eSVijay Khemka         }
654e7d23d0eSVijay Khemka         data.header.record_id_msb = req->recordID << 8;
655e7d23d0eSVijay Khemka         data.header.record_id_lsb = req->recordID & 0xFF;
656e7d23d0eSVijay Khemka         if (sizeof(data) < (req->offset + req->bytesToRead))
657e7d23d0eSVijay Khemka         {
658e7d23d0eSVijay Khemka             req->bytesToRead = sizeof(data) - req->offset;
659e7d23d0eSVijay Khemka         }
660e7d23d0eSVijay Khemka         *dataLen = req->bytesToRead + 2; // next record
661e7d23d0eSVijay Khemka         std::memcpy(&resp->record_data, (char*)&data + req->offset,
662e7d23d0eSVijay Khemka                     req->bytesToRead);
663e7d23d0eSVijay Khemka         return IPMI_CC_OK;
664e7d23d0eSVijay Khemka     }
665e7d23d0eSVijay Khemka 
666e7d23d0eSVijay Khemka     std::string connection;
667e7d23d0eSVijay Khemka     std::string path;
668e7d23d0eSVijay Khemka     uint16_t sensorIndex = req->recordID;
669e7d23d0eSVijay Khemka     for (const auto& sensor : sensorTree)
670e7d23d0eSVijay Khemka     {
671e7d23d0eSVijay Khemka         if (sensorIndex-- == 0)
672e7d23d0eSVijay Khemka         {
673e7d23d0eSVijay Khemka             if (!sensor.second.size())
674e7d23d0eSVijay Khemka             {
675e7d23d0eSVijay Khemka                 return IPMI_CC_RESPONSE_ERROR;
676e7d23d0eSVijay Khemka             }
677e7d23d0eSVijay Khemka             connection = sensor.second.begin()->first;
678e7d23d0eSVijay Khemka             path = sensor.first;
679e7d23d0eSVijay Khemka             break;
680e7d23d0eSVijay Khemka         }
681e7d23d0eSVijay Khemka     }
682e7d23d0eSVijay Khemka 
683e7d23d0eSVijay Khemka     SensorMap sensorMap;
684e7d23d0eSVijay Khemka     if (!getSensorMap(connection, path, sensorMap))
685e7d23d0eSVijay Khemka     {
686e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
687e7d23d0eSVijay Khemka     }
688e7d23d0eSVijay Khemka     uint8_t sensornumber = (req->recordID & 0xFF);
689e39f9393SWilly Tu     get_sdr::SensorDataFullRecord record = {};
690e7d23d0eSVijay Khemka 
691e7d23d0eSVijay Khemka     record.header.record_id_msb = req->recordID << 8;
692e7d23d0eSVijay Khemka     record.header.record_id_lsb = req->recordID & 0xFF;
693e7d23d0eSVijay Khemka     record.header.sdr_version = ipmiSdrVersion;
694e7d23d0eSVijay Khemka     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
695e7d23d0eSVijay Khemka     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
696e7d23d0eSVijay Khemka                                   sizeof(get_sdr::SensorDataRecordHeader);
697e7d23d0eSVijay Khemka     record.key.owner_id = 0x20;
698e7d23d0eSVijay Khemka     record.key.owner_lun = 0x0;
699e7d23d0eSVijay Khemka     record.key.sensor_number = sensornumber;
700e7d23d0eSVijay Khemka 
701e7d23d0eSVijay Khemka     record.body.entity_id = 0x0;
702e7d23d0eSVijay Khemka     record.body.entity_instance = 0x01;
703e7d23d0eSVijay Khemka     record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
704e7d23d0eSVijay Khemka     record.body.sensor_type = getSensorTypeFromPath(path);
705e7d23d0eSVijay Khemka     std::string type = getSensorTypeStringFromPath(path);
706e7d23d0eSVijay Khemka     auto typeCstr = type.c_str();
707e7d23d0eSVijay Khemka     auto findUnits = sensorUnits.find(typeCstr);
708e7d23d0eSVijay Khemka     if (findUnits != sensorUnits.end())
709e7d23d0eSVijay Khemka     {
710e7d23d0eSVijay Khemka         record.body.sensor_units_2_base =
711e7d23d0eSVijay Khemka             static_cast<uint8_t>(findUnits->second);
712e7d23d0eSVijay Khemka     } // else default 0x0 unspecified
713e7d23d0eSVijay Khemka 
714e7d23d0eSVijay Khemka     record.body.event_reading_type = getSensorEventTypeFromPath(path);
715e7d23d0eSVijay Khemka 
716e7d23d0eSVijay Khemka     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
717e7d23d0eSVijay Khemka     if (sensorObject == sensorMap.end())
718e7d23d0eSVijay Khemka     {
719e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
720e7d23d0eSVijay Khemka     }
721e7d23d0eSVijay Khemka 
722e7d23d0eSVijay Khemka     auto maxObject = sensorObject->second.find("MaxValue");
723e7d23d0eSVijay Khemka     auto minObject = sensorObject->second.find("MinValue");
724e7d23d0eSVijay Khemka     double max = 128;
725e7d23d0eSVijay Khemka     double min = -127;
726e7d23d0eSVijay Khemka     if (maxObject != sensorObject->second.end())
727e7d23d0eSVijay Khemka     {
728ef0efbc4SPatrick Williams         max = std::visit(VariantToDoubleVisitor(), maxObject->second);
729e7d23d0eSVijay Khemka     }
730e7d23d0eSVijay Khemka 
731e7d23d0eSVijay Khemka     if (minObject != sensorObject->second.end())
732e7d23d0eSVijay Khemka     {
733ef0efbc4SPatrick Williams         min = std::visit(VariantToDoubleVisitor(), minObject->second);
734e7d23d0eSVijay Khemka     }
735e7d23d0eSVijay Khemka 
736e7d23d0eSVijay Khemka     int16_t mValue;
737e7d23d0eSVijay Khemka     int8_t rExp;
738e7d23d0eSVijay Khemka     int16_t bValue;
739e7d23d0eSVijay Khemka     int8_t bExp;
740e7d23d0eSVijay Khemka     bool bSigned;
741e7d23d0eSVijay Khemka 
742e7d23d0eSVijay Khemka     if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
743e7d23d0eSVijay Khemka     {
744e7d23d0eSVijay Khemka         return IPMI_CC_RESPONSE_ERROR;
745e7d23d0eSVijay Khemka     }
746e7d23d0eSVijay Khemka 
747e7d23d0eSVijay Khemka     // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
748e7d23d0eSVijay Khemka     record.body.m_lsb = mValue & 0xFF;
749e7d23d0eSVijay Khemka 
750e7d23d0eSVijay Khemka     // move the smallest bit of the MSB into place (bit 9)
751e7d23d0eSVijay Khemka     // the MSbs are bits 7:8 in m_msb_and_tolerance
752e7d23d0eSVijay Khemka     uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
753e7d23d0eSVijay Khemka 
754e7d23d0eSVijay Khemka     // assign the negative
755e7d23d0eSVijay Khemka     if (mValue < 0)
756e7d23d0eSVijay Khemka     {
757e7d23d0eSVijay Khemka         mMsb |= (1 << 7);
758e7d23d0eSVijay Khemka     }
759e7d23d0eSVijay Khemka     record.body.m_msb_and_tolerance = mMsb;
760e7d23d0eSVijay Khemka 
761e7d23d0eSVijay Khemka     record.body.b_lsb = bValue & 0xFF;
762e7d23d0eSVijay Khemka 
763e7d23d0eSVijay Khemka     // move the smallest bit of the MSB into place
764e7d23d0eSVijay Khemka     // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
765e7d23d0eSVijay Khemka     uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
766e7d23d0eSVijay Khemka 
767e7d23d0eSVijay Khemka     // assign the negative
768e7d23d0eSVijay Khemka     if (bValue < 0)
769e7d23d0eSVijay Khemka     {
770e7d23d0eSVijay Khemka         bMsb |= (1 << 7);
771e7d23d0eSVijay Khemka     }
772e7d23d0eSVijay Khemka     record.body.b_msb_and_accuracy_lsb = bMsb;
773e7d23d0eSVijay Khemka 
774e7d23d0eSVijay Khemka     record.body.r_b_exponents = bExp & 0x7;
775e7d23d0eSVijay Khemka     if (bExp < 0)
776e7d23d0eSVijay Khemka     {
777e7d23d0eSVijay Khemka         record.body.r_b_exponents |= 1 << 3;
778e7d23d0eSVijay Khemka     }
779e7d23d0eSVijay Khemka     record.body.r_b_exponents = (rExp & 0x7) << 4;
780e7d23d0eSVijay Khemka     if (rExp < 0)
781e7d23d0eSVijay Khemka     {
782e7d23d0eSVijay Khemka         record.body.r_b_exponents |= 1 << 7;
783e7d23d0eSVijay Khemka     }
784e7d23d0eSVijay Khemka 
785e7d23d0eSVijay Khemka     // todo fill out rest of units
786e7d23d0eSVijay Khemka     if (bSigned)
787e7d23d0eSVijay Khemka     {
788e7d23d0eSVijay Khemka         record.body.sensor_units_1 = 1 << 7;
789e7d23d0eSVijay Khemka     }
790e7d23d0eSVijay Khemka 
791e7d23d0eSVijay Khemka     // populate sensor name from path
792e7d23d0eSVijay Khemka     std::string name;
793e7d23d0eSVijay Khemka     size_t nameStart = path.rfind("/");
794e7d23d0eSVijay Khemka     if (nameStart != std::string::npos)
795e7d23d0eSVijay Khemka     {
796e7d23d0eSVijay Khemka         name = path.substr(nameStart + 1, std::string::npos - nameStart);
797e7d23d0eSVijay Khemka     }
798e7d23d0eSVijay Khemka 
799e7d23d0eSVijay Khemka     std::replace(name.begin(), name.end(), '_', ' ');
800e7d23d0eSVijay Khemka     if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
801e7d23d0eSVijay Khemka     {
802e7d23d0eSVijay Khemka         name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
803e7d23d0eSVijay Khemka     }
804e7d23d0eSVijay Khemka     record.body.id_string_info = name.size();
805e7d23d0eSVijay Khemka     std::strncpy(record.body.id_string, name.c_str(),
806e7d23d0eSVijay Khemka                  sizeof(record.body.id_string));
807e7d23d0eSVijay Khemka 
808e7d23d0eSVijay Khemka     if (sizeof(get_sdr::SensorDataFullRecord) <
809e7d23d0eSVijay Khemka         (req->offset + req->bytesToRead))
810e7d23d0eSVijay Khemka     {
811e7d23d0eSVijay Khemka         req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
812e7d23d0eSVijay Khemka     }
813e7d23d0eSVijay Khemka 
8142405ae98SPatrick Williams     *dataLen = 2 +
8152405ae98SPatrick Williams                req->bytesToRead; // bytesToRead + MSB and LSB of next record id
816e7d23d0eSVijay Khemka 
817e7d23d0eSVijay Khemka     std::memcpy(&resp->record_data, (char*)&record + req->offset,
818e7d23d0eSVijay Khemka                 req->bytesToRead);
819e7d23d0eSVijay Khemka 
820e7d23d0eSVijay Khemka     return IPMI_CC_OK;
821e7d23d0eSVijay Khemka }
822e7d23d0eSVijay Khemka 
getSensorConnectionByName(std::string & name,std::string & connection,std::string & path)823427b2762SVijay Khemka static int getSensorConnectionByName(std::string& name, std::string& connection,
824427b2762SVijay Khemka                                      std::string& path)
825427b2762SVijay Khemka {
8265a7a04daSPotin Lai     if (!getSensorSubtree(sensorTree) && sensorTree.empty())
827427b2762SVijay Khemka     {
828427b2762SVijay Khemka         return -1;
829427b2762SVijay Khemka     }
830427b2762SVijay Khemka 
831427b2762SVijay Khemka     for (const auto& sensor : sensorTree)
832427b2762SVijay Khemka     {
833427b2762SVijay Khemka         path = sensor.first;
834427b2762SVijay Khemka         if (path.find(name) != std::string::npos)
835427b2762SVijay Khemka         {
836427b2762SVijay Khemka             connection = sensor.second.begin()->first;
837427b2762SVijay Khemka             return 0;
838427b2762SVijay Khemka         }
839427b2762SVijay Khemka     }
840427b2762SVijay Khemka     return -1;
841427b2762SVijay Khemka }
842427b2762SVijay Khemka 
getSensorThreshold(std::string & name,std::string & thresholdStr)8432ca4aa0eSDelphine CC Chiu int getSensorThreshold(std::string& name, std::string& thresholdStr)
8442ca4aa0eSDelphine CC Chiu {
8452ca4aa0eSDelphine CC Chiu     std::string connection;
8462ca4aa0eSDelphine CC Chiu     std::string path;
8472ca4aa0eSDelphine CC Chiu     int ret = -1;
8482ca4aa0eSDelphine CC Chiu     thresholdStr = "";
8492ca4aa0eSDelphine CC Chiu 
8502ca4aa0eSDelphine CC Chiu     ret = getSensorConnectionByName(name, connection, path);
8512ca4aa0eSDelphine CC Chiu     if (ret < 0)
8522ca4aa0eSDelphine CC Chiu     {
8532ca4aa0eSDelphine CC Chiu         return ret;
8542ca4aa0eSDelphine CC Chiu     }
8552ca4aa0eSDelphine CC Chiu 
8562ca4aa0eSDelphine CC Chiu     SensorMap sensorMap;
8572ca4aa0eSDelphine CC Chiu     if (!getSensorMap(connection, path, sensorMap))
8582ca4aa0eSDelphine CC Chiu     {
8592ca4aa0eSDelphine CC Chiu         return ret;
8602ca4aa0eSDelphine CC Chiu     }
8612ca4aa0eSDelphine CC Chiu 
8622ca4aa0eSDelphine CC Chiu     // Iterate threshold interfaces with priority order
8632ca4aa0eSDelphine CC Chiu     for (auto& interface : thresholdCheckedOrder)
8642ca4aa0eSDelphine CC Chiu     {
8652ca4aa0eSDelphine CC Chiu         auto interfaceProperty = alarmProperties.find(interface);
8662ca4aa0eSDelphine CC Chiu         if (interfaceProperty == alarmProperties.end())
8672ca4aa0eSDelphine CC Chiu         {
8682ca4aa0eSDelphine CC Chiu             continue;
8692ca4aa0eSDelphine CC Chiu         }
8702ca4aa0eSDelphine CC Chiu 
8712ca4aa0eSDelphine CC Chiu         auto propertyValue = interfaceProperty->second;
8722ca4aa0eSDelphine CC Chiu 
8732ca4aa0eSDelphine CC Chiu         // Checks threshold properties value in sensorMap
8742ca4aa0eSDelphine CC Chiu         auto thresholdInterfaceSensorMap = sensorMap.find(interface);
8752ca4aa0eSDelphine CC Chiu 
8762ca4aa0eSDelphine CC Chiu         // Ignore if interface not set
8772ca4aa0eSDelphine CC Chiu         if (thresholdInterfaceSensorMap == sensorMap.end())
8782ca4aa0eSDelphine CC Chiu         {
8792ca4aa0eSDelphine CC Chiu             continue;
8802ca4aa0eSDelphine CC Chiu         }
8812ca4aa0eSDelphine CC Chiu 
8822ca4aa0eSDelphine CC Chiu         auto& thresholdMap = thresholdInterfaceSensorMap->second;
8832ca4aa0eSDelphine CC Chiu 
8842ca4aa0eSDelphine CC Chiu         auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
8852ca4aa0eSDelphine CC Chiu         auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
8862ca4aa0eSDelphine CC Chiu         if (alarmHigh != thresholdMap.end())
8872ca4aa0eSDelphine CC Chiu         {
8882ca4aa0eSDelphine CC Chiu             if (std::get<bool>(alarmHigh->second))
8892ca4aa0eSDelphine CC Chiu             {
8902ca4aa0eSDelphine CC Chiu                 thresholdStr = propertyAlarmHigh.threshold;
8912ca4aa0eSDelphine CC Chiu                 break;
8922ca4aa0eSDelphine CC Chiu             }
8932ca4aa0eSDelphine CC Chiu         }
8942ca4aa0eSDelphine CC Chiu 
8952ca4aa0eSDelphine CC Chiu         auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
8962ca4aa0eSDelphine CC Chiu         auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
8972ca4aa0eSDelphine CC Chiu         if (alarmLow != thresholdMap.end())
8982ca4aa0eSDelphine CC Chiu         {
8992ca4aa0eSDelphine CC Chiu             if (std::get<bool>(alarmLow->second))
9002ca4aa0eSDelphine CC Chiu             {
9012ca4aa0eSDelphine CC Chiu                 thresholdStr = propertyAlarmLow.threshold;
9022ca4aa0eSDelphine CC Chiu                 break;
9032ca4aa0eSDelphine CC Chiu             }
9042ca4aa0eSDelphine CC Chiu         }
9052ca4aa0eSDelphine CC Chiu     }
9062ca4aa0eSDelphine CC Chiu 
9072ca4aa0eSDelphine CC Chiu     return 0;
9082ca4aa0eSDelphine CC Chiu }
9092ca4aa0eSDelphine CC Chiu 
getSensorValue(std::string & name,double & val)910427b2762SVijay Khemka int getSensorValue(std::string& name, double& val)
911427b2762SVijay Khemka {
912427b2762SVijay Khemka     std::string connection;
913427b2762SVijay Khemka     std::string path;
914427b2762SVijay Khemka     int ret = -1;
915427b2762SVijay Khemka 
916427b2762SVijay Khemka     ret = getSensorConnectionByName(name, connection, path);
917427b2762SVijay Khemka     if (ret < 0)
918427b2762SVijay Khemka     {
919427b2762SVijay Khemka         return ret;
920427b2762SVijay Khemka     }
921427b2762SVijay Khemka 
922427b2762SVijay Khemka     SensorMap sensorMap;
923427b2762SVijay Khemka     if (!getSensorMap(connection, path, sensorMap))
924427b2762SVijay Khemka     {
925427b2762SVijay Khemka         return ret;
926427b2762SVijay Khemka     }
927427b2762SVijay Khemka     auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
928427b2762SVijay Khemka 
929427b2762SVijay Khemka     if (sensorObject == sensorMap.end() ||
930427b2762SVijay Khemka         sensorObject->second.find("Value") == sensorObject->second.end())
931427b2762SVijay Khemka     {
932427b2762SVijay Khemka         return ret;
933427b2762SVijay Khemka     }
934427b2762SVijay Khemka     auto& valueVariant = sensorObject->second["Value"];
935427b2762SVijay Khemka     val = std::visit(VariantToDoubleVisitor(), valueVariant);
936427b2762SVijay Khemka 
937427b2762SVijay Khemka     return 0;
938427b2762SVijay Khemka }
939427b2762SVijay Khemka 
94058bd5d84SVijay Khemka const static boost::container::flat_map<const char*, std::string, CmpStr>
94158bd5d84SVijay Khemka     sensorUnitStr{{{"temperature", "C"},
94258bd5d84SVijay Khemka                    {"voltage", "V"},
94358bd5d84SVijay Khemka                    {"current", "mA"},
94458bd5d84SVijay Khemka                    {"fan_tach", "RPM"},
94558bd5d84SVijay Khemka                    {"fan_pwm", "RPM"},
94658bd5d84SVijay Khemka                    {"power", "W"}}};
94758bd5d84SVijay Khemka 
getSensorUnit(std::string & name,std::string & unit)94858bd5d84SVijay Khemka int getSensorUnit(std::string& name, std::string& unit)
94958bd5d84SVijay Khemka {
95058bd5d84SVijay Khemka     std::string connection;
95158bd5d84SVijay Khemka     std::string path;
95258bd5d84SVijay Khemka     int ret = -1;
95358bd5d84SVijay Khemka 
95458bd5d84SVijay Khemka     ret = getSensorConnectionByName(name, connection, path);
95558bd5d84SVijay Khemka     if (ret < 0)
95658bd5d84SVijay Khemka     {
95758bd5d84SVijay Khemka         return ret;
95858bd5d84SVijay Khemka     }
95958bd5d84SVijay Khemka 
96058bd5d84SVijay Khemka     std::string sensorTypeStr = getSensorTypeStringFromPath(path);
96158bd5d84SVijay Khemka     auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
96258bd5d84SVijay Khemka     if (findSensor != sensorUnitStr.end())
96358bd5d84SVijay Khemka     {
96458bd5d84SVijay Khemka         unit = findSensor->second;
96558bd5d84SVijay Khemka         return 0;
96658bd5d84SVijay Khemka     }
96758bd5d84SVijay Khemka     else
96858bd5d84SVijay Khemka         return -1;
96958bd5d84SVijay Khemka }
97058bd5d84SVijay Khemka 
ipmiStorageGetFRUInvAreaInfo(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t dataLen,ipmi_context_t)971*010dee04SPatrick Williams ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
972*010dee04SPatrick Williams     ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
973e39f9393SWilly Tu     ipmi_data_len_t dataLen, ipmi_context_t)
97417cf1349SVijay Khemka {
97517cf1349SVijay Khemka     if (*dataLen != 1)
97617cf1349SVijay Khemka     {
97717cf1349SVijay Khemka         *dataLen = 0;
97817cf1349SVijay Khemka         return IPMI_CC_REQ_DATA_LEN_INVALID;
97917cf1349SVijay Khemka     }
98017cf1349SVijay Khemka     *dataLen = 0; // default to 0 in case of an error
98117cf1349SVijay Khemka 
98217cf1349SVijay Khemka     uint8_t reqDev = *(static_cast<uint8_t*>(request));
98317cf1349SVijay Khemka     if (reqDev == 0xFF)
98417cf1349SVijay Khemka     {
98517cf1349SVijay Khemka         return IPMI_CC_INVALID_FIELD_REQUEST;
98617cf1349SVijay Khemka     }
98717cf1349SVijay Khemka     ipmi_ret_t status = replaceCacheFru(reqDev);
98817cf1349SVijay Khemka 
98917cf1349SVijay Khemka     if (status != IPMI_CC_OK)
99017cf1349SVijay Khemka     {
99117cf1349SVijay Khemka         return status;
99217cf1349SVijay Khemka     }
99317cf1349SVijay Khemka 
99417cf1349SVijay Khemka     GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
99517cf1349SVijay Khemka     respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
99617cf1349SVijay Khemka     respPtr->inventorySizeMSB = fruCache.size() >> 8;
99717cf1349SVijay Khemka     respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
99817cf1349SVijay Khemka 
99917cf1349SVijay Khemka     *dataLen = sizeof(GetFRUAreaResp);
100017cf1349SVijay Khemka     return IPMI_CC_OK;
100117cf1349SVijay Khemka }
100217cf1349SVijay Khemka 
registerStorageFunctions()1003e7d23d0eSVijay Khemka void registerStorageFunctions()
1004e7d23d0eSVijay Khemka {
100517cf1349SVijay Khemka     // <Get FRU Inventory Area Info>
100617cf1349SVijay Khemka     ipmiPrintAndRegister(
100717cf1349SVijay Khemka         NETFUN_STORAGE,
100817cf1349SVijay Khemka         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
100917cf1349SVijay Khemka         NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
101017cf1349SVijay Khemka 
1011e7d23d0eSVijay Khemka     // <READ FRU Data>
1012e7d23d0eSVijay Khemka     ipmiPrintAndRegister(
1013e7d23d0eSVijay Khemka         NETFUN_STORAGE,
1014e7d23d0eSVijay Khemka         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1015e7d23d0eSVijay Khemka         ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1016e7d23d0eSVijay Khemka 
1017e7d23d0eSVijay Khemka     // <WRITE FRU Data>
1018e7d23d0eSVijay Khemka     ipmiPrintAndRegister(
1019e7d23d0eSVijay Khemka         NETFUN_STORAGE,
1020e7d23d0eSVijay Khemka         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1021e7d23d0eSVijay Khemka         NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1022e7d23d0eSVijay Khemka 
1023e7d23d0eSVijay Khemka     // <Reserve SDR Repo>
1024e7d23d0eSVijay Khemka     ipmiPrintAndRegister(
1025e7d23d0eSVijay Khemka         NETFUN_STORAGE,
1026e7d23d0eSVijay Khemka         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1027e7d23d0eSVijay Khemka         nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1028e7d23d0eSVijay Khemka 
1029e7d23d0eSVijay Khemka     // <Get Sdr>
1030e7d23d0eSVijay Khemka     ipmiPrintAndRegister(
1031e7d23d0eSVijay Khemka         NETFUN_STORAGE,
1032e7d23d0eSVijay Khemka         static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1033e7d23d0eSVijay Khemka         ipmiStorageGetSDR, PRIVILEGE_USER);
1034e7d23d0eSVijay Khemka     return;
1035e7d23d0eSVijay Khemka }
1036e7d23d0eSVijay Khemka } // namespace storage
1037e7d23d0eSVijay Khemka } // namespace ipmi
1038