blob: 0a725bc877ae807f747afdbd361ad0dc27695b73 [file] [log] [blame]
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001/*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <ipmid/api.h>
19
20#include <boost/container/flat_map.hpp>
21#include <commandutils.hpp>
Vijay Khemka1b6fae32019-03-25 17:43:01 -070022#include <ipmid/utils.hpp>
Vijay Khemkae7d23d02019-03-08 13:13:40 -080023#include <phosphor-logging/log.hpp>
24#include <sdbusplus/message/types.hpp>
25#include <sdbusplus/timer.hpp>
26#include <sensorutils.hpp>
27#include <storagecommands.hpp>
28
Vijay Khemka63c99be2020-05-27 19:14:35 -070029#include <iostream>
Patrick Williams2405ae92023-05-10 07:50:09 -050030#include <unordered_map>
Vijay Khemka63c99be2020-05-27 19:14:35 -070031
Vijay Khemkae7d23d02019-03-08 13:13:40 -080032namespace ipmi
33{
34
35namespace storage
36{
37void registerStorageFunctions() __attribute__((constructor));
38
39constexpr static const size_t maxMessageSize = 64;
40constexpr static const size_t maxFruSdrNameSize = 16;
41static constexpr int sensorMapUpdatePeriod = 2;
42using SensorMap = std::map<std::string, std::map<std::string, DbusVariant>>;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080043
44using ManagedObjectSensor =
45 std::map<sdbusplus::message::object_path,
46 std::map<std::string, std::map<std::string, DbusVariant>>>;
47
48static uint16_t sdrReservationID;
49
50static boost::container::flat_map<std::string, ManagedObjectSensor> SensorCache;
51static SensorSubTree sensorTree;
52
53void registerSensorFunctions() __attribute__((constructor));
54using ManagedObjectType = boost::container::flat_map<
55 sdbusplus::message::object_path,
56 boost::container::flat_map<
57 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
58using ManagedEntry = std::pair<
59 sdbusplus::message::object_path,
60 boost::container::flat_map<
61 std::string, boost::container::flat_map<std::string, DbusVariant>>>;
62
Vijay Khemka63c99be2020-05-27 19:14:35 -070063constexpr static const char* fruDeviceServiceName =
Vijay Khemkae7d23d02019-03-08 13:13:40 -080064 "xyz.openbmc_project.FruDevice";
65constexpr static const size_t cacheTimeoutSeconds = 10;
66
67static std::vector<uint8_t> fruCache;
Potin Lai4de58762023-09-01 15:56:00 +080068static uint16_t cacheBus = 0xFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080069static uint8_t cacheAddr = 0XFF;
70
Patrick Williamsd79f89e2023-12-05 12:45:03 -060071std::unique_ptr<sdbusplus::Timer> cacheTimer = nullptr;
Vijay Khemkae7d23d02019-03-08 13:13:40 -080072
73// we unfortunately have to build a map of hashes in case there is a
74// collision to verify our dev-id
75boost::container::flat_map<uint8_t, std::pair<uint8_t, uint8_t>> deviceHashes;
76
Patrick Williamscd315e02022-07-22 19:26:52 -050077static sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection());
Vijay Khemkae7d23d02019-03-08 13:13:40 -080078
Delphine CC Chiu2ca4aa02023-02-01 16:30:18 +080079using InterfaceName = std::string;
80using PropertyName = std::string;
81using ThresholdStr = std::string;
82
83enum class AlarmType
84{
85 low,
86 high
87};
88
89struct Property
90{
91 PropertyName name;
92 ThresholdStr threshold;
93};
94
95const std::vector<InterfaceName> thresholdCheckedOrder{
96 "xyz.openbmc_project.Sensor.Threshold.HardShutdown",
97 "xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
98 "xyz.openbmc_project.Sensor.Threshold.Critical",
99 "xyz.openbmc_project.Sensor.Threshold.Warning"};
100
101const std::unordered_map<std::string, std::map<AlarmType, Property>>
102 alarmProperties{
103 {"xyz.openbmc_project.Sensor.Threshold.HardShutdown",
104 {{AlarmType::low, Property{"HardShutdownAlarmLow", "LNR"}},
105 {AlarmType::high, Property{"HardShutdownAlarmHigh", "UNR"}}}},
106
107 {"xyz.openbmc_project.Sensor.Threshold.SoftShutdown",
108 {{AlarmType::low, Property{"SoftShutdownAlarmLow", "LNR"}},
109 {AlarmType::high, Property{"SoftShutdownAlarmHigh", "UNR"}}}},
110
111 {"xyz.openbmc_project.Sensor.Threshold.Critical",
112 {{AlarmType::low, Property{"CriticalAlarmLow", "LCR"}},
113 {AlarmType::high, Property{"CriticalAlarmHigh", "UCR"}}}},
114
115 {"xyz.openbmc_project.Sensor.Threshold.Warning",
116 {{AlarmType::low, Property{"WarningAlarmLow", "LNC"}},
117 {AlarmType::high, Property{"WarningAlarmHigh", "UNC"}}}},
118 };
119
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800120static bool getSensorMap(std::string sensorConnection, std::string sensorPath,
Vijay Khemka63c99be2020-05-27 19:14:35 -0700121 SensorMap& sensorMap)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800122{
123 static boost::container::flat_map<
124 std::string, std::chrono::time_point<std::chrono::steady_clock>>
125 updateTimeMap;
126
127 auto updateFind = updateTimeMap.find(sensorConnection);
128 auto lastUpdate = std::chrono::time_point<std::chrono::steady_clock>();
129 if (updateFind != updateTimeMap.end())
130 {
131 lastUpdate = updateFind->second;
132 }
133
134 auto now = std::chrono::steady_clock::now();
135
136 if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate)
137 .count() > sensorMapUpdatePeriod)
138 {
139 updateTimeMap[sensorConnection] = now;
140
141 auto managedObj = dbus.new_method_call(
Potin Lai8ee95d62022-12-21 11:11:59 +0800142 sensorConnection.c_str(), "/xyz/openbmc_project/sensors",
143 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800144
145 ManagedObjectSensor managedObjects;
146 try
147 {
148 auto reply = dbus.call(managedObj);
149 reply.read(managedObjects);
150 }
Patrick Williams35d12542021-10-06 11:21:13 -0500151 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800152 {
153 phosphor::logging::log<phosphor::logging::level::ERR>(
154 "Error getting managed objects from connection",
155 phosphor::logging::entry("CONNECTION=%s",
156 sensorConnection.c_str()));
157 return false;
158 }
159
160 SensorCache[sensorConnection] = managedObjects;
161 }
162 auto connection = SensorCache.find(sensorConnection);
163 if (connection == SensorCache.end())
164 {
165 return false;
166 }
167 auto path = connection->second.find(sensorPath);
168 if (path == connection->second.end())
169 {
170 return false;
171 }
172 sensorMap = path->second;
173
174 return true;
175}
176
177bool writeFru()
178{
Patrick Williamscd315e02022-07-22 19:26:52 -0500179 sdbusplus::message_t writeFru = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800180 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
181 "xyz.openbmc_project.FruDeviceManager", "WriteFru");
182 writeFru.append(cacheBus, cacheAddr, fruCache);
183 try
184 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500185 sdbusplus::message_t writeFruResp = dbus.call(writeFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800186 }
Patrick Williams35d12542021-10-06 11:21:13 -0500187 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800188 {
189 // todo: log sel?
190 phosphor::logging::log<phosphor::logging::level::ERR>(
191 "error writing fru");
192 return false;
193 }
194 return true;
195}
196
197void createTimer()
198{
199 if (cacheTimer == nullptr)
200 {
Patrick Williamsd79f89e2023-12-05 12:45:03 -0600201 cacheTimer = std::make_unique<sdbusplus::Timer>(writeFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800202 }
203}
204
205ipmi_ret_t replaceCacheFru(uint8_t devId)
206{
207 static uint8_t lastDevId = 0xFF;
208
209 bool timerRunning = (cacheTimer != nullptr) && !cacheTimer->isExpired();
210 if (lastDevId == devId && timerRunning)
211 {
George Liu2f454992025-07-02 16:43:57 +0800212 return ipmi::ccSuccess; // cache already up to date
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800213 }
214 // if timer is running, stop it and writeFru manually
215 else if (timerRunning)
216 {
217 cacheTimer->stop();
218 writeFru();
219 }
220
Patrick Williamscd315e02022-07-22 19:26:52 -0500221 sdbusplus::message_t getObjects = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800222 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
223 "GetManagedObjects");
224 ManagedObjectType frus;
225 try
226 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500227 sdbusplus::message_t resp = dbus.call(getObjects);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800228 resp.read(frus);
229 }
Patrick Williams35d12542021-10-06 11:21:13 -0500230 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800231 {
232 phosphor::logging::log<phosphor::logging::level::ERR>(
233 "replaceCacheFru: error getting managed objects");
George Liu2f454992025-07-02 16:43:57 +0800234 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800235 }
236
237 deviceHashes.clear();
238
cchouxb2ae88b2023-09-13 00:35:36 +0800239 uint8_t fruHash = 0;
240 uint8_t mbFruBus = 0, mbFruAddr = 0;
241
242 auto device = getMbFruDevice();
243 if (device)
244 {
245 std::tie(mbFruBus, mbFruAddr) = *device;
246 deviceHashes.emplace(0, std::make_pair(mbFruBus, mbFruAddr));
247 fruHash++;
248 }
249
Vijay Khemka63c99be2020-05-27 19:14:35 -0700250 for (const auto& fru : frus)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800251 {
252 auto fruIface = fru.second.find("xyz.openbmc_project.FruDevice");
253 if (fruIface == fru.second.end())
254 {
255 continue;
256 }
257
258 auto busFind = fruIface->second.find("BUS");
259 auto addrFind = fruIface->second.find("ADDRESS");
260 if (busFind == fruIface->second.end() ||
261 addrFind == fruIface->second.end())
262 {
263 phosphor::logging::log<phosphor::logging::level::INFO>(
264 "fru device missing Bus or Address",
265 phosphor::logging::entry("FRU=%s", fru.first.str.c_str()));
266 continue;
267 }
268
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500269 uint8_t fruBus = std::get<uint32_t>(busFind->second);
270 uint8_t fruAddr = std::get<uint32_t>(addrFind->second);
cchouxb2ae88b2023-09-13 00:35:36 +0800271 if (fruBus != mbFruBus || fruAddr != mbFruAddr)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800272 {
cchouxb2ae88b2023-09-13 00:35:36 +0800273 deviceHashes.emplace(fruHash, std::make_pair(fruBus, fruAddr));
274 fruHash++;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800275 }
276 }
277 auto deviceFind = deviceHashes.find(devId);
278 if (deviceFind == deviceHashes.end())
279 {
George Liu2f454992025-07-02 16:43:57 +0800280 return ipmi::ccSensorInvalid;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800281 }
282
283 fruCache.clear();
Patrick Williamscd315e02022-07-22 19:26:52 -0500284 sdbusplus::message_t getRawFru = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800285 fruDeviceServiceName, "/xyz/openbmc_project/FruDevice",
286 "xyz.openbmc_project.FruDeviceManager", "GetRawFru");
287 cacheBus = deviceFind->second.first;
288 cacheAddr = deviceFind->second.second;
289 getRawFru.append(cacheBus, cacheAddr);
290 try
291 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500292 sdbusplus::message_t getRawResp = dbus.call(getRawFru);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800293 getRawResp.read(fruCache);
294 }
Patrick Williams35d12542021-10-06 11:21:13 -0500295 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800296 {
297 lastDevId = 0xFF;
Potin Lai4de58762023-09-01 15:56:00 +0800298 cacheBus = 0xFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800299 cacheAddr = 0xFF;
George Liu2f454992025-07-02 16:43:57 +0800300 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800301 }
302
303 lastDevId = devId;
George Liu2f454992025-07-02 16:43:57 +0800304 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800305}
306
Patrick Williams010dee02024-08-16 15:19:44 -0400307ipmi_ret_t ipmiStorageReadFRUData(
308 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
309 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800310{
311 if (*dataLen != 4)
312 {
313 *dataLen = 0;
George Liu2f454992025-07-02 16:43:57 +0800314 return ipmi::ccReqDataLenInvalid;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800315 }
316 *dataLen = 0; // default to 0 in case of an error
317
Vijay Khemka63c99be2020-05-27 19:14:35 -0700318 auto req = static_cast<GetFRUAreaReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800319
320 if (req->countToRead > maxMessageSize - 1)
321 {
George Liu2f454992025-07-02 16:43:57 +0800322 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800323 }
324 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
325
George Liu2f454992025-07-02 16:43:57 +0800326 if (status != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800327 {
328 return status;
329 }
330
331 size_t fromFRUByteLen = 0;
332 if (req->countToRead + req->fruInventoryOffset < fruCache.size())
333 {
334 fromFRUByteLen = req->countToRead;
335 }
336 else if (fruCache.size() > req->fruInventoryOffset)
337 {
338 fromFRUByteLen = fruCache.size() - req->fruInventoryOffset;
339 }
340 size_t padByteLen = req->countToRead - fromFRUByteLen;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700341 uint8_t* respPtr = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800342 *respPtr = req->countToRead;
343 std::copy(fruCache.begin() + req->fruInventoryOffset,
344 fruCache.begin() + req->fruInventoryOffset + fromFRUByteLen,
345 ++respPtr);
346 // if longer than the fru is requested, fill with 0xFF
347 if (padByteLen)
348 {
349 respPtr += fromFRUByteLen;
350 std::fill(respPtr, respPtr + padByteLen, 0xFF);
351 }
352 *dataLen = fromFRUByteLen + 1;
353
George Liu2f454992025-07-02 16:43:57 +0800354 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800355}
356
Patrick Williams010dee02024-08-16 15:19:44 -0400357ipmi_ret_t ipmiStorageWriteFRUData(
358 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
359 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800360{
361 if (*dataLen < 4 ||
362 *dataLen >=
363 0xFF + 3) // count written return is one byte, so limit to one
364 // byte of data after the three request data bytes
365 {
366 *dataLen = 0;
George Liu2f454992025-07-02 16:43:57 +0800367 return ipmi::ccReqDataLenInvalid;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800368 }
369
Vijay Khemka63c99be2020-05-27 19:14:35 -0700370 auto req = static_cast<WriteFRUDataReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800371 size_t writeLen = *dataLen - 3;
372 *dataLen = 0; // default to 0 in case of an error
373
374 ipmi_ret_t status = replaceCacheFru(req->fruDeviceID);
George Liu2f454992025-07-02 16:43:57 +0800375 if (status != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800376 {
377 return status;
378 }
Willy Tue39f9392022-06-15 13:24:20 -0700379 size_t lastWriteAddr = req->fruInventoryOffset + writeLen;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800380 if (fruCache.size() < lastWriteAddr)
381 {
382 fruCache.resize(req->fruInventoryOffset + writeLen);
383 }
384
385 std::copy(req->data, req->data + writeLen,
386 fruCache.begin() + req->fruInventoryOffset);
387
388 bool atEnd = false;
389
390 if (fruCache.size() >= sizeof(FRUHeader))
391 {
Vijay Khemka63c99be2020-05-27 19:14:35 -0700392 FRUHeader* header = reinterpret_cast<FRUHeader*>(fruCache.data());
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800393
Willy Tue39f9392022-06-15 13:24:20 -0700394 size_t lastRecordStart = std::max(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800395 header->internalOffset,
396 std::max(header->chassisOffset,
397 std::max(header->boardOffset, header->productOffset)));
398 // TODO: Handle Multi-Record FRUs?
399
400 lastRecordStart *= 8; // header starts in are multiples of 8 bytes
401
402 // get the length of the area in multiples of 8 bytes
403 if (lastWriteAddr > (lastRecordStart + 1))
404 {
405 // second byte in record area is the length
406 int areaLength(fruCache[lastRecordStart + 1]);
407 areaLength *= 8; // it is in multiples of 8 bytes
408
409 if (lastWriteAddr >= (areaLength + lastRecordStart))
410 {
411 atEnd = true;
412 }
413 }
414 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700415 uint8_t* respPtr = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800416 if (atEnd)
417 {
418 // cancel timer, we're at the end so might as well send it
419 cacheTimer->stop();
420 if (!writeFru())
421 {
George Liu2f454992025-07-02 16:43:57 +0800422 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800423 }
424 *respPtr = std::min(fruCache.size(), static_cast<size_t>(0xFF));
425 }
426 else
427 {
428 // start a timer, if no further data is sent in cacheTimeoutSeconds
429 // seconds, check to see if it is valid
430 createTimer();
431 cacheTimer->start(std::chrono::duration_cast<std::chrono::microseconds>(
432 std::chrono::seconds(cacheTimeoutSeconds)));
433 *respPtr = 0;
434 }
435
436 *dataLen = 1;
437
George Liu2f454992025-07-02 16:43:57 +0800438 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800439}
440
Vijay Khemka63c99be2020-05-27 19:14:35 -0700441ipmi_ret_t getFruSdrCount(size_t& count)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800442{
443 ipmi_ret_t ret = replaceCacheFru(0);
George Liu2f454992025-07-02 16:43:57 +0800444 if (ret != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800445 {
446 return ret;
447 }
448 count = deviceHashes.size();
George Liu2f454992025-07-02 16:43:57 +0800449 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800450}
451
Vijay Khemka63c99be2020-05-27 19:14:35 -0700452ipmi_ret_t getFruSdrs(size_t index, get_sdr::SensorDataFruRecord& resp)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800453{
454 ipmi_ret_t ret = replaceCacheFru(0); // this will update the hash list
George Liu2f454992025-07-02 16:43:57 +0800455 if (ret != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800456 {
457 return ret;
458 }
459 if (deviceHashes.size() < index)
460 {
George Liu2f454992025-07-02 16:43:57 +0800461 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800462 }
463 auto device = deviceHashes.begin() + index;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700464 uint8_t& bus = device->second.first;
465 uint8_t& address = device->second.second;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800466
467 ManagedObjectType frus;
468
Patrick Williamscd315e02022-07-22 19:26:52 -0500469 sdbusplus::message_t getObjects = dbus.new_method_call(
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800470 fruDeviceServiceName, "/", "org.freedesktop.DBus.ObjectManager",
471 "GetManagedObjects");
472 try
473 {
Patrick Williamscd315e02022-07-22 19:26:52 -0500474 sdbusplus::message_t resp = dbus.call(getObjects);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800475 resp.read(frus);
476 }
Patrick Williams35d12542021-10-06 11:21:13 -0500477 catch (const sdbusplus::exception_t&)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800478 {
George Liu2f454992025-07-02 16:43:57 +0800479 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800480 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700481 boost::container::flat_map<std::string, DbusVariant>* fruData = nullptr;
Patrick Williams010dee02024-08-16 15:19:44 -0400482 auto fru = std::find_if(
483 frus.begin(), frus.end(),
484 [bus, address, &fruData](ManagedEntry& entry) {
485 auto findFruDevice =
486 entry.second.find("xyz.openbmc_project.FruDevice");
487 if (findFruDevice == entry.second.end())
488 {
489 return false;
490 }
491 fruData = &(findFruDevice->second);
492 auto findBus = findFruDevice->second.find("BUS");
493 auto findAddress = findFruDevice->second.find("ADDRESS");
494 if (findBus == findFruDevice->second.end() ||
495 findAddress == findFruDevice->second.end())
496 {
497 return false;
498 }
499 if (std::get<uint32_t>(findBus->second) != bus)
500 {
501 return false;
502 }
503 if (std::get<uint32_t>(findAddress->second) != address)
504 {
505 return false;
506 }
507 return true;
508 });
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800509 if (fru == frus.end())
510 {
George Liu2f454992025-07-02 16:43:57 +0800511 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800512 }
513 std::string name;
514 auto findProductName = fruData->find("BOARD_PRODUCT_NAME");
515 auto findBoardName = fruData->find("PRODUCT_PRODUCT_NAME");
516 if (findProductName != fruData->end())
517 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500518 name = std::get<std::string>(findProductName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800519 }
520 else if (findBoardName != fruData->end())
521 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500522 name = std::get<std::string>(findBoardName->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800523 }
524 else
525 {
526 name = "UNKNOWN";
527 }
528 if (name.size() > maxFruSdrNameSize)
529 {
530 name = name.substr(0, maxFruSdrNameSize);
531 }
532 size_t sizeDiff = maxFruSdrNameSize - name.size();
533
534 resp.header.record_id_lsb = 0x0; // calling code is to implement these
535 resp.header.record_id_msb = 0x0;
536 resp.header.sdr_version = ipmiSdrVersion;
537 resp.header.record_type = 0x11; // FRU Device Locator
538 resp.header.record_length = sizeof(resp.body) + sizeof(resp.key) - sizeDiff;
539 resp.key.deviceAddress = 0x20;
540 resp.key.fruID = device->first;
541 resp.key.accessLun = 0x80; // logical / physical fru device
542 resp.key.channelNumber = 0x0;
543 resp.body.reserved = 0x0;
544 resp.body.deviceType = 0x10;
Alex Wangab565bf2025-10-13 11:42:26 +0800545 resp.body.deviceTypeModifier = 0x00;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800546 resp.body.entityID = 0x0;
547 resp.body.entityInstance = 0x1;
548 resp.body.oem = 0x0;
549 resp.body.deviceIDLen = name.size();
550 name.copy(resp.body.deviceID, name.size());
551
George Liu2f454992025-07-02 16:43:57 +0800552 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800553}
554
555ipmi_ret_t ipmiStorageReserveSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Willy Tue39f9392022-06-15 13:24:20 -0700556 ipmi_request_t, ipmi_response_t response,
557 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800558{
559 printCommand(+netfn, +cmd);
560
561 if (*dataLen)
562 {
563 *dataLen = 0;
George Liu2f454992025-07-02 16:43:57 +0800564 return ipmi::ccReqDataLenInvalid;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800565 }
566 *dataLen = 0; // default to 0 in case of an error
567 sdrReservationID++;
568 if (sdrReservationID == 0)
569 {
570 sdrReservationID++;
571 }
572 *dataLen = 2;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700573 auto resp = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800574 resp[0] = sdrReservationID & 0xFF;
575 resp[1] = sdrReservationID >> 8;
576
George Liu2f454992025-07-02 16:43:57 +0800577 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800578}
579
580ipmi_ret_t ipmiStorageGetSDR(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
581 ipmi_request_t request, ipmi_response_t response,
Willy Tue39f9392022-06-15 13:24:20 -0700582 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800583{
584 printCommand(+netfn, +cmd);
585
586 if (*dataLen != 6)
587 {
588 *dataLen = 0;
George Liu2f454992025-07-02 16:43:57 +0800589 return ipmi::ccReqDataLenInvalid;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800590 }
591 auto requestedSize = *dataLen;
592 *dataLen = 0; // default to 0 in case of an error
593
594 constexpr uint16_t lastRecordIndex = 0xFFFF;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700595 auto req = static_cast<GetSDRReq*>(request);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800596
597 // reservation required for partial reads with non zero offset into
598 // record
599 if ((sdrReservationID == 0 || req->reservationID != sdrReservationID) &&
600 req->offset)
601 {
George Liu2f454992025-07-02 16:43:57 +0800602 return ipmi::ccInvalidReservationId;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800603 }
604
Potin Lai5a7a04d2024-01-10 15:25:21 +0800605 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800606 {
George Liu2f454992025-07-02 16:43:57 +0800607 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800608 }
609
610 size_t fruCount = 0;
611 ipmi_ret_t ret = ipmi::storage::getFruSdrCount(fruCount);
George Liu2f454992025-07-02 16:43:57 +0800612 if (ret != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800613 {
614 return ret;
615 }
616
617 size_t lastRecord = sensorTree.size() + fruCount - 1;
618 if (req->recordID == lastRecordIndex)
619 {
620 req->recordID = lastRecord;
621 }
622 if (req->recordID > lastRecord)
623 {
George Liu2f454992025-07-02 16:43:57 +0800624 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800625 }
626
cchoux87134272023-10-06 15:20:23 +0800627 uint16_t nextRecord = lastRecord >= static_cast<size_t>(req->recordID + 1)
Willy Tue39f9392022-06-15 13:24:20 -0700628 ? req->recordID + 1
629 : 0XFFFF;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800630
Vijay Khemka63c99be2020-05-27 19:14:35 -0700631 auto responseClear = static_cast<uint8_t*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800632 std::fill(responseClear, responseClear + requestedSize, 0);
633
Vijay Khemka63c99be2020-05-27 19:14:35 -0700634 auto resp = static_cast<get_sdr::GetSdrResp*>(response);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800635 resp->next_record_id_lsb = nextRecord & 0xFF;
636 resp->next_record_id_msb = nextRecord >> 8;
637
638 if (req->recordID >= sensorTree.size())
639 {
640 size_t fruIndex = req->recordID - sensorTree.size();
641 if (fruIndex >= fruCount)
642 {
George Liu2f454992025-07-02 16:43:57 +0800643 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800644 }
645 get_sdr::SensorDataFruRecord data;
646 if (req->offset > sizeof(data))
647 {
George Liu2f454992025-07-02 16:43:57 +0800648 return ipmi::ccInvalidFieldRequest;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800649 }
650 ret = ipmi::storage::getFruSdrs(fruIndex, data);
George Liu2f454992025-07-02 16:43:57 +0800651 if (ret != ipmi::ccSuccess)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800652 {
653 return ret;
654 }
655 data.header.record_id_msb = req->recordID << 8;
656 data.header.record_id_lsb = req->recordID & 0xFF;
657 if (sizeof(data) < (req->offset + req->bytesToRead))
658 {
659 req->bytesToRead = sizeof(data) - req->offset;
660 }
661 *dataLen = req->bytesToRead + 2; // next record
Vijay Khemka63c99be2020-05-27 19:14:35 -0700662 std::memcpy(&resp->record_data, (char*)&data + req->offset,
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800663 req->bytesToRead);
George Liu2f454992025-07-02 16:43:57 +0800664 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800665 }
666
667 std::string connection;
668 std::string path;
669 uint16_t sensorIndex = req->recordID;
Vijay Khemka63c99be2020-05-27 19:14:35 -0700670 for (const auto& sensor : sensorTree)
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800671 {
672 if (sensorIndex-- == 0)
673 {
674 if (!sensor.second.size())
675 {
George Liu2f454992025-07-02 16:43:57 +0800676 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800677 }
678 connection = sensor.second.begin()->first;
679 path = sensor.first;
680 break;
681 }
682 }
683
684 SensorMap sensorMap;
685 if (!getSensorMap(connection, path, sensorMap))
686 {
George Liu2f454992025-07-02 16:43:57 +0800687 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800688 }
689 uint8_t sensornumber = (req->recordID & 0xFF);
Willy Tue39f9392022-06-15 13:24:20 -0700690 get_sdr::SensorDataFullRecord record = {};
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800691
692 record.header.record_id_msb = req->recordID << 8;
693 record.header.record_id_lsb = req->recordID & 0xFF;
694 record.header.sdr_version = ipmiSdrVersion;
695 record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
696 record.header.record_length = sizeof(get_sdr::SensorDataFullRecord) -
697 sizeof(get_sdr::SensorDataRecordHeader);
698 record.key.owner_id = 0x20;
699 record.key.owner_lun = 0x0;
700 record.key.sensor_number = sensornumber;
701
702 record.body.entity_id = 0x0;
703 record.body.entity_instance = 0x01;
704 record.body.sensor_capabilities = 0x60; // auto rearm - todo hysteresis
705 record.body.sensor_type = getSensorTypeFromPath(path);
706 std::string type = getSensorTypeStringFromPath(path);
707 auto typeCstr = type.c_str();
708 auto findUnits = sensorUnits.find(typeCstr);
709 if (findUnits != sensorUnits.end())
710 {
711 record.body.sensor_units_2_base =
712 static_cast<uint8_t>(findUnits->second);
713 } // else default 0x0 unspecified
714
715 record.body.event_reading_type = getSensorEventTypeFromPath(path);
716
717 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
718 if (sensorObject == sensorMap.end())
719 {
George Liu2f454992025-07-02 16:43:57 +0800720 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800721 }
722
723 auto maxObject = sensorObject->second.find("MaxValue");
724 auto minObject = sensorObject->second.find("MinValue");
725 double max = 128;
726 double min = -127;
727 if (maxObject != sensorObject->second.end())
728 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500729 max = std::visit(VariantToDoubleVisitor(), maxObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800730 }
731
732 if (minObject != sensorObject->second.end())
733 {
Patrick Williamsef0efbc2020-05-13 11:26:51 -0500734 min = std::visit(VariantToDoubleVisitor(), minObject->second);
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800735 }
736
737 int16_t mValue;
738 int8_t rExp;
739 int16_t bValue;
740 int8_t bExp;
741 bool bSigned;
742
743 if (!getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned))
744 {
George Liu2f454992025-07-02 16:43:57 +0800745 return ipmi::ccResponseError;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800746 }
747
748 // apply M, B, and exponents, M and B are 10 bit values, exponents are 4
749 record.body.m_lsb = mValue & 0xFF;
750
751 // move the smallest bit of the MSB into place (bit 9)
752 // the MSbs are bits 7:8 in m_msb_and_tolerance
753 uint8_t mMsb = (mValue & (1 << 8)) > 0 ? (1 << 6) : 0;
754
755 // assign the negative
756 if (mValue < 0)
757 {
758 mMsb |= (1 << 7);
759 }
760 record.body.m_msb_and_tolerance = mMsb;
761
762 record.body.b_lsb = bValue & 0xFF;
763
764 // move the smallest bit of the MSB into place
765 // the MSbs are bits 7:8 in b_msb_and_accuracy_lsb
766 uint8_t bMsb = (bValue & (1 << 8)) > 0 ? (1 << 6) : 0;
767
768 // assign the negative
769 if (bValue < 0)
770 {
771 bMsb |= (1 << 7);
772 }
773 record.body.b_msb_and_accuracy_lsb = bMsb;
774
775 record.body.r_b_exponents = bExp & 0x7;
776 if (bExp < 0)
777 {
778 record.body.r_b_exponents |= 1 << 3;
779 }
780 record.body.r_b_exponents = (rExp & 0x7) << 4;
781 if (rExp < 0)
782 {
783 record.body.r_b_exponents |= 1 << 7;
784 }
785
786 // todo fill out rest of units
787 if (bSigned)
788 {
789 record.body.sensor_units_1 = 1 << 7;
790 }
791
792 // populate sensor name from path
793 std::string name;
794 size_t nameStart = path.rfind("/");
795 if (nameStart != std::string::npos)
796 {
797 name = path.substr(nameStart + 1, std::string::npos - nameStart);
798 }
799
800 std::replace(name.begin(), name.end(), '_', ' ');
801 if (name.size() > FULL_RECORD_ID_STR_MAX_LENGTH)
802 {
803 name.resize(FULL_RECORD_ID_STR_MAX_LENGTH);
804 }
805 record.body.id_string_info = name.size();
806 std::strncpy(record.body.id_string, name.c_str(),
807 sizeof(record.body.id_string));
808
809 if (sizeof(get_sdr::SensorDataFullRecord) <
810 (req->offset + req->bytesToRead))
811 {
812 req->bytesToRead = sizeof(get_sdr::SensorDataFullRecord) - req->offset;
813 }
814
Patrick Williams2405ae92023-05-10 07:50:09 -0500815 *dataLen = 2 +
816 req->bytesToRead; // bytesToRead + MSB and LSB of next record id
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800817
Vijay Khemka63c99be2020-05-27 19:14:35 -0700818 std::memcpy(&resp->record_data, (char*)&record + req->offset,
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800819 req->bytesToRead);
820
George Liu2f454992025-07-02 16:43:57 +0800821 return ipmi::ccSuccess;
Vijay Khemkae7d23d02019-03-08 13:13:40 -0800822}
823
Vijay Khemka63c99be2020-05-27 19:14:35 -0700824static int getSensorConnectionByName(std::string& name, std::string& connection,
825 std::string& path)
Vijay Khemka427b2762019-12-12 12:49:25 -0800826{
Potin Lai5a7a04d2024-01-10 15:25:21 +0800827 if (!getSensorSubtree(sensorTree) && sensorTree.empty())
Vijay Khemka427b2762019-12-12 12:49:25 -0800828 {
829 return -1;
830 }
831
Vijay Khemka63c99be2020-05-27 19:14:35 -0700832 for (const auto& sensor : sensorTree)
Vijay Khemka427b2762019-12-12 12:49:25 -0800833 {
834 path = sensor.first;
835 if (path.find(name) != std::string::npos)
836 {
837 connection = sensor.second.begin()->first;
838 return 0;
839 }
840 }
841 return -1;
842}
843
Delphine CC Chiu2ca4aa02023-02-01 16:30:18 +0800844int getSensorThreshold(std::string& name, std::string& thresholdStr)
845{
846 std::string connection;
847 std::string path;
848 int ret = -1;
849 thresholdStr = "";
850
851 ret = getSensorConnectionByName(name, connection, path);
852 if (ret < 0)
853 {
854 return ret;
855 }
856
857 SensorMap sensorMap;
858 if (!getSensorMap(connection, path, sensorMap))
859 {
860 return ret;
861 }
862
863 // Iterate threshold interfaces with priority order
864 for (auto& interface : thresholdCheckedOrder)
865 {
866 auto interfaceProperty = alarmProperties.find(interface);
867 if (interfaceProperty == alarmProperties.end())
868 {
869 continue;
870 }
871
872 auto propertyValue = interfaceProperty->second;
873
874 // Checks threshold properties value in sensorMap
875 auto thresholdInterfaceSensorMap = sensorMap.find(interface);
876
877 // Ignore if interface not set
878 if (thresholdInterfaceSensorMap == sensorMap.end())
879 {
880 continue;
881 }
882
883 auto& thresholdMap = thresholdInterfaceSensorMap->second;
884
885 auto& propertyAlarmHigh = propertyValue.at(AlarmType::high);
886 auto alarmHigh = thresholdMap.find(propertyAlarmHigh.name);
887 if (alarmHigh != thresholdMap.end())
888 {
889 if (std::get<bool>(alarmHigh->second))
890 {
891 thresholdStr = propertyAlarmHigh.threshold;
892 break;
893 }
894 }
895
896 auto& propertyAlarmLow = propertyValue.at(AlarmType::low);
897 auto alarmLow = thresholdMap.find(propertyAlarmLow.name);
898 if (alarmLow != thresholdMap.end())
899 {
900 if (std::get<bool>(alarmLow->second))
901 {
902 thresholdStr = propertyAlarmLow.threshold;
903 break;
904 }
905 }
906 }
907
908 return 0;
909}
910
Vijay Khemka63c99be2020-05-27 19:14:35 -0700911int getSensorValue(std::string& name, double& val)
Vijay Khemka427b2762019-12-12 12:49:25 -0800912{
913 std::string connection;
914 std::string path;
915 int ret = -1;
916
917 ret = getSensorConnectionByName(name, connection, path);
918 if (ret < 0)
919 {
920 return ret;
921 }
922
923 SensorMap sensorMap;
924 if (!getSensorMap(connection, path, sensorMap))
925 {
926 return ret;
927 }
928 auto sensorObject = sensorMap.find("xyz.openbmc_project.Sensor.Value");
929
930 if (sensorObject == sensorMap.end() ||
931 sensorObject->second.find("Value") == sensorObject->second.end())
932 {
933 return ret;
934 }
Vijay Khemka63c99be2020-05-27 19:14:35 -0700935 auto& valueVariant = sensorObject->second["Value"];
Vijay Khemka427b2762019-12-12 12:49:25 -0800936 val = std::visit(VariantToDoubleVisitor(), valueVariant);
937
938 return 0;
939}
940
Vijay Khemka63c99be2020-05-27 19:14:35 -0700941const static boost::container::flat_map<const char*, std::string, CmpStr>
Vijay Khemka58bd5d82019-12-13 11:05:56 -0800942 sensorUnitStr{{{"temperature", "C"},
943 {"voltage", "V"},
944 {"current", "mA"},
945 {"fan_tach", "RPM"},
946 {"fan_pwm", "RPM"},
947 {"power", "W"}}};
948
Vijay Khemka63c99be2020-05-27 19:14:35 -0700949int getSensorUnit(std::string& name, std::string& unit)
Vijay Khemka58bd5d82019-12-13 11:05:56 -0800950{
951 std::string connection;
952 std::string path;
953 int ret = -1;
954
955 ret = getSensorConnectionByName(name, connection, path);
956 if (ret < 0)
957 {
958 return ret;
959 }
960
961 std::string sensorTypeStr = getSensorTypeStringFromPath(path);
962 auto findSensor = sensorUnitStr.find(sensorTypeStr.c_str());
963 if (findSensor != sensorUnitStr.end())
964 {
965 unit = findSensor->second;
966 return 0;
967 }
968 else
969 return -1;
970}
971
Patrick Williams010dee02024-08-16 15:19:44 -0400972ipmi_ret_t ipmiStorageGetFRUInvAreaInfo(
973 ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t response,
974 ipmi_data_len_t dataLen, ipmi_context_t)
Vijay Khemka17cf1342020-11-02 14:44:42 -0800975{
976 if (*dataLen != 1)
977 {
978 *dataLen = 0;
George Liu2f454992025-07-02 16:43:57 +0800979 return ipmi::ccReqDataLenInvalid;
Vijay Khemka17cf1342020-11-02 14:44:42 -0800980 }
981 *dataLen = 0; // default to 0 in case of an error
982
983 uint8_t reqDev = *(static_cast<uint8_t*>(request));
984 if (reqDev == 0xFF)
985 {
George Liu2f454992025-07-02 16:43:57 +0800986 return ipmi::ccInvalidFieldRequest;
Vijay Khemka17cf1342020-11-02 14:44:42 -0800987 }
988 ipmi_ret_t status = replaceCacheFru(reqDev);
989
George Liu2f454992025-07-02 16:43:57 +0800990 if (status != ipmi::ccSuccess)
Vijay Khemka17cf1342020-11-02 14:44:42 -0800991 {
992 return status;
993 }
994
995 GetFRUAreaResp* respPtr = static_cast<GetFRUAreaResp*>(response);
996 respPtr->inventorySizeLSB = fruCache.size() & 0xFF;
997 respPtr->inventorySizeMSB = fruCache.size() >> 8;
998 respPtr->accessType = static_cast<uint8_t>(GetFRUAreaAccessType::byte);
999
1000 *dataLen = sizeof(GetFRUAreaResp);
George Liu2f454992025-07-02 16:43:57 +08001001 return ipmi::ccSuccess;
Vijay Khemka17cf1342020-11-02 14:44:42 -08001002}
1003
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001004void registerStorageFunctions()
1005{
Vijay Khemka17cf1342020-11-02 14:44:42 -08001006 // <Get FRU Inventory Area Info>
1007 ipmiPrintAndRegister(
George Liu0821a2e2025-04-03 10:12:10 +08001008 ipmi::netFnStorage,
Vijay Khemka17cf1342020-11-02 14:44:42 -08001009 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetFRUInvAreaInfo),
1010 NULL, ipmiStorageGetFRUInvAreaInfo, PRIVILEGE_OPERATOR);
1011
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001012 // <READ FRU Data>
1013 ipmiPrintAndRegister(
George Liu0821a2e2025-04-03 10:12:10 +08001014 ipmi::netFnStorage,
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001015 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReadFRUData), NULL,
1016 ipmiStorageReadFRUData, PRIVILEGE_OPERATOR);
1017
1018 // <WRITE FRU Data>
1019 ipmiPrintAndRegister(
George Liu0821a2e2025-04-03 10:12:10 +08001020 ipmi::netFnStorage,
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001021 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdWriteFRUData),
1022 NULL, ipmiStorageWriteFRUData, PRIVILEGE_OPERATOR);
1023
1024 // <Reserve SDR Repo>
1025 ipmiPrintAndRegister(
George Liu0821a2e2025-04-03 10:12:10 +08001026 ipmi::netFnStorage,
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001027 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdReserveSDR),
1028 nullptr, ipmiStorageReserveSDR, PRIVILEGE_USER);
1029
1030 // <Get Sdr>
1031 ipmiPrintAndRegister(
George Liu0821a2e2025-04-03 10:12:10 +08001032 ipmi::netFnStorage,
Vijay Khemkae7d23d02019-03-08 13:13:40 -08001033 static_cast<ipmi_cmd_t>(IPMINetfnStorageCmds::ipmiCmdGetSDR), nullptr,
1034 ipmiStorageGetSDR, PRIVILEGE_USER);
1035 return;
1036}
1037} // namespace storage
1038} // namespace ipmi