blob: 6f208355d4e6c815b481c65e67a2d6ec5ea4f4cd [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>
William A. Kennington III4c008022018-10-12 17:18:14 -070022#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070023#include <sdbusplus/server.hpp>
24#include <string>
25#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;
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -060030extern const ipmi::sensor::IdInfoMap sensors;
31extern const FruMap frus;
Chris Austenb4f5b922015-10-13 12:44:43 -050032
Patrick Venture0b02be92018-08-31 11:55:55 -070033namespace
34{
Lei YUe8939392017-06-15 10:45:05 +080035constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
36constexpr auto HOST_TIME_PATH = "/xyz/openbmc_project/time/host";
37constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
Patrick Venture0b02be92018-08-31 11:55:55 -070038constexpr auto PROPERTY_ELAPSED = "Elapsed";
Lei YUe8939392017-06-15 10:45:05 +080039
40const char* getTimeString(const uint64_t& usecSinceEpoch)
41{
42 using namespace std::chrono;
43 system_clock::time_point tp{microseconds(usecSinceEpoch)};
44 auto t = system_clock::to_time_t(tp);
45 return std::ctime(&t);
46}
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
Tom Joseph6f7deaa2017-06-30 19:03:54 +053091ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
92 ipmi_request_t request, ipmi_response_t response,
93 ipmi_data_len_t data_len, ipmi_context_t context)
94{
Jason M. Bills851acb12019-02-04 14:06:57 -080095 if (*data_len != 0)
96 {
97 *data_len = 0;
98 return IPMI_CC_REQ_DATA_LEN_INVALID;
99 }
100
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530101 std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse));
Patrick Venture0b02be92018-08-31 11:55:55 -0700102 auto responseData =
103 reinterpret_cast<ipmi::sel::GetSELInfoResponse*>(outPayload.data());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530104
105 responseData->selVersion = ipmi::sel::selVersion;
106 // Last erase timestamp is not available from log manager.
107 responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp;
108 responseData->operationSupport = ipmi::sel::operationSupport;
109
Tom Josephe59abfb2018-08-06 18:46:27 +0530110 try
111 {
112 ipmi::sel::readLoggingObjectPaths(cache::paths);
113 }
114 catch (const sdbusplus::exception::SdBusError& e)
115 {
116 // No action if reading log objects have failed for this command.
117 // readLoggingObjectPaths will throw exception if there are no log
118 // entries. The command will be responded with number of SEL entries
119 // as 0.
120 }
121
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530122 responseData->entries = 0;
123 responseData->addTimeStamp = ipmi::sel::invalidTimeStamp;
124
125 if (!cache::paths.empty())
126 {
127 responseData->entries = static_cast<uint16_t>(cache::paths.size());
128
129 try
130 {
131 responseData->addTimeStamp = static_cast<uint32_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700132 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530133 }
134 catch (InternalFailure& e)
135 {
136 }
137 catch (const std::runtime_error& e)
138 {
139 log<level::ERR>(e.what());
140 }
141 }
142
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700143 std::memcpy(response, outPayload.data(), outPayload.size());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530144 *data_len = outPayload.size();
145
146 return IPMI_CC_OK;
147}
148
Tom Josepha4953392017-06-30 19:09:47 +0530149ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
150 ipmi_request_t request, ipmi_response_t response,
151 ipmi_data_len_t data_len, ipmi_context_t context)
152{
Jason M. Bills851acb12019-02-04 14:06:57 -0800153 if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
154 {
155 *data_len = 0;
156 return IPMI_CC_REQ_DATA_LEN_INVALID;
157 }
158
Patrick Venture0b02be92018-08-31 11:55:55 -0700159 auto requestData =
160 reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
Tom Josepha4953392017-06-30 19:09:47 +0530161
162 if (requestData->reservationID != 0)
163 {
Jason M. Bills13e67c82018-09-10 14:12:16 -0700164 if (!checkSELReservation(requestData->reservationID))
Tom Josepha4953392017-06-30 19:09:47 +0530165 {
166 *data_len = 0;
167 return IPMI_CC_INVALID_RESERVATION_ID;
168 }
169 }
170
171 if (cache::paths.empty())
172 {
173 *data_len = 0;
174 return IPMI_CC_SENSOR_INVALID;
175 }
176
177 ipmi::sel::ObjectPaths::const_iterator iter;
178
179 // Check for the requested SEL Entry.
180 if (requestData->selRecordID == ipmi::sel::firstEntry)
181 {
182 iter = cache::paths.begin();
183 }
184 else if (requestData->selRecordID == ipmi::sel::lastEntry)
185 {
186 iter = cache::paths.end();
187 }
188 else
189 {
190 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
191 std::to_string(requestData->selRecordID);
192
193 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
194 if (iter == cache::paths.end())
195 {
196 *data_len = 0;
197 return IPMI_CC_SENSOR_INVALID;
198 }
199 }
200
Patrick Venture0b02be92018-08-31 11:55:55 -0700201 ipmi::sel::GetSELEntryResponse record{};
Tom Josepha4953392017-06-30 19:09:47 +0530202
203 // Convert the log entry into SEL record.
204 try
205 {
206 record = ipmi::sel::convertLogEntrytoSEL(*iter);
207 }
208 catch (InternalFailure& e)
209 {
210 *data_len = 0;
211 return IPMI_CC_UNSPECIFIED_ERROR;
212 }
213 catch (const std::runtime_error& e)
214 {
215 log<level::ERR>(e.what());
216 *data_len = 0;
217 return IPMI_CC_UNSPECIFIED_ERROR;
218 }
219
Tom Josepha4953392017-06-30 19:09:47 +0530220 // Identify the next SEL record ID
Patrick Venture0b02be92018-08-31 11:55:55 -0700221 if (iter != cache::paths.end())
Tom Josepha4953392017-06-30 19:09:47 +0530222 {
223 ++iter;
224 if (iter == cache::paths.end())
225 {
226 record.nextRecordID = ipmi::sel::lastEntry;
227 }
228 else
229 {
Vernon Mauery185b9f82018-07-20 10:52:36 -0700230 namespace fs = std::filesystem;
Tom Josepha4953392017-06-30 19:09:47 +0530231 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700232 record.nextRecordID = static_cast<uint16_t>(
233 std::stoul(std::string(path.filename().c_str())));
Tom Josepha4953392017-06-30 19:09:47 +0530234 }
235 }
236 else
237 {
238 record.nextRecordID = ipmi::sel::lastEntry;
239 }
240
241 if (requestData->readLength == ipmi::sel::entireRecord)
242 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700243 std::memcpy(response, &record, sizeof(record));
Tom Josepha4953392017-06-30 19:09:47 +0530244 *data_len = sizeof(record);
245 }
246 else
247 {
248 if (requestData->offset >= ipmi::sel::selRecordSize ||
249 requestData->readLength > ipmi::sel::selRecordSize)
250 {
251 *data_len = 0;
252 return IPMI_CC_INVALID_FIELD_REQUEST;
253 }
254
255 auto diff = ipmi::sel::selRecordSize - requestData->offset;
Patrick Venture0b02be92018-08-31 11:55:55 -0700256 auto readLength =
257 std::min(diff, static_cast<int>(requestData->readLength));
Tom Josepha4953392017-06-30 19:09:47 +0530258
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700259 std::memcpy(response, &record.nextRecordID,
260 sizeof(record.nextRecordID));
261 std::memcpy(static_cast<uint8_t*>(response) +
262 sizeof(record.nextRecordID),
263 &record.recordID + requestData->offset, readLength);
Tom Josepha4953392017-06-30 19:09:47 +0530264 *data_len = sizeof(record.nextRecordID) + readLength;
265 }
266
267 return IPMI_CC_OK;
268}
269
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000270/** @brief implements the delete SEL entry command
271 * @request
272 * - reservationID; // reservation ID.
273 * - selRecordID; // SEL record ID.
274 *
275 * @returns ipmi completion code plus response data
276 * - Record ID of the deleted record
277 */
278ipmi::RspType<uint16_t // deleted record ID
279 >
280 deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530281{
Jason M. Bills851acb12019-02-04 14:06:57 -0800282
Brad Bishop1a4117b2018-11-21 15:48:18 -0500283 namespace fs = std::filesystem;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530284
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000285 if (!checkSELReservation(reservationID))
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530286 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000287 return ipmi::responseInvalidReservationId();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530288 }
289
Jason M. Bills13e67c82018-09-10 14:12:16 -0700290 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
291 // deleted
292 cancelSELReservation();
293
Tom Josephe59abfb2018-08-06 18:46:27 +0530294 try
295 {
296 ipmi::sel::readLoggingObjectPaths(cache::paths);
297 }
298 catch (const sdbusplus::exception::SdBusError& e)
299 {
300 // readLoggingObjectPaths will throw exception if there are no error
301 // log entries.
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000302 return ipmi::responseSensorInvalid();
Tom Josephe59abfb2018-08-06 18:46:27 +0530303 }
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530304
305 if (cache::paths.empty())
306 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000307 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530308 }
309
310 ipmi::sel::ObjectPaths::const_iterator iter;
311 uint16_t delRecordID = 0;
312
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000313 if (selRecordID == ipmi::sel::firstEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530314 {
315 iter = cache::paths.begin();
316 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700317 delRecordID = static_cast<uint16_t>(
318 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530319 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000320 else if (selRecordID == ipmi::sel::lastEntry)
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530321 {
322 iter = cache::paths.end();
323 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700324 delRecordID = static_cast<uint16_t>(
325 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530326 }
327 else
328 {
329 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000330 std::to_string(selRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530331
332 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
333 if (iter == cache::paths.end())
334 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000335 return ipmi::responseSensorInvalid();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530336 }
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000337 delRecordID = selRecordID;
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530338 }
339
340 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
341 std::string service;
342
343 try
344 {
345 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter);
346 }
347 catch (const std::runtime_error& e)
348 {
349 log<level::ERR>(e.what());
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000350 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530351 }
352
Patrick Venture0b02be92018-08-31 11:55:55 -0700353 auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
354 ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530355 auto reply = bus.call(methodCall);
356 if (reply.is_method_error())
357 {
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000358 return ipmi::responseUnspecifiedError();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530359 }
360
361 // Invalidate the cache of dbus entry objects.
362 cache::paths.clear();
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530363
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000364 return ipmi::responseSuccess(delRecordID);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530365}
366
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000367/** @brief implements the Clear SEL command
368 * @request
369 * - reservationID // Reservation ID.
370 * - clr // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) }
371 * - eraseOperation; // requested operation.
372 *
373 * @returns ipmi completion code plus response data
374 * - erase status
375 */
376
377ipmi::RspType<uint8_t // erase status
378 >
379 clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
380 uint8_t eraseOperation)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530381{
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000382 static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
383 if (clr != clrOk)
Jason M. Bills851acb12019-02-04 14:06:57 -0800384 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000385 return ipmi::responseInvalidFieldRequest();
Jason M. Bills851acb12019-02-04 14:06:57 -0800386 }
387
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000388 if (!checkSELReservation(reservationID))
Tom Joseph2f05bb52017-06-30 19:14:49 +0530389 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000390 return ipmi::responseInvalidReservationId();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530391 }
392
Tom Joseph2f05bb52017-06-30 19:14:49 +0530393 /*
394 * Erasure status cannot be fetched from DBUS, so always return erasure
395 * status as `erase completed`.
396 */
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000397 if (eraseOperation == ipmi::sel::getEraseStatus)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530398 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000399 return ipmi::responseSuccess(
400 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530401 }
402
Jason M. Bills13e67c82018-09-10 14:12:16 -0700403 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
404 cancelSELReservation();
405
Tom Joseph2f05bb52017-06-30 19:14:49 +0530406 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
Tom Josephe59abfb2018-08-06 18:46:27 +0530407 ipmi::sel::ObjectPaths objectPaths;
Tom Joseph2f05bb52017-06-30 19:14:49 +0530408 auto depth = 0;
409
Patrick Venture0b02be92018-08-31 11:55:55 -0700410 auto mapperCall =
411 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
412 ipmi::sel::mapperIntf, "GetSubTreePaths");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530413 mapperCall.append(ipmi::sel::logBasePath);
414 mapperCall.append(depth);
415 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
416
Tom Josephe59abfb2018-08-06 18:46:27 +0530417 try
Tom Joseph2f05bb52017-06-30 19:14:49 +0530418 {
Tom Josephe59abfb2018-08-06 18:46:27 +0530419 auto reply = bus.call(mapperCall);
420 if (reply.is_method_error())
421 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000422 return ipmi::responseSuccess(
423 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530424 }
Tom Joseph2f05bb52017-06-30 19:14:49 +0530425
Tom Josephe59abfb2018-08-06 18:46:27 +0530426 reply.read(objectPaths);
427 if (objectPaths.empty())
428 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000429 return ipmi::responseSuccess(
430 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Josephe59abfb2018-08-06 18:46:27 +0530431 }
432 }
433 catch (const sdbusplus::exception::SdBusError& e)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530434 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000435 return ipmi::responseSuccess(
436 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530437 }
438
439 std::string service;
440
441 try
442 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700443 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
Tom Joseph2f05bb52017-06-30 19:14:49 +0530444 objectPaths.front());
445 }
446 catch (const std::runtime_error& e)
447 {
448 log<level::ERR>(e.what());
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000449 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530450 }
451
452 for (const auto& iter : objectPaths)
453 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700454 auto methodCall = bus.new_method_call(
455 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530456
457 auto reply = bus.call(methodCall);
458 if (reply.is_method_error())
459 {
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000460 return ipmi::responseUnspecifiedError();
Tom Joseph2f05bb52017-06-30 19:14:49 +0530461 }
462 }
463
464 // Invalidate the cache of dbus entry objects.
465 cache::paths.clear();
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000466 return ipmi::responseSuccess(
467 static_cast<uint8_t>(ipmi::sel::eraseComplete));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530468}
469
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500470ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700471 ipmi_request_t request,
472 ipmi_response_t response,
473 ipmi_data_len_t data_len,
474 ipmi_context_t context)
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500475{
Jason M. Bills851acb12019-02-04 14:06:57 -0800476 if (*data_len != 0)
477 {
478 *data_len = 0;
479 return IPMI_CC_REQ_DATA_LEN_INVALID;
480 }
481
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530482 using namespace std::chrono;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530483 uint64_t host_time_usec = 0;
484 uint32_t resp = 0;
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500485 std::stringstream hostTime;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500486
Lei YUe8939392017-06-15 10:45:05 +0800487 try
488 {
489 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
490 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
491 sdbusplus::message::variant<uint64_t> value;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530492
Lei YUe8939392017-06-15 10:45:05 +0800493 // Get host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700494 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
495 DBUS_PROPERTIES, "Get");
Lei YUe8939392017-06-15 10:45:05 +0800496
497 method.append(TIME_INTERFACE, PROPERTY_ELAPSED);
498 auto reply = bus.call(method);
499 if (reply.is_method_error())
500 {
501 log<level::ERR>("Error getting time",
502 entry("SERVICE=%s", service.c_str()),
503 entry("PATH=%s", HOST_TIME_PATH));
504 return IPMI_CC_UNSPECIFIED_ERROR;
505 }
506 reply.read(value);
Vernon Maueryf442e112019-04-09 11:44:36 -0700507 host_time_usec = std::get<uint64_t>(value);
Lei YUe8939392017-06-15 10:45:05 +0800508 }
509 catch (InternalFailure& e)
510 {
511 log<level::ERR>(e.what());
512 return IPMI_CC_UNSPECIFIED_ERROR;
513 }
514 catch (const std::runtime_error& e)
515 {
516 log<level::ERR>(e.what());
517 return IPMI_CC_UNSPECIFIED_ERROR;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530518 }
519
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500520 hostTime << "Host time:" << getTimeString(host_time_usec);
521 log<level::DEBUG>(hostTime.str().c_str());
522
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530523 // Time is really long int but IPMI wants just uint32. This works okay until
524 // the number of seconds since 1970 overflows uint32 size.. Still a whole
525 // lot of time here to even think about that.
526 resp = duration_cast<seconds>(microseconds(host_time_usec)).count();
527 resp = htole32(resp);
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530528
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500529 // From the IPMI Spec 2.0, response should be a 32-bit value
530 *data_len = sizeof(resp);
531
532 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700533 std::memcpy(response, &resp, *data_len);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500534
Lei YUe8939392017-06-15 10:45:05 +0800535 return IPMI_CC_OK;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500536}
537
538ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700539 ipmi_request_t request,
540 ipmi_response_t response,
541 ipmi_data_len_t data_len,
542 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500543{
Jason M. Bills851acb12019-02-04 14:06:57 -0800544 if (*data_len != sizeof(uint32_t))
545 {
546 *data_len = 0;
547 return IPMI_CC_REQ_DATA_LEN_INVALID;
548 }
Lei YUe8939392017-06-15 10:45:05 +0800549 using namespace std::chrono;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530550 ipmi_ret_t rc = IPMI_CC_OK;
Lei YUe8939392017-06-15 10:45:05 +0800551 uint32_t secs = *static_cast<uint32_t*>(request);
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530552 *data_len = 0;
Chris Austenb4f5b922015-10-13 12:44:43 -0500553
Lei YUe8939392017-06-15 10:45:05 +0800554 secs = le32toh(secs);
555 microseconds usec{seconds(secs)};
Norman James82330442015-11-19 16:53:26 -0600556
Lei YUe8939392017-06-15 10:45:05 +0800557 try
558 {
559 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
560 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
561 sdbusplus::message::variant<uint64_t> value{usec.count()};
562
563 // Set host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700564 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
565 DBUS_PROPERTIES, "Set");
Lei YUe8939392017-06-15 10:45:05 +0800566
567 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
568 auto reply = bus.call(method);
569 if (reply.is_method_error())
570 {
571 log<level::ERR>("Error setting time",
572 entry("SERVICE=%s", service.c_str()),
573 entry("PATH=%s", HOST_TIME_PATH));
574 rc = IPMI_CC_UNSPECIFIED_ERROR;
575 }
Norman James82330442015-11-19 16:53:26 -0600576 }
Lei YUe8939392017-06-15 10:45:05 +0800577 catch (InternalFailure& e)
578 {
579 log<level::ERR>(e.what());
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530580 rc = IPMI_CC_UNSPECIFIED_ERROR;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530581 }
Lei YUe8939392017-06-15 10:45:05 +0800582 catch (const std::runtime_error& e)
583 {
584 log<level::ERR>(e.what());
Norman James82330442015-11-19 16:53:26 -0600585 rc = IPMI_CC_UNSPECIFIED_ERROR;
586 }
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530587
Chris Austenb4f5b922015-10-13 12:44:43 -0500588 return rc;
589}
590
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500591ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700592 ipmi_request_t request,
593 ipmi_response_t response,
594 ipmi_data_len_t data_len,
595 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500596{
Jason M. Bills851acb12019-02-04 14:06:57 -0800597 if (*data_len != 0)
598 {
599 *data_len = 0;
600 return IPMI_CC_REQ_DATA_LEN_INVALID;
601 }
602
Chris Austenb4f5b922015-10-13 12:44:43 -0500603 ipmi_ret_t rc = IPMI_CC_OK;
Jason M. Bills13e67c82018-09-10 14:12:16 -0700604 unsigned short selResID = reserveSel();
Chris Austenb4f5b922015-10-13 12:44:43 -0500605
Jason M. Bills13e67c82018-09-10 14:12:16 -0700606 *data_len = sizeof(selResID);
Chris Austenb4f5b922015-10-13 12:44:43 -0500607
608 // Pack the actual response
Jason M. Bills13e67c82018-09-10 14:12:16 -0700609 std::memcpy(response, &selResID, *data_len);
Chris Austenb4f5b922015-10-13 12:44:43 -0500610
611 return rc;
612}
613
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500614ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700615 ipmi_request_t request,
616 ipmi_response_t response,
617 ipmi_data_len_t data_len,
618 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500619{
Jason M. Bills851acb12019-02-04 14:06:57 -0800620 if (*data_len != sizeof(ipmi_add_sel_request_t))
621 {
622 *data_len = 0;
623 return IPMI_CC_REQ_DATA_LEN_INVALID;
624 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500625
626 ipmi_ret_t rc = IPMI_CC_OK;
Patrick Venture0b02be92018-08-31 11:55:55 -0700627 ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
Chris Austen41a4b312015-10-25 03:45:42 -0500628 uint16_t recordid;
Chris Austenb4f5b922015-10-13 12:44:43 -0500629
Jason M. Bills13e67c82018-09-10 14:12:16 -0700630 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
631 // added
632 cancelSELReservation();
633
Chris Austen313d95b2015-10-31 12:55:30 -0500634 recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
Chris Austen41a4b312015-10-25 03:45:42 -0500635
Jason M. Bills13e67c82018-09-10 14:12:16 -0700636 *data_len = sizeof(recordid);
Chris Austenb4f5b922015-10-13 12:44:43 -0500637
638 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700639 std::memcpy(response, &p->eventdata[1], 2);
Chris Austenb4f5b922015-10-13 12:44:43 -0500640
Tom Josephb647d5b2017-10-31 17:25:33 +0530641 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
642 // a maintenance procedure associated with eSEL record.
643 static constexpr auto procedureType = 0xDE;
644 if (p->recordtype == procedureType)
645 {
646 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
647 // procedure number.
648 createProcedureLogEntry(p->sensortype);
649 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500650
651 return rc;
652}
653
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000654/** @brief implements the get FRU Inventory Area Info command
655 *
656 * @returns IPMI completion code plus response data
657 * - FRU Inventory area size in bytes,
658 * - access bit
659 **/
660ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
661 uint8_t // access size (bytes / words)
662 >
663 ipmiStorageGetFruInvAreaInfo(uint8_t fruID)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500664{
Tom Joseph187f5642018-03-29 13:49:06 +0530665
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000666 auto iter = frus.find(fruID);
Tom Joseph187f5642018-03-29 13:49:06 +0530667 if (iter == frus.end())
668 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000669 return ipmi::responseSensorInvalid();
Tom Joseph187f5642018-03-29 13:49:06 +0530670 }
671
Marri Devender Raocac383b2017-07-03 13:24:27 -0500672 try
673 {
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000674 return ipmi::responseSuccess(
675 static_cast<uint16_t>(getFruAreaData(fruID).size()),
676 static_cast<uint8_t>(AccessMode::bytes));
Marri Devender Raocac383b2017-07-03 13:24:27 -0500677 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700678 catch (const InternalFailure& e)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500679 {
Marri Devender Raocac383b2017-07-03 13:24:27 -0500680 log<level::ERR>(e.what());
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000681 return ipmi::responseUnspecifiedError();
Marri Devender Raocac383b2017-07-03 13:24:27 -0500682 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500683}
684
Patrick Venture0b02be92018-08-31 11:55:55 -0700685// Read FRU data
686ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
687 ipmi_request_t request,
688 ipmi_response_t response,
689 ipmi_data_len_t data_len,
690 ipmi_context_t context)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500691{
692 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500693 const ReadFruDataRequest* reqptr =
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500694 reinterpret_cast<const ReadFruDataRequest*>(request);
Patrick Venture0b02be92018-08-31 11:55:55 -0700695 auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
Tom Joseph187f5642018-03-29 13:49:06 +0530696
697 auto iter = frus.find(reqptr->fruID);
698 if (iter == frus.end())
699 {
700 *data_len = 0;
701 return IPMI_CC_SENSOR_INVALID;
702 }
703
Marri Devender Raocac383b2017-07-03 13:24:27 -0500704 auto offset =
705 static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS);
706 try
707 {
708 const auto& fruArea = getFruAreaData(reqptr->fruID);
709 auto size = fruArea.size();
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500710
Tom Josephefcd68b2018-04-26 18:46:27 +0530711 if (offset >= size)
712 {
713 return IPMI_CC_PARM_OUT_OF_RANGE;
714 }
715
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500716 // Write the count of response data.
717 if ((offset + reqptr->count) <= size)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500718 {
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500719 resptr->count = reqptr->count;
720 }
721 else
722 {
723 resptr->count = size - offset;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500724 }
Ratan Gupta2848d602018-01-31 20:39:20 +0530725
Ratan Gupta2848d602018-01-31 20:39:20 +0530726 std::copy((fruArea.begin() + offset),
Patrick Venture0b02be92018-08-31 11:55:55 -0700727 (fruArea.begin() + offset + resptr->count), resptr->data);
Ratan Gupta2848d602018-01-31 20:39:20 +0530728
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500729 *data_len = resptr->count + 1; // additional one byte for count
Marri Devender Raocac383b2017-07-03 13:24:27 -0500730 }
731 catch (const InternalFailure& e)
732 {
733 rc = IPMI_CC_UNSPECIFIED_ERROR;
734 *data_len = 0;
735 log<level::ERR>(e.what());
Marri Devender Raocac383b2017-07-03 13:24:27 -0500736 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500737 return rc;
738}
739
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600740ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700741 ipmi_request_t request,
742 ipmi_response_t response,
743 ipmi_data_len_t data_len,
744 ipmi_context_t context)
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600745{
746 constexpr auto sdrVersion = 0x51;
Patrick Venture0b02be92018-08-31 11:55:55 -0700747 auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600748
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700749 std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600750
751 responseData->sdrVersion = sdrVersion;
752
753 uint16_t records = frus.size() + sensors.size();
754 responseData->recordCountMs = records >> 8;
755 responseData->recordCountLs = records;
756
757 responseData->freeSpace[0] = 0xFF;
758 responseData->freeSpace[1] = 0xFF;
759
760 *data_len = sizeof(GetRepositoryInfoResponse);
761
762 return IPMI_CC_OK;
763}
Chris Austenb4f5b922015-10-13 12:44:43 -0500764
Chris Austenb4f5b922015-10-13 12:44:43 -0500765void register_netfn_storage_functions()
766{
Tom05732372016-09-06 17:21:23 +0530767 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700768 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
769 ipmi_storage_wildcard, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500770
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530771 // <Get SEL Info>
Patrick Venture0b02be92018-08-31 11:55:55 -0700772 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
773 getSELInfo, PRIVILEGE_USER);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530774
Tom05732372016-09-06 17:21:23 +0530775 // <Get SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700776 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
777 ipmi_storage_get_sel_time, PRIVILEGE_USER);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500778
Tom05732372016-09-06 17:21:23 +0530779 // <Set SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700780 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
781 ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
Chris Austenb4f5b922015-10-13 12:44:43 -0500782
Tom05732372016-09-06 17:21:23 +0530783 // <Reserve SEL>
Patrick Venture0b02be92018-08-31 11:55:55 -0700784 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
785 ipmi_storage_reserve_sel, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500786
Tom Josepha4953392017-06-30 19:09:47 +0530787 // <Get SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700788 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
789 getSELEntry, PRIVILEGE_USER);
Tom Josepha4953392017-06-30 19:09:47 +0530790
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530791 // <Delete SEL Entry>
Pradeep Kumar00a18d02019-04-26 17:04:28 +0000792 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
793 ipmi::storage::cmdDeleteSelEntry,
794 ipmi::Privilege::Operator, deleteSELEntry);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530795
Tom05732372016-09-06 17:21:23 +0530796 // <Add SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700797 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
798 ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
Tom Joseph2f05bb52017-06-30 19:14:49 +0530799 // <Clear SEL>
Pradeep Kumar4a5a99a2019-04-26 15:22:39 +0000800 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
801 ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
802 clearSEL);
803
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500804 // <Get FRU Inventory Area Info>
Pradeep Kumarb0c794d2019-05-02 13:09:14 +0000805 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
806 ipmi::storage::cmdGetFruInventoryAreaInfo,
807 ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500808
809 // <Add READ FRU Data
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500810 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
Patrick Venture0b02be92018-08-31 11:55:55 -0700811 ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500812
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600813 // <Get Repository Info>
814 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
Patrick Venture0b02be92018-08-31 11:55:55 -0700815 nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600816
Tom Joseph5ca50952018-02-22 00:33:38 +0530817 // <Reserve SDR Repository>
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
819 ipmi_sen_reserve_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530820
821 // <Get SDR>
Patrick Venture0b02be92018-08-31 11:55:55 -0700822 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
823 ipmi_sen_get_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530824
Marri Devender Rao908f7502017-07-10 01:49:54 -0500825 ipmi::fru::registerCallbackHandler();
Chris Austenb4f5b922015-10-13 12:44:43 -0500826 return;
827}