blob: 3f09940886feafedd0fcd017cbb66333c05344ae [file] [log] [blame]
Patrick Venture3a5071a2018-09-12 13:27:42 -07001#include "storagehandler.hpp"
2
3#include "fruread.hpp"
4#include "read_fru_data.hpp"
5#include "selutility.hpp"
6#include "sensorhandler.hpp"
7#include "storageaddsel.hpp"
Patrick Venture3a5071a2018-09-12 13:27:42 -07008
Lei YU52d91242017-10-17 22:52:28 +08009#include <arpa/inet.h>
Patrick Venture3a5071a2018-09-12 13:27:42 -070010#include <mapper.h>
11#include <systemd/sd-bus.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070012
13#include <algorithm>
Lei YU52d91242017-10-17 22:52:28 +080014#include <chrono>
15#include <cstdio>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -070016#include <cstring>
Vernon Mauerybdda8002019-02-26 10:18:51 -080017#include <filesystem>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070018#include <ipmid/api.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070019#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070020#include <phosphor-logging/elog-errors.hpp>
21#include <phosphor-logging/log.hpp>
22#include <sdbusplus/server.hpp>
23#include <string>
Vernon Mauery16b86932019-05-01 08:36:11 -070024#include <variant>
Patrick Venture3a5071a2018-09-12 13:27:42 -070025#include <xyz/openbmc_project/Common/error.hpp>
26
Chris Austenb4f5b922015-10-13 12:44:43 -050027void register_netfn_storage_functions() __attribute__((constructor));
28
Patrick Venture0b02be92018-08-31 11:55:55 -070029unsigned int g_sel_time = 0xFFFFFFFF;
Patrick Venturedb0cbe62019-09-09 14:47:22 -070030namespace ipmi
31{
32namespace sensor
33{
34extern const IdInfoMap sensors;
35} // namespace sensor
36} // namespace ipmi
37
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -060038extern const FruMap frus;
anil kumar appana2c7db1d2019-05-28 11:20:19 +000039constexpr uint8_t eventDataSize = 3;
Patrick Venture0b02be92018-08-31 11:55:55 -070040namespace
41{
Lei YUe8939392017-06-15 10:45:05 +080042constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
George Liu4d623e92020-05-25 16:51:57 +080043constexpr auto BMC_TIME_PATH = "/xyz/openbmc_project/time/bmc";
Lei YUe8939392017-06-15 10:45:05 +080044constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
Patrick Venture0b02be92018-08-31 11:55:55 -070045constexpr auto PROPERTY_ELAPSED = "Elapsed";
Lei YUe8939392017-06-15 10:45:05 +080046
Patrick Venture0b02be92018-08-31 11:55:55 -070047} // namespace
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +053048
Tom Joseph6f7deaa2017-06-30 19:03:54 +053049namespace cache
50{
Patrick Venture0b02be92018-08-31 11:55:55 -070051/*
52 * This cache contains the object paths of the logging entries sorted in the
53 * order of the filename(numeric order). The cache is initialized by
54 * invoking readLoggingObjectPaths with the cache as the parameter. The
55 * cache is invoked in the execution of the Get SEL info and Delete SEL
56 * entry command. The Get SEL Info command is typically invoked before the
57 * Get SEL entry command, so the cache is utilized for responding to Get SEL
58 * entry command. The cache is invalidated by clearing after Delete SEL
59 * entry and Clear SEL command.
60 */
61ipmi::sel::ObjectPaths paths;
Tom Joseph6f7deaa2017-06-30 19:03:54 +053062
Patrick Venture0b02be92018-08-31 11:55:55 -070063} // namespace cache
Tom Joseph6f7deaa2017-06-30 19:03:54 +053064
65using InternalFailure =
Patrick Venture0b02be92018-08-31 11:55:55 -070066 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Tom Joseph6f7deaa2017-06-30 19:03:54 +053067using namespace phosphor::logging;
Marri Devender Raocac383b2017-07-03 13:24:27 -050068using namespace ipmi::fru;
69
70/**
71 * @enum Device access mode
72 */
73enum class AccessMode
74{
75 bytes, ///< Device is accessed by bytes
76 words ///< Device is accessed by words
77};
78
jayaprakash Mutyalab7557722019-05-02 21:13:30 +000079/** @brief implements the get SEL Info command
80 * @returns IPMI completion code plus response data
81 * - selVersion - SEL revision
82 * - entries - Number of log entries in SEL.
83 * - freeSpace - Free Space in bytes.
84 * - addTimeStamp - Most recent addition timestamp
85 * - eraseTimeStamp - Most recent erase timestamp
86 * - operationSupport - Reserve & Delete SEL operations supported
87 */
88
89ipmi::RspType<uint8_t, // SEL revision.
90 uint16_t, // number of log entries in SEL.
91 uint16_t, // free Space in bytes.
92 uint32_t, // most recent addition timestamp
93 uint32_t, // most recent erase timestamp.
94
95 bool, // SEL allocation info supported
96 bool, // reserve SEL supported
97 bool, // partial Add SEL Entry supported
98 bool, // delete SEL supported
99 uint3_t, // reserved
100 bool // overflow flag
101 >
102 ipmiStorageGetSelInfo()
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530103{
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000104 uint16_t entries = 0;
105 // Most recent addition timestamp.
106 uint32_t addTimeStamp = ipmi::sel::invalidTimeStamp;
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530107
Tom Josephe59abfb2018-08-06 18:46:27 +0530108 try
109 {
110 ipmi::sel::readLoggingObjectPaths(cache::paths);
111 }
112 catch (const sdbusplus::exception::SdBusError& e)
113 {
114 // No action if reading log objects have failed for this command.
115 // readLoggingObjectPaths will throw exception if there are no log
116 // entries. The command will be responded with number of SEL entries
117 // as 0.
118 }
119
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530120 if (!cache::paths.empty())
121 {
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000122 entries = static_cast<uint16_t>(cache::paths.size());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530123
124 try
125 {
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000126 addTimeStamp = static_cast<uint32_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700127 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530128 }
129 catch (InternalFailure& e)
130 {
131 }
132 catch (const std::runtime_error& e)
133 {
134 log<level::ERR>(e.what());
135 }
136 }
137
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000138 constexpr uint8_t selVersion = ipmi::sel::selVersion;
139 constexpr uint16_t freeSpace = 0xFFFF;
140 constexpr uint32_t eraseTimeStamp = ipmi::sel::invalidTimeStamp;
141 constexpr uint3_t reserved{0};
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530142
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000143 return ipmi::responseSuccess(
144 selVersion, entries, freeSpace, addTimeStamp, eraseTimeStamp,
145 ipmi::sel::operationSupport::getSelAllocationInfo,
146 ipmi::sel::operationSupport::reserveSel,
147 ipmi::sel::operationSupport::partialAddSelEntry,
148 ipmi::sel::operationSupport::deleteSel, reserved,
149 ipmi::sel::operationSupport::overflow);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530150}
151
Tom Josepha4953392017-06-30 19:09:47 +0530152ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
153 ipmi_request_t request, ipmi_response_t response,
154 ipmi_data_len_t data_len, ipmi_context_t context)
155{
Jason M. Bills851acb12019-02-04 14:06:57 -0800156 if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
157 {
158 *data_len = 0;
159 return IPMI_CC_REQ_DATA_LEN_INVALID;
160 }
161
Patrick Venture0b02be92018-08-31 11:55:55 -0700162 auto requestData =
163 reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
Tom Josepha4953392017-06-30 19:09:47 +0530164
165 if (requestData->reservationID != 0)
166 {
Jason M. Bills13e67c82018-09-10 14:12:16 -0700167 if (!checkSELReservation(requestData->reservationID))
Tom Josepha4953392017-06-30 19:09:47 +0530168 {
169 *data_len = 0;
170 return IPMI_CC_INVALID_RESERVATION_ID;
171 }
172 }
173
174 if (cache::paths.empty())
175 {
176 *data_len = 0;
177 return IPMI_CC_SENSOR_INVALID;
178 }
179
180 ipmi::sel::ObjectPaths::const_iterator iter;
181
182 // Check for the requested SEL Entry.
183 if (requestData->selRecordID == ipmi::sel::firstEntry)
184 {
185 iter = cache::paths.begin();
186 }
187 else if (requestData->selRecordID == ipmi::sel::lastEntry)
188 {
189 iter = cache::paths.end();
190 }
191 else
192 {
193 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
194 std::to_string(requestData->selRecordID);
195
196 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
197 if (iter == cache::paths.end())
198 {
199 *data_len = 0;
200 return IPMI_CC_SENSOR_INVALID;
201 }
202 }
203
Patrick Venture0b02be92018-08-31 11:55:55 -0700204 ipmi::sel::GetSELEntryResponse record{};
Tom Josepha4953392017-06-30 19:09:47 +0530205
206 // Convert the log entry into SEL record.
207 try
208 {
209 record = ipmi::sel::convertLogEntrytoSEL(*iter);
210 }
211 catch (InternalFailure& e)
212 {
213 *data_len = 0;
214 return IPMI_CC_UNSPECIFIED_ERROR;
215 }
216 catch (const std::runtime_error& e)
217 {
218 log<level::ERR>(e.what());
219 *data_len = 0;
220 return IPMI_CC_UNSPECIFIED_ERROR;
221 }
222
Tom Josepha4953392017-06-30 19:09:47 +0530223 // Identify the next SEL record ID
Patrick Venture0b02be92018-08-31 11:55:55 -0700224 if (iter != cache::paths.end())
Tom Josepha4953392017-06-30 19:09:47 +0530225 {
226 ++iter;
227 if (iter == cache::paths.end())
228 {
229 record.nextRecordID = ipmi::sel::lastEntry;
230 }
231 else
232 {
Vernon Mauery185b9f82018-07-20 10:52:36 -0700233 namespace fs = std::filesystem;
Tom Josepha4953392017-06-30 19:09:47 +0530234 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700235 record.nextRecordID = static_cast<uint16_t>(
236 std::stoul(std::string(path.filename().c_str())));
Tom Josepha4953392017-06-30 19:09:47 +0530237 }
238 }
239 else
240 {
241 record.nextRecordID = ipmi::sel::lastEntry;
242 }
243
244 if (requestData->readLength == ipmi::sel::entireRecord)
245 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700246 std::memcpy(response, &record, sizeof(record));
Tom Josepha4953392017-06-30 19:09:47 +0530247 *data_len = sizeof(record);
248 }
249 else
250 {
251 if (requestData->offset >= ipmi::sel::selRecordSize ||
252 requestData->readLength > ipmi::sel::selRecordSize)
253 {
254 *data_len = 0;
255 return IPMI_CC_INVALID_FIELD_REQUEST;
256 }
257
258 auto diff = ipmi::sel::selRecordSize - requestData->offset;
Patrick Venture0b02be92018-08-31 11:55:55 -0700259 auto readLength =
260 std::min(diff, static_cast<int>(requestData->readLength));
Tom Josepha4953392017-06-30 19:09:47 +0530261
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700262 std::memcpy(response, &record.nextRecordID,
263 sizeof(record.nextRecordID));
264 std::memcpy(static_cast<uint8_t*>(response) +
265 sizeof(record.nextRecordID),
Lei YUaf378fa2020-12-02 16:28:57 +0800266 &record.event.eventRecord.recordID + requestData->offset,
267 readLength);
Tom Josepha4953392017-06-30 19:09:47 +0530268 *data_len = sizeof(record.nextRecordID) + readLength;
269 }
270
271 return IPMI_CC_OK;
272}
273
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000274/** @brief implements the delete SEL entry command
275 * @request
276 * - reservationID; // reservation ID.
277 * - selRecordID; // SEL record ID.
278 *
279 * @returns ipmi completion code plus response data
280 * - Record ID of the deleted record
281 */
282ipmi::RspType<uint16_t // deleted record ID
283 >
284 deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530285{
Jason M. Bills851acb12019-02-04 14:06:57 -0800286
Brad Bishop1a4117b2018-11-21 15:48:18 -0500287 namespace fs = std::filesystem;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530288
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000289 if (!checkSELReservation(reservationID))
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530290 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000291 return ipmi::responseInvalidReservationId();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530292 }
293
Jason M. Bills13e67c82018-09-10 14:12:16 -0700294 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
295 // deleted
296 cancelSELReservation();
297
Tom Josephe59abfb2018-08-06 18:46:27 +0530298 try
299 {
300 ipmi::sel::readLoggingObjectPaths(cache::paths);
301 }
302 catch (const sdbusplus::exception::SdBusError& e)
303 {
304 // readLoggingObjectPaths will throw exception if there are no error
305 // log entries.
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000306 return ipmi::responseSensorInvalid();
Tom Josephe59abfb2018-08-06 18:46:27 +0530307 }
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530308
309 if (cache::paths.empty())
310 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000311 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530312 }
313
314 ipmi::sel::ObjectPaths::const_iterator iter;
315 uint16_t delRecordID = 0;
316
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000317 if (selRecordID == ipmi::sel::firstEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530318 {
319 iter = cache::paths.begin();
320 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700321 delRecordID = static_cast<uint16_t>(
322 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530323 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000324 else if (selRecordID == ipmi::sel::lastEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530325 {
326 iter = cache::paths.end();
327 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700328 delRecordID = static_cast<uint16_t>(
329 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530330 }
331 else
332 {
333 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000334 std::to_string(selRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530335
336 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
337 if (iter == cache::paths.end())
338 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000339 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530340 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000341 delRecordID = selRecordID;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530342 }
343
344 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
345 std::string service;
346
347 try
348 {
349 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter);
350 }
351 catch (const std::runtime_error& e)
352 {
353 log<level::ERR>(e.what());
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000354 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530355 }
356
Patrick Venture0b02be92018-08-31 11:55:55 -0700357 auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
358 ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530359 auto reply = bus.call(methodCall);
360 if (reply.is_method_error())
361 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000362 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530363 }
364
365 // Invalidate the cache of dbus entry objects.
366 cache::paths.clear();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530367
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000368 return ipmi::responseSuccess(delRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530369}
370
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000371/** @brief implements the Clear SEL command
372 * @request
373 * - reservationID // Reservation ID.
374 * - clr // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) }
375 * - eraseOperation; // requested operation.
376 *
377 * @returns ipmi completion code plus response data
378 * - erase status
379 */
380
381ipmi::RspType<uint8_t // erase status
382 >
383 clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
384 uint8_t eraseOperation)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530385{
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000386 static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
387 if (clr != clrOk)
Jason M. Bills851acb12019-02-04 14:06:57 -0800388 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000389 return ipmi::responseInvalidFieldRequest();
Jason M. Bills851acb12019-02-04 14:06:57 -0800390 }
391
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000392 if (!checkSELReservation(reservationID))
Tom Joseph2f05bb52017-06-30 19:14:49 +0530393 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000394 return ipmi::responseInvalidReservationId();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530395 }
396
Tom Joseph2f05bb52017-06-30 19:14:49 +0530397 /*
398 * Erasure status cannot be fetched from DBUS, so always return erasure
399 * status as `erase completed`.
400 */
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000401 if (eraseOperation == ipmi::sel::getEraseStatus)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530402 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000403 return ipmi::responseSuccess(
404 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530405 }
406
Jason M. Bills13e67c82018-09-10 14:12:16 -0700407 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
408 cancelSELReservation();
409
Tom Joseph2f05bb52017-06-30 19:14:49 +0530410 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
Tom Josephe59abfb2018-08-06 18:46:27 +0530411 ipmi::sel::ObjectPaths objectPaths;
Tom Joseph2f05bb52017-06-30 19:14:49 +0530412 auto depth = 0;
413
Patrick Venture0b02be92018-08-31 11:55:55 -0700414 auto mapperCall =
415 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
416 ipmi::sel::mapperIntf, "GetSubTreePaths");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530417 mapperCall.append(ipmi::sel::logBasePath);
418 mapperCall.append(depth);
419 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
420
Tom Josephe59abfb2018-08-06 18:46:27 +0530421 try
Tom Joseph2f05bb52017-06-30 19:14:49 +0530422 {
Tom Josephe59abfb2018-08-06 18:46:27 +0530423 auto reply = bus.call(mapperCall);
424 if (reply.is_method_error())
425 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000426 return ipmi::responseSuccess(
427 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530428 }
Tom Joseph2f05bb52017-06-30 19:14:49 +0530429
Tom Josephe59abfb2018-08-06 18:46:27 +0530430 reply.read(objectPaths);
431 if (objectPaths.empty())
432 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000433 return ipmi::responseSuccess(
434 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530435 }
436 }
437 catch (const sdbusplus::exception::SdBusError& e)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530438 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000439 return ipmi::responseSuccess(
440 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530441 }
442
443 std::string service;
444
445 try
446 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700447 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
Tom Joseph2f05bb52017-06-30 19:14:49 +0530448 objectPaths.front());
449 }
450 catch (const std::runtime_error& e)
451 {
452 log<level::ERR>(e.what());
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000453 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530454 }
455
456 for (const auto& iter : objectPaths)
457 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700458 auto methodCall = bus.new_method_call(
459 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530460
461 auto reply = bus.call(methodCall);
462 if (reply.is_method_error())
463 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000464 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530465 }
466 }
467
468 // Invalidate the cache of dbus entry objects.
469 cache::paths.clear();
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000470 return ipmi::responseSuccess(
471 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530472}
473
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000474/** @brief implements the get SEL time command
475 * @returns IPMI completion code plus response data
476 * -current time
477 */
478ipmi::RspType<uint32_t> // current time
479 ipmiStorageGetSelTime()
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500480{
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530481 using namespace std::chrono;
George Liu4d623e92020-05-25 16:51:57 +0800482 uint64_t bmc_time_usec = 0;
483 std::stringstream bmcTime;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500484
Lei YUe8939392017-06-15 10:45:05 +0800485 try
486 {
487 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
George Liu4d623e92020-05-25 16:51:57 +0800488 auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH);
Vernon Mauery16b86932019-05-01 08:36:11 -0700489 std::variant<uint64_t> value;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530490
George Liu4d623e92020-05-25 16:51:57 +0800491 // Get bmc time
492 auto method = bus.new_method_call(service.c_str(), BMC_TIME_PATH,
Patrick Venture0b02be92018-08-31 11:55:55 -0700493 DBUS_PROPERTIES, "Get");
Lei YUe8939392017-06-15 10:45:05 +0800494
495 method.append(TIME_INTERFACE, PROPERTY_ELAPSED);
496 auto reply = bus.call(method);
497 if (reply.is_method_error())
498 {
499 log<level::ERR>("Error getting time",
500 entry("SERVICE=%s", service.c_str()),
George Liu4d623e92020-05-25 16:51:57 +0800501 entry("PATH=%s", BMC_TIME_PATH));
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000502 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800503 }
504 reply.read(value);
George Liu4d623e92020-05-25 16:51:57 +0800505 bmc_time_usec = std::get<uint64_t>(value);
Lei YUe8939392017-06-15 10:45:05 +0800506 }
507 catch (InternalFailure& e)
508 {
509 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000510 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800511 }
Tom Joseph30fd0a12019-09-24 06:53:22 +0530512 catch (const std::exception& e)
Lei YUe8939392017-06-15 10:45:05 +0800513 {
514 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000515 return ipmi::responseUnspecifiedError();
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530516 }
517
George Liu4d623e92020-05-25 16:51:57 +0800518 bmcTime << "BMC time:"
519 << duration_cast<seconds>(microseconds(bmc_time_usec)).count();
520 log<level::DEBUG>(bmcTime.str().c_str());
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500521
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530522 // Time is really long int but IPMI wants just uint32. This works okay until
523 // the number of seconds since 1970 overflows uint32 size.. Still a whole
524 // lot of time here to even think about that.
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000525 return ipmi::responseSuccess(
George Liu4d623e92020-05-25 16:51:57 +0800526 duration_cast<seconds>(microseconds(bmc_time_usec)).count());
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500527}
528
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000529/** @brief implements the set SEL time command
530 * @param selDeviceTime - epoch time
531 * -local time as the number of seconds from 00:00:00, January 1, 1970
532 * @returns IPMI completion code
533 */
534ipmi::RspType<> ipmiStorageSetSelTime(uint32_t selDeviceTime)
Chris Austenb4f5b922015-10-13 12:44:43 -0500535{
Lei YUe8939392017-06-15 10:45:05 +0800536 using namespace std::chrono;
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000537 microseconds usec{seconds(selDeviceTime)};
Norman James82330442015-11-19 16:53:26 -0600538
Lei YUe8939392017-06-15 10:45:05 +0800539 try
540 {
541 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
George Liu4d623e92020-05-25 16:51:57 +0800542 auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH);
Andrew Geissler6467ed22020-05-16 16:03:53 -0500543 std::variant<uint64_t> value{(uint64_t)usec.count()};
Lei YUe8939392017-06-15 10:45:05 +0800544
George Liu4d623e92020-05-25 16:51:57 +0800545 // Set bmc time
546 auto method = bus.new_method_call(service.c_str(), BMC_TIME_PATH,
Patrick Venture0b02be92018-08-31 11:55:55 -0700547 DBUS_PROPERTIES, "Set");
Lei YUe8939392017-06-15 10:45:05 +0800548
549 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
550 auto reply = bus.call(method);
551 if (reply.is_method_error())
552 {
553 log<level::ERR>("Error setting time",
554 entry("SERVICE=%s", service.c_str()),
George Liu4d623e92020-05-25 16:51:57 +0800555 entry("PATH=%s", BMC_TIME_PATH));
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000556 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800557 }
Norman James82330442015-11-19 16:53:26 -0600558 }
Lei YUe8939392017-06-15 10:45:05 +0800559 catch (InternalFailure& e)
560 {
561 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000562 return ipmi::responseUnspecifiedError();
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530563 }
Tom Joseph30fd0a12019-09-24 06:53:22 +0530564 catch (const std::exception& e)
Lei YUe8939392017-06-15 10:45:05 +0800565 {
566 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000567 return ipmi::responseUnspecifiedError();
Norman James82330442015-11-19 16:53:26 -0600568 }
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530569
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000570 return ipmi::responseSuccess();
Chris Austenb4f5b922015-10-13 12:44:43 -0500571}
572
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000573/** @brief implements the reserve SEL command
574 * @returns IPMI completion code plus response data
575 * - SEL reservation ID.
576 */
577ipmi::RspType<uint16_t> ipmiStorageReserveSel()
Chris Austenb4f5b922015-10-13 12:44:43 -0500578{
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000579 return ipmi::responseSuccess(reserveSel());
Chris Austenb4f5b922015-10-13 12:44:43 -0500580}
581
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000582/** @brief implements the Add SEL entry command
583 * @request
584 *
585 * - recordID ID used for SEL Record access
586 * - recordType Record Type
587 * - timeStamp Time when event was logged. LS byte first
588 * - generatorID software ID if event was generated from
589 * system software
590 * - evmRev event message format version
591 * - sensorType sensor type code for service that generated
592 * the event
593 * - sensorNumber number of sensors that generated the event
594 * - eventDir event dir
595 * - eventData event data field contents
596 *
597 * @returns ipmi completion code plus response data
598 * - RecordID of the Added SEL entry
599 */
600ipmi::RspType<uint16_t // recordID of the Added SEL entry
601 >
602 ipmiStorageAddSEL(uint16_t recordID, uint8_t recordType, uint32_t timeStamp,
603 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType,
604 uint8_t sensorNumber, uint8_t eventDir,
605 std::array<uint8_t, eventDataSize> eventData)
Chris Austenb4f5b922015-10-13 12:44:43 -0500606{
Jason M. Bills13e67c82018-09-10 14:12:16 -0700607 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
608 // added
609 cancelSELReservation();
Tom Josephb647d5b2017-10-31 17:25:33 +0530610 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
611 // a maintenance procedure associated with eSEL record.
612 static constexpr auto procedureType = 0xDE;
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000613 if (recordType == procedureType)
Tom Josephb647d5b2017-10-31 17:25:33 +0530614 {
615 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
616 // procedure number.
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000617 createProcedureLogEntry(sensorType);
Tom Josephb647d5b2017-10-31 17:25:33 +0530618 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500619
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000620 return ipmi::responseSuccess(recordID);
Chris Austenb4f5b922015-10-13 12:44:43 -0500621}
622
Kirill Pakhomovfa6e2092020-04-24 18:57:15 +0300623bool isFruPresent(const std::string& fruPath)
624{
625 using namespace ipmi::fru;
626
627 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
628
629 auto propValue =
630 ipmi::getDbusProperty(bus, invMgrInterface, invObjPath + fruPath,
631 invItemInterface, itemPresentProp);
632
633 return std::get<bool>(propValue);
634}
635
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000636/** @brief implements the get FRU Inventory Area Info command
637 *
638 * @returns IPMI completion code plus response data
639 * - FRU Inventory area size in bytes,
640 * - access bit
641 **/
642ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
643 uint8_t // access size (bytes / words)
644 >
645 ipmiStorageGetFruInvAreaInfo(uint8_t fruID)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500646{
Tom Joseph187f5642018-03-29 13:49:06 +0530647
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000648 auto iter = frus.find(fruID);
Tom Joseph187f5642018-03-29 13:49:06 +0530649 if (iter == frus.end())
650 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000651 return ipmi::responseSensorInvalid();
Tom Joseph187f5642018-03-29 13:49:06 +0530652 }
653
Kirill Pakhomovfa6e2092020-04-24 18:57:15 +0300654 auto path = iter->second[0].path;
655 if (!isFruPresent(path))
656 {
657 return ipmi::responseSensorInvalid();
658 }
659
Marri Devender Raocac383b2017-07-03 13:24:27 -0500660 try
661 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000662 return ipmi::responseSuccess(
663 static_cast<uint16_t>(getFruAreaData(fruID).size()),
664 static_cast<uint8_t>(AccessMode::bytes));
Marri Devender Raocac383b2017-07-03 13:24:27 -0500665 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700666 catch (const InternalFailure& e)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500667 {
Marri Devender Raocac383b2017-07-03 13:24:27 -0500668 log<level::ERR>(e.what());
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000669 return ipmi::responseUnspecifiedError();
Marri Devender Raocac383b2017-07-03 13:24:27 -0500670 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500671}
672
anil kumar appana5b7c3262019-05-27 18:10:23 +0000673/**@brief implements the Read FRU Data command
674 * @param fruDeviceId - FRU device ID. FFh = reserved
675 * @param offset - FRU inventory offset to read
676 * @param readCount - count to read
677 *
678 * @return IPMI completion code plus response data
679 * - returnCount - response data count.
680 * - data - response data
681 */
682ipmi::RspType<uint8_t, // count returned
683 std::vector<uint8_t>> // FRU data
684 ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t offset,
685 uint8_t readCount)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500686{
anil kumar appana5b7c3262019-05-27 18:10:23 +0000687 if (fruDeviceId == 0xFF)
Tom Joseph187f5642018-03-29 13:49:06 +0530688 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000689 return ipmi::responseInvalidFieldRequest();
Tom Joseph187f5642018-03-29 13:49:06 +0530690 }
691
anil kumar appana5b7c3262019-05-27 18:10:23 +0000692 auto iter = frus.find(fruDeviceId);
693 if (iter == frus.end())
694 {
695 return ipmi::responseSensorInvalid();
696 }
697
Marri Devender Raocac383b2017-07-03 13:24:27 -0500698 try
699 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000700 const auto& fruArea = getFruAreaData(fruDeviceId);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500701 auto size = fruArea.size();
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500702
Tom Josephefcd68b2018-04-26 18:46:27 +0530703 if (offset >= size)
704 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000705 return ipmi::responseParmOutOfRange();
Tom Josephefcd68b2018-04-26 18:46:27 +0530706 }
707
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500708 // Write the count of response data.
anil kumar appana5b7c3262019-05-27 18:10:23 +0000709 uint8_t returnCount;
710 if ((offset + readCount) <= size)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500711 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000712 returnCount = readCount;
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500713 }
714 else
715 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000716 returnCount = size - offset;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500717 }
Ratan Gupta2848d602018-01-31 20:39:20 +0530718
anil kumar appana5b7c3262019-05-27 18:10:23 +0000719 std::vector<uint8_t> fruData((fruArea.begin() + offset),
720 (fruArea.begin() + offset + returnCount));
Ratan Gupta2848d602018-01-31 20:39:20 +0530721
anil kumar appana5b7c3262019-05-27 18:10:23 +0000722 return ipmi::responseSuccess(returnCount, fruData);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500723 }
724 catch (const InternalFailure& e)
725 {
Marri Devender Raocac383b2017-07-03 13:24:27 -0500726 log<level::ERR>(e.what());
anil kumar appana5b7c3262019-05-27 18:10:23 +0000727 return ipmi::responseUnspecifiedError();
Marri Devender Raocac383b2017-07-03 13:24:27 -0500728 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500729}
730
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000731ipmi::RspType<uint8_t, // SDR version
732 uint16_t, // record count LS first
733 uint16_t, // free space in bytes, LS first
734 uint32_t, // addition timestamp LS first
735 uint32_t, // deletion timestamp LS first
736 uint8_t> // operation Support
737 ipmiGetRepositoryInfo()
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600738{
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600739
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000740 constexpr uint8_t sdrVersion = 0x51;
741 constexpr uint16_t freeSpace = 0xFFFF;
742 constexpr uint32_t additionTimestamp = 0x0;
743 constexpr uint32_t deletionTimestamp = 0x0;
744 constexpr uint8_t operationSupport = 0;
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600745
Patrick Venturedb0cbe62019-09-09 14:47:22 -0700746 uint16_t records = frus.size() + ipmi::sensor::sensors.size();
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600747
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000748 return ipmi::responseSuccess(sdrVersion, records, freeSpace,
749 additionTimestamp, deletionTimestamp,
750 operationSupport);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600751}
Chris Austenb4f5b922015-10-13 12:44:43 -0500752
Chris Austenb4f5b922015-10-13 12:44:43 -0500753void register_netfn_storage_functions()
754{
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530755 // <Get SEL Info>
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000756 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
757 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
758 ipmiStorageGetSelInfo);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530759
Tom05732372016-09-06 17:21:23 +0530760 // <Get SEL Time>
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000761 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
762 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
763 ipmiStorageGetSelTime);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500764
Tom05732372016-09-06 17:21:23 +0530765 // <Set SEL Time>
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000766 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
767 ipmi::storage::cmdSetSelTime,
768 ipmi::Privilege::Operator, ipmiStorageSetSelTime);
Chris Austenb4f5b922015-10-13 12:44:43 -0500769
Tom05732372016-09-06 17:21:23 +0530770 // <Reserve SEL>
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000771 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
772 ipmi::storage::cmdReserveSel, ipmi::Privilege::User,
773 ipmiStorageReserveSel);
Tom Josepha4953392017-06-30 19:09:47 +0530774 // <Get SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700775 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
776 getSELEntry, PRIVILEGE_USER);
Tom Josepha4953392017-06-30 19:09:47 +0530777
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530778 // <Delete SEL Entry>
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000779 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
780 ipmi::storage::cmdDeleteSelEntry,
781 ipmi::Privilege::Operator, deleteSELEntry);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530782
Tom05732372016-09-06 17:21:23 +0530783 // <Add SEL Entry>
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000784 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
785 ipmi::storage::cmdAddSelEntry,
786 ipmi::Privilege::Operator, ipmiStorageAddSEL);
787
Tom Joseph2f05bb52017-06-30 19:14:49 +0530788 // <Clear SEL>
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000789 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
790 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
791 clearSEL);
792
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500793 // <Get FRU Inventory Area Info>
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000794 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
795 ipmi::storage::cmdGetFruInventoryAreaInfo,
796 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500797
Jason M. Billsb5248c92019-06-24 15:53:08 -0700798 // <READ FRU Data>
anil kumar appana5b7c3262019-05-27 18:10:23 +0000799 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
800 ipmi::storage::cmdReadFruData,
801 ipmi::Privilege::Operator, ipmiStorageReadFruData);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500802
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600803 // <Get Repository Info>
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000804 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
805 ipmi::storage::cmdGetSdrRepositoryInfo,
806 ipmi::Privilege::User, ipmiGetRepositoryInfo);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600807
Tom Joseph5ca50952018-02-22 00:33:38 +0530808 // <Reserve SDR Repository>
jayaprakash Mutyalad9578232019-05-13 20:22:50 +0000809 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
810 ipmi::storage::cmdReserveSdrRepository,
811 ipmi::Privilege::User, ipmiSensorReserveSdr);
Tom Joseph5ca50952018-02-22 00:33:38 +0530812
813 // <Get SDR>
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
815 ipmi_sen_get_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530816
Marri Devender Rao908f7502017-07-10 01:49:54 -0500817 ipmi::fru::registerCallbackHandler();
Chris Austenb4f5b922015-10-13 12:44:43 -0500818 return;
819}