blob: d2bc8a5e489c810839fd8e475d1f0cad4580e3ae [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";
43constexpr auto HOST_TIME_PATH = "/xyz/openbmc_project/time/host";
44constexpr 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
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -050079ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070080 ipmi_request_t request,
81 ipmi_response_t response,
82 ipmi_data_len_t data_len,
83 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -050084{
Chris Austenb4f5b922015-10-13 12:44:43 -050085 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +080086 ipmi_ret_t rc = IPMI_CC_INVALID;
Chris Austenb4f5b922015-10-13 12:44:43 -050087 *data_len = 0;
88 return rc;
89}
90
jayaprakash Mutyalab7557722019-05-02 21:13:30 +000091/** @brief implements the get SEL Info command
92 * @returns IPMI completion code plus response data
93 * - selVersion - SEL revision
94 * - entries - Number of log entries in SEL.
95 * - freeSpace - Free Space in bytes.
96 * - addTimeStamp - Most recent addition timestamp
97 * - eraseTimeStamp - Most recent erase timestamp
98 * - operationSupport - Reserve & Delete SEL operations supported
99 */
100
101ipmi::RspType<uint8_t, // SEL revision.
102 uint16_t, // number of log entries in SEL.
103 uint16_t, // free Space in bytes.
104 uint32_t, // most recent addition timestamp
105 uint32_t, // most recent erase timestamp.
106
107 bool, // SEL allocation info supported
108 bool, // reserve SEL supported
109 bool, // partial Add SEL Entry supported
110 bool, // delete SEL supported
111 uint3_t, // reserved
112 bool // overflow flag
113 >
114 ipmiStorageGetSelInfo()
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530115{
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000116 uint16_t entries = 0;
117 // Most recent addition timestamp.
118 uint32_t addTimeStamp = ipmi::sel::invalidTimeStamp;
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530119
Tom Josephe59abfb2018-08-06 18:46:27 +0530120 try
121 {
122 ipmi::sel::readLoggingObjectPaths(cache::paths);
123 }
124 catch (const sdbusplus::exception::SdBusError& e)
125 {
126 // No action if reading log objects have failed for this command.
127 // readLoggingObjectPaths will throw exception if there are no log
128 // entries. The command will be responded with number of SEL entries
129 // as 0.
130 }
131
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530132 if (!cache::paths.empty())
133 {
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000134 entries = static_cast<uint16_t>(cache::paths.size());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530135
136 try
137 {
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000138 addTimeStamp = static_cast<uint32_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700139 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530140 }
141 catch (InternalFailure& e)
142 {
143 }
144 catch (const std::runtime_error& e)
145 {
146 log<level::ERR>(e.what());
147 }
148 }
149
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000150 constexpr uint8_t selVersion = ipmi::sel::selVersion;
151 constexpr uint16_t freeSpace = 0xFFFF;
152 constexpr uint32_t eraseTimeStamp = ipmi::sel::invalidTimeStamp;
153 constexpr uint3_t reserved{0};
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530154
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000155 return ipmi::responseSuccess(
156 selVersion, entries, freeSpace, addTimeStamp, eraseTimeStamp,
157 ipmi::sel::operationSupport::getSelAllocationInfo,
158 ipmi::sel::operationSupport::reserveSel,
159 ipmi::sel::operationSupport::partialAddSelEntry,
160 ipmi::sel::operationSupport::deleteSel, reserved,
161 ipmi::sel::operationSupport::overflow);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530162}
163
Tom Josepha4953392017-06-30 19:09:47 +0530164ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
165 ipmi_request_t request, ipmi_response_t response,
166 ipmi_data_len_t data_len, ipmi_context_t context)
167{
Jason M. Bills851acb12019-02-04 14:06:57 -0800168 if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
169 {
170 *data_len = 0;
171 return IPMI_CC_REQ_DATA_LEN_INVALID;
172 }
173
Patrick Venture0b02be92018-08-31 11:55:55 -0700174 auto requestData =
175 reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
Tom Josepha4953392017-06-30 19:09:47 +0530176
177 if (requestData->reservationID != 0)
178 {
Jason M. Bills13e67c82018-09-10 14:12:16 -0700179 if (!checkSELReservation(requestData->reservationID))
Tom Josepha4953392017-06-30 19:09:47 +0530180 {
181 *data_len = 0;
182 return IPMI_CC_INVALID_RESERVATION_ID;
183 }
184 }
185
186 if (cache::paths.empty())
187 {
188 *data_len = 0;
189 return IPMI_CC_SENSOR_INVALID;
190 }
191
192 ipmi::sel::ObjectPaths::const_iterator iter;
193
194 // Check for the requested SEL Entry.
195 if (requestData->selRecordID == ipmi::sel::firstEntry)
196 {
197 iter = cache::paths.begin();
198 }
199 else if (requestData->selRecordID == ipmi::sel::lastEntry)
200 {
201 iter = cache::paths.end();
202 }
203 else
204 {
205 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
206 std::to_string(requestData->selRecordID);
207
208 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
209 if (iter == cache::paths.end())
210 {
211 *data_len = 0;
212 return IPMI_CC_SENSOR_INVALID;
213 }
214 }
215
Patrick Venture0b02be92018-08-31 11:55:55 -0700216 ipmi::sel::GetSELEntryResponse record{};
Tom Josepha4953392017-06-30 19:09:47 +0530217
218 // Convert the log entry into SEL record.
219 try
220 {
221 record = ipmi::sel::convertLogEntrytoSEL(*iter);
222 }
223 catch (InternalFailure& e)
224 {
225 *data_len = 0;
226 return IPMI_CC_UNSPECIFIED_ERROR;
227 }
228 catch (const std::runtime_error& e)
229 {
230 log<level::ERR>(e.what());
231 *data_len = 0;
232 return IPMI_CC_UNSPECIFIED_ERROR;
233 }
234
Tom Josepha4953392017-06-30 19:09:47 +0530235 // Identify the next SEL record ID
Patrick Venture0b02be92018-08-31 11:55:55 -0700236 if (iter != cache::paths.end())
Tom Josepha4953392017-06-30 19:09:47 +0530237 {
238 ++iter;
239 if (iter == cache::paths.end())
240 {
241 record.nextRecordID = ipmi::sel::lastEntry;
242 }
243 else
244 {
Vernon Mauery185b9f82018-07-20 10:52:36 -0700245 namespace fs = std::filesystem;
Tom Josepha4953392017-06-30 19:09:47 +0530246 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700247 record.nextRecordID = static_cast<uint16_t>(
248 std::stoul(std::string(path.filename().c_str())));
Tom Josepha4953392017-06-30 19:09:47 +0530249 }
250 }
251 else
252 {
253 record.nextRecordID = ipmi::sel::lastEntry;
254 }
255
256 if (requestData->readLength == ipmi::sel::entireRecord)
257 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700258 std::memcpy(response, &record, sizeof(record));
Tom Josepha4953392017-06-30 19:09:47 +0530259 *data_len = sizeof(record);
260 }
261 else
262 {
263 if (requestData->offset >= ipmi::sel::selRecordSize ||
264 requestData->readLength > ipmi::sel::selRecordSize)
265 {
266 *data_len = 0;
267 return IPMI_CC_INVALID_FIELD_REQUEST;
268 }
269
270 auto diff = ipmi::sel::selRecordSize - requestData->offset;
Patrick Venture0b02be92018-08-31 11:55:55 -0700271 auto readLength =
272 std::min(diff, static_cast<int>(requestData->readLength));
Tom Josepha4953392017-06-30 19:09:47 +0530273
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700274 std::memcpy(response, &record.nextRecordID,
275 sizeof(record.nextRecordID));
276 std::memcpy(static_cast<uint8_t*>(response) +
277 sizeof(record.nextRecordID),
278 &record.recordID + requestData->offset, readLength);
Tom Josepha4953392017-06-30 19:09:47 +0530279 *data_len = sizeof(record.nextRecordID) + readLength;
280 }
281
282 return IPMI_CC_OK;
283}
284
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000285/** @brief implements the delete SEL entry command
286 * @request
287 * - reservationID; // reservation ID.
288 * - selRecordID; // SEL record ID.
289 *
290 * @returns ipmi completion code plus response data
291 * - Record ID of the deleted record
292 */
293ipmi::RspType<uint16_t // deleted record ID
294 >
295 deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530296{
Jason M. Bills851acb12019-02-04 14:06:57 -0800297
Brad Bishop1a4117b2018-11-21 15:48:18 -0500298 namespace fs = std::filesystem;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530299
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000300 if (!checkSELReservation(reservationID))
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530301 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000302 return ipmi::responseInvalidReservationId();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530303 }
304
Jason M. Bills13e67c82018-09-10 14:12:16 -0700305 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
306 // deleted
307 cancelSELReservation();
308
Tom Josephe59abfb2018-08-06 18:46:27 +0530309 try
310 {
311 ipmi::sel::readLoggingObjectPaths(cache::paths);
312 }
313 catch (const sdbusplus::exception::SdBusError& e)
314 {
315 // readLoggingObjectPaths will throw exception if there are no error
316 // log entries.
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000317 return ipmi::responseSensorInvalid();
Tom Josephe59abfb2018-08-06 18:46:27 +0530318 }
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530319
320 if (cache::paths.empty())
321 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000322 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530323 }
324
325 ipmi::sel::ObjectPaths::const_iterator iter;
326 uint16_t delRecordID = 0;
327
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000328 if (selRecordID == ipmi::sel::firstEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530329 {
330 iter = cache::paths.begin();
331 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700332 delRecordID = static_cast<uint16_t>(
333 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530334 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000335 else if (selRecordID == ipmi::sel::lastEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530336 {
337 iter = cache::paths.end();
338 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700339 delRecordID = static_cast<uint16_t>(
340 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530341 }
342 else
343 {
344 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000345 std::to_string(selRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530346
347 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
348 if (iter == cache::paths.end())
349 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000350 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530351 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000352 delRecordID = selRecordID;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530353 }
354
355 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
356 std::string service;
357
358 try
359 {
360 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter);
361 }
362 catch (const std::runtime_error& e)
363 {
364 log<level::ERR>(e.what());
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000365 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530366 }
367
Patrick Venture0b02be92018-08-31 11:55:55 -0700368 auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
369 ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530370 auto reply = bus.call(methodCall);
371 if (reply.is_method_error())
372 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000373 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530374 }
375
376 // Invalidate the cache of dbus entry objects.
377 cache::paths.clear();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530378
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000379 return ipmi::responseSuccess(delRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530380}
381
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000382/** @brief implements the Clear SEL command
383 * @request
384 * - reservationID // Reservation ID.
385 * - clr // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) }
386 * - eraseOperation; // requested operation.
387 *
388 * @returns ipmi completion code plus response data
389 * - erase status
390 */
391
392ipmi::RspType<uint8_t // erase status
393 >
394 clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
395 uint8_t eraseOperation)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530396{
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000397 static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
398 if (clr != clrOk)
Jason M. Bills851acb12019-02-04 14:06:57 -0800399 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000400 return ipmi::responseInvalidFieldRequest();
Jason M. Bills851acb12019-02-04 14:06:57 -0800401 }
402
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000403 if (!checkSELReservation(reservationID))
Tom Joseph2f05bb52017-06-30 19:14:49 +0530404 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000405 return ipmi::responseInvalidReservationId();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530406 }
407
Tom Joseph2f05bb52017-06-30 19:14:49 +0530408 /*
409 * Erasure status cannot be fetched from DBUS, so always return erasure
410 * status as `erase completed`.
411 */
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000412 if (eraseOperation == ipmi::sel::getEraseStatus)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530413 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000414 return ipmi::responseSuccess(
415 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530416 }
417
Jason M. Bills13e67c82018-09-10 14:12:16 -0700418 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
419 cancelSELReservation();
420
Tom Joseph2f05bb52017-06-30 19:14:49 +0530421 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
Tom Josephe59abfb2018-08-06 18:46:27 +0530422 ipmi::sel::ObjectPaths objectPaths;
Tom Joseph2f05bb52017-06-30 19:14:49 +0530423 auto depth = 0;
424
Patrick Venture0b02be92018-08-31 11:55:55 -0700425 auto mapperCall =
426 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
427 ipmi::sel::mapperIntf, "GetSubTreePaths");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530428 mapperCall.append(ipmi::sel::logBasePath);
429 mapperCall.append(depth);
430 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
431
Tom Josephe59abfb2018-08-06 18:46:27 +0530432 try
Tom Joseph2f05bb52017-06-30 19:14:49 +0530433 {
Tom Josephe59abfb2018-08-06 18:46:27 +0530434 auto reply = bus.call(mapperCall);
435 if (reply.is_method_error())
436 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000437 return ipmi::responseSuccess(
438 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530439 }
Tom Joseph2f05bb52017-06-30 19:14:49 +0530440
Tom Josephe59abfb2018-08-06 18:46:27 +0530441 reply.read(objectPaths);
442 if (objectPaths.empty())
443 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000444 return ipmi::responseSuccess(
445 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530446 }
447 }
448 catch (const sdbusplus::exception::SdBusError& e)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530449 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000450 return ipmi::responseSuccess(
451 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530452 }
453
454 std::string service;
455
456 try
457 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700458 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
Tom Joseph2f05bb52017-06-30 19:14:49 +0530459 objectPaths.front());
460 }
461 catch (const std::runtime_error& e)
462 {
463 log<level::ERR>(e.what());
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000464 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530465 }
466
467 for (const auto& iter : objectPaths)
468 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700469 auto methodCall = bus.new_method_call(
470 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530471
472 auto reply = bus.call(methodCall);
473 if (reply.is_method_error())
474 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000475 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530476 }
477 }
478
479 // Invalidate the cache of dbus entry objects.
480 cache::paths.clear();
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000481 return ipmi::responseSuccess(
482 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530483}
484
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000485/** @brief implements the get SEL time command
486 * @returns IPMI completion code plus response data
487 * -current time
488 */
489ipmi::RspType<uint32_t> // current time
490 ipmiStorageGetSelTime()
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500491{
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530492 using namespace std::chrono;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530493 uint64_t host_time_usec = 0;
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500494 std::stringstream hostTime;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500495
Lei YUe8939392017-06-15 10:45:05 +0800496 try
497 {
498 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
499 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
Vernon Mauery16b86932019-05-01 08:36:11 -0700500 std::variant<uint64_t> value;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530501
Lei YUe8939392017-06-15 10:45:05 +0800502 // Get host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700503 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
504 DBUS_PROPERTIES, "Get");
Lei YUe8939392017-06-15 10:45:05 +0800505
506 method.append(TIME_INTERFACE, PROPERTY_ELAPSED);
507 auto reply = bus.call(method);
508 if (reply.is_method_error())
509 {
510 log<level::ERR>("Error getting time",
511 entry("SERVICE=%s", service.c_str()),
512 entry("PATH=%s", HOST_TIME_PATH));
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000513 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800514 }
515 reply.read(value);
Vernon Maueryf442e112019-04-09 11:44:36 -0700516 host_time_usec = std::get<uint64_t>(value);
Lei YUe8939392017-06-15 10:45:05 +0800517 }
518 catch (InternalFailure& e)
519 {
520 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000521 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800522 }
Tom Joseph30fd0a12019-09-24 06:53:22 +0530523 catch (const std::exception& e)
Lei YUe8939392017-06-15 10:45:05 +0800524 {
525 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000526 return ipmi::responseUnspecifiedError();
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530527 }
528
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000529 hostTime << "Host time:"
530 << duration_cast<seconds>(microseconds(host_time_usec)).count();
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500531 log<level::DEBUG>(hostTime.str().c_str());
532
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530533 // Time is really long int but IPMI wants just uint32. This works okay until
534 // the number of seconds since 1970 overflows uint32 size.. Still a whole
535 // lot of time here to even think about that.
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000536 return ipmi::responseSuccess(
537 duration_cast<seconds>(microseconds(host_time_usec)).count());
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500538}
539
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000540/** @brief implements the set SEL time command
541 * @param selDeviceTime - epoch time
542 * -local time as the number of seconds from 00:00:00, January 1, 1970
543 * @returns IPMI completion code
544 */
545ipmi::RspType<> ipmiStorageSetSelTime(uint32_t selDeviceTime)
Chris Austenb4f5b922015-10-13 12:44:43 -0500546{
Lei YUe8939392017-06-15 10:45:05 +0800547 using namespace std::chrono;
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000548 microseconds usec{seconds(selDeviceTime)};
Norman James82330442015-11-19 16:53:26 -0600549
Lei YUe8939392017-06-15 10:45:05 +0800550 try
551 {
552 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
553 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
Vernon Mauery16b86932019-05-01 08:36:11 -0700554 std::variant<uint64_t> value{usec.count()};
Lei YUe8939392017-06-15 10:45:05 +0800555
556 // Set host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700557 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
558 DBUS_PROPERTIES, "Set");
Lei YUe8939392017-06-15 10:45:05 +0800559
560 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
561 auto reply = bus.call(method);
562 if (reply.is_method_error())
563 {
564 log<level::ERR>("Error setting time",
565 entry("SERVICE=%s", service.c_str()),
566 entry("PATH=%s", HOST_TIME_PATH));
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000567 return ipmi::responseUnspecifiedError();
Lei YUe8939392017-06-15 10:45:05 +0800568 }
Norman James82330442015-11-19 16:53:26 -0600569 }
Lei YUe8939392017-06-15 10:45:05 +0800570 catch (InternalFailure& e)
571 {
572 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000573 return ipmi::responseUnspecifiedError();
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530574 }
Tom Joseph30fd0a12019-09-24 06:53:22 +0530575 catch (const std::exception& e)
Lei YUe8939392017-06-15 10:45:05 +0800576 {
577 log<level::ERR>(e.what());
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000578 return ipmi::responseUnspecifiedError();
Norman James82330442015-11-19 16:53:26 -0600579 }
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530580
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000581 return ipmi::responseSuccess();
Chris Austenb4f5b922015-10-13 12:44:43 -0500582}
583
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000584/** @brief implements the reserve SEL command
585 * @returns IPMI completion code plus response data
586 * - SEL reservation ID.
587 */
588ipmi::RspType<uint16_t> ipmiStorageReserveSel()
Chris Austenb4f5b922015-10-13 12:44:43 -0500589{
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000590 return ipmi::responseSuccess(reserveSel());
Chris Austenb4f5b922015-10-13 12:44:43 -0500591}
592
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000593/** @brief implements the Add SEL entry command
594 * @request
595 *
596 * - recordID ID used for SEL Record access
597 * - recordType Record Type
598 * - timeStamp Time when event was logged. LS byte first
599 * - generatorID software ID if event was generated from
600 * system software
601 * - evmRev event message format version
602 * - sensorType sensor type code for service that generated
603 * the event
604 * - sensorNumber number of sensors that generated the event
605 * - eventDir event dir
606 * - eventData event data field contents
607 *
608 * @returns ipmi completion code plus response data
609 * - RecordID of the Added SEL entry
610 */
611ipmi::RspType<uint16_t // recordID of the Added SEL entry
612 >
613 ipmiStorageAddSEL(uint16_t recordID, uint8_t recordType, uint32_t timeStamp,
614 uint16_t generatorID, uint8_t evmRev, uint8_t sensorType,
615 uint8_t sensorNumber, uint8_t eventDir,
616 std::array<uint8_t, eventDataSize> eventData)
Chris Austenb4f5b922015-10-13 12:44:43 -0500617{
Jason M. Bills13e67c82018-09-10 14:12:16 -0700618 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
619 // added
620 cancelSELReservation();
Tom Josephb647d5b2017-10-31 17:25:33 +0530621 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
622 // a maintenance procedure associated with eSEL record.
623 static constexpr auto procedureType = 0xDE;
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000624 if (recordType == procedureType)
Tom Josephb647d5b2017-10-31 17:25:33 +0530625 {
626 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
627 // procedure number.
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000628 createProcedureLogEntry(sensorType);
Tom Josephb647d5b2017-10-31 17:25:33 +0530629 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500630
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000631 return ipmi::responseSuccess(recordID);
Chris Austenb4f5b922015-10-13 12:44:43 -0500632}
633
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000634/** @brief implements the get FRU Inventory Area Info command
635 *
636 * @returns IPMI completion code plus response data
637 * - FRU Inventory area size in bytes,
638 * - access bit
639 **/
640ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
641 uint8_t // access size (bytes / words)
642 >
643 ipmiStorageGetFruInvAreaInfo(uint8_t fruID)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500644{
Tom Joseph187f5642018-03-29 13:49:06 +0530645
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000646 auto iter = frus.find(fruID);
Tom Joseph187f5642018-03-29 13:49:06 +0530647 if (iter == frus.end())
648 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000649 return ipmi::responseSensorInvalid();
Tom Joseph187f5642018-03-29 13:49:06 +0530650 }
651
Marri Devender Raocac383b2017-07-03 13:24:27 -0500652 try
653 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000654 return ipmi::responseSuccess(
655 static_cast<uint16_t>(getFruAreaData(fruID).size()),
656 static_cast<uint8_t>(AccessMode::bytes));
Marri Devender Raocac383b2017-07-03 13:24:27 -0500657 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700658 catch (const InternalFailure& e)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500659 {
Marri Devender Raocac383b2017-07-03 13:24:27 -0500660 log<level::ERR>(e.what());
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000661 return ipmi::responseUnspecifiedError();
Marri Devender Raocac383b2017-07-03 13:24:27 -0500662 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500663}
664
anil kumar appana5b7c3262019-05-27 18:10:23 +0000665/**@brief implements the Read FRU Data command
666 * @param fruDeviceId - FRU device ID. FFh = reserved
667 * @param offset - FRU inventory offset to read
668 * @param readCount - count to read
669 *
670 * @return IPMI completion code plus response data
671 * - returnCount - response data count.
672 * - data - response data
673 */
674ipmi::RspType<uint8_t, // count returned
675 std::vector<uint8_t>> // FRU data
676 ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t offset,
677 uint8_t readCount)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500678{
anil kumar appana5b7c3262019-05-27 18:10:23 +0000679 if (fruDeviceId == 0xFF)
Tom Joseph187f5642018-03-29 13:49:06 +0530680 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000681 return ipmi::responseInvalidFieldRequest();
Tom Joseph187f5642018-03-29 13:49:06 +0530682 }
683
anil kumar appana5b7c3262019-05-27 18:10:23 +0000684 auto iter = frus.find(fruDeviceId);
685 if (iter == frus.end())
686 {
687 return ipmi::responseSensorInvalid();
688 }
689
Marri Devender Raocac383b2017-07-03 13:24:27 -0500690 try
691 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000692 const auto& fruArea = getFruAreaData(fruDeviceId);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500693 auto size = fruArea.size();
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500694
Tom Josephefcd68b2018-04-26 18:46:27 +0530695 if (offset >= size)
696 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000697 return ipmi::responseParmOutOfRange();
Tom Josephefcd68b2018-04-26 18:46:27 +0530698 }
699
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500700 // Write the count of response data.
anil kumar appana5b7c3262019-05-27 18:10:23 +0000701 uint8_t returnCount;
702 if ((offset + readCount) <= size)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500703 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000704 returnCount = readCount;
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500705 }
706 else
707 {
anil kumar appana5b7c3262019-05-27 18:10:23 +0000708 returnCount = size - offset;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500709 }
Ratan Gupta2848d602018-01-31 20:39:20 +0530710
anil kumar appana5b7c3262019-05-27 18:10:23 +0000711 std::vector<uint8_t> fruData((fruArea.begin() + offset),
712 (fruArea.begin() + offset + returnCount));
Ratan Gupta2848d602018-01-31 20:39:20 +0530713
anil kumar appana5b7c3262019-05-27 18:10:23 +0000714 return ipmi::responseSuccess(returnCount, fruData);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500715 }
716 catch (const InternalFailure& e)
717 {
Marri Devender Raocac383b2017-07-03 13:24:27 -0500718 log<level::ERR>(e.what());
anil kumar appana5b7c3262019-05-27 18:10:23 +0000719 return ipmi::responseUnspecifiedError();
Marri Devender Raocac383b2017-07-03 13:24:27 -0500720 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500721}
722
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000723ipmi::RspType<uint8_t, // SDR version
724 uint16_t, // record count LS first
725 uint16_t, // free space in bytes, LS first
726 uint32_t, // addition timestamp LS first
727 uint32_t, // deletion timestamp LS first
728 uint8_t> // operation Support
729 ipmiGetRepositoryInfo()
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600730{
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600731
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000732 constexpr uint8_t sdrVersion = 0x51;
733 constexpr uint16_t freeSpace = 0xFFFF;
734 constexpr uint32_t additionTimestamp = 0x0;
735 constexpr uint32_t deletionTimestamp = 0x0;
736 constexpr uint8_t operationSupport = 0;
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600737
Patrick Venturedb0cbe62019-09-09 14:47:22 -0700738 uint16_t records = frus.size() + ipmi::sensor::sensors.size();
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600739
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000740 return ipmi::responseSuccess(sdrVersion, records, freeSpace,
741 additionTimestamp, deletionTimestamp,
742 operationSupport);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600743}
Chris Austenb4f5b922015-10-13 12:44:43 -0500744
Chris Austenb4f5b922015-10-13 12:44:43 -0500745void register_netfn_storage_functions()
746{
Tom05732372016-09-06 17:21:23 +0530747 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700748 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
749 ipmi_storage_wildcard, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500750
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530751 // <Get SEL Info>
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000752 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
753 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
754 ipmiStorageGetSelInfo);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530755
Tom05732372016-09-06 17:21:23 +0530756 // <Get SEL Time>
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000757 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
758 ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
759 ipmiStorageGetSelTime);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500760
Tom05732372016-09-06 17:21:23 +0530761 // <Set SEL Time>
jayaprakash Mutyaladb2e8c42019-05-03 01:38:01 +0000762 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
763 ipmi::storage::cmdSetSelTime,
764 ipmi::Privilege::Operator, ipmiStorageSetSelTime);
Chris Austenb4f5b922015-10-13 12:44:43 -0500765
Tom05732372016-09-06 17:21:23 +0530766 // <Reserve SEL>
jayaprakash Mutyalab7557722019-05-02 21:13:30 +0000767 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
768 ipmi::storage::cmdReserveSel, ipmi::Privilege::User,
769 ipmiStorageReserveSel);
Tom Josepha4953392017-06-30 19:09:47 +0530770 // <Get SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700771 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
772 getSELEntry, PRIVILEGE_USER);
Tom Josepha4953392017-06-30 19:09:47 +0530773
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530774 // <Delete SEL Entry>
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000775 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
776 ipmi::storage::cmdDeleteSelEntry,
777 ipmi::Privilege::Operator, deleteSELEntry);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530778
Tom05732372016-09-06 17:21:23 +0530779 // <Add SEL Entry>
anil kumar appana2c7db1d2019-05-28 11:20:19 +0000780 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
781 ipmi::storage::cmdAddSelEntry,
782 ipmi::Privilege::Operator, ipmiStorageAddSEL);
783
Tom Joseph2f05bb52017-06-30 19:14:49 +0530784 // <Clear SEL>
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000785 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
786 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
787 clearSEL);
788
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500789 // <Get FRU Inventory Area Info>
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000790 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
791 ipmi::storage::cmdGetFruInventoryAreaInfo,
792 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500793
Jason M. Billsb5248c92019-06-24 15:53:08 -0700794 // <READ FRU Data>
anil kumar appana5b7c3262019-05-27 18:10:23 +0000795 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
796 ipmi::storage::cmdReadFruData,
797 ipmi::Privilege::Operator, ipmiStorageReadFruData);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500798
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600799 // <Get Repository Info>
Pradeep Kumarb60e8402019-05-06 15:17:01 +0000800 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
801 ipmi::storage::cmdGetSdrRepositoryInfo,
802 ipmi::Privilege::User, ipmiGetRepositoryInfo);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600803
Tom Joseph5ca50952018-02-22 00:33:38 +0530804 // <Reserve SDR Repository>
jayaprakash Mutyalad9578232019-05-13 20:22:50 +0000805 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
806 ipmi::storage::cmdReserveSdrRepository,
807 ipmi::Privilege::User, ipmiSensorReserveSdr);
Tom Joseph5ca50952018-02-22 00:33:38 +0530808
809 // <Get SDR>
Patrick Venture0b02be92018-08-31 11:55:55 -0700810 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
811 ipmi_sen_get_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530812
Marri Devender Rao908f7502017-07-10 01:49:54 -0500813 ipmi::fru::registerCallbackHandler();
Chris Austenb4f5b922015-10-13 12:44:43 -0500814 return;
815}