blob: e142d9f17e37b0adfd78492092d7791071663585 [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"
8#include "utils.hpp"
9
Lei YU52d91242017-10-17 22:52:28 +080010#include <arpa/inet.h>
Patrick Venture3a5071a2018-09-12 13:27:42 -070011#include <host-ipmid/ipmid-api.h>
12#include <mapper.h>
13#include <systemd/sd-bus.h>
Patrick Venture0b02be92018-08-31 11:55:55 -070014
15#include <algorithm>
Lei YU52d91242017-10-17 22:52:28 +080016#include <chrono>
17#include <cstdio>
Patrick Ventureb51bf9c2018-09-10 15:53:14 -070018#include <cstring>
Patrick Venture3a5071a2018-09-12 13:27:42 -070019#include <phosphor-logging/elog-errors.hpp>
20#include <phosphor-logging/log.hpp>
William A. Kennington III4c008022018-10-12 17:18:14 -070021#include <sdbusplus/message/types.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070022#include <sdbusplus/server.hpp>
23#include <string>
24#include <xyz/openbmc_project/Common/error.hpp>
25
Vernon Mauery185b9f82018-07-20 10:52:36 -070026#if __has_include(<filesystem>)
27#include <filesystem>
28#elif __has_include(<experimental/filesystem>)
Tom Josepha4953392017-06-30 19:09:47 +053029#include <experimental/filesystem>
Patrick Venture0b02be92018-08-31 11:55:55 -070030namespace std
31{
32// splice experimental::filesystem into std
33namespace filesystem = std::experimental::filesystem;
34} // namespace std
Vernon Mauery185b9f82018-07-20 10:52:36 -070035#else
Patrick Venture0b02be92018-08-31 11:55:55 -070036#error filesystem not available
Vernon Mauery185b9f82018-07-20 10:52:36 -070037#endif
Patrick Venture0b02be92018-08-31 11:55:55 -070038
Chris Austenb4f5b922015-10-13 12:44:43 -050039void register_netfn_storage_functions() __attribute__((constructor));
40
Patrick Venture0b02be92018-08-31 11:55:55 -070041unsigned int g_sel_time = 0xFFFFFFFF;
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -060042extern const ipmi::sensor::IdInfoMap sensors;
43extern const FruMap frus;
Chris Austenb4f5b922015-10-13 12:44:43 -050044
Patrick Venture0b02be92018-08-31 11:55:55 -070045namespace
46{
Lei YUe8939392017-06-15 10:45:05 +080047constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
48constexpr auto HOST_TIME_PATH = "/xyz/openbmc_project/time/host";
49constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
Patrick Venture0b02be92018-08-31 11:55:55 -070050constexpr auto PROPERTY_ELAPSED = "Elapsed";
Lei YUe8939392017-06-15 10:45:05 +080051
52const char* getTimeString(const uint64_t& usecSinceEpoch)
53{
54 using namespace std::chrono;
55 system_clock::time_point tp{microseconds(usecSinceEpoch)};
56 auto t = system_clock::to_time_t(tp);
57 return std::ctime(&t);
58}
Patrick Venture0b02be92018-08-31 11:55:55 -070059} // namespace
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +053060
Tom Joseph6f7deaa2017-06-30 19:03:54 +053061namespace cache
62{
Patrick Venture0b02be92018-08-31 11:55:55 -070063/*
64 * This cache contains the object paths of the logging entries sorted in the
65 * order of the filename(numeric order). The cache is initialized by
66 * invoking readLoggingObjectPaths with the cache as the parameter. The
67 * cache is invoked in the execution of the Get SEL info and Delete SEL
68 * entry command. The Get SEL Info command is typically invoked before the
69 * Get SEL entry command, so the cache is utilized for responding to Get SEL
70 * entry command. The cache is invalidated by clearing after Delete SEL
71 * entry and Clear SEL command.
72 */
73ipmi::sel::ObjectPaths paths;
Tom Joseph6f7deaa2017-06-30 19:03:54 +053074
Patrick Venture0b02be92018-08-31 11:55:55 -070075} // namespace cache
Tom Joseph6f7deaa2017-06-30 19:03:54 +053076
William A. Kennington III4c008022018-10-12 17:18:14 -070077namespace variant_ns = sdbusplus::message::variant_ns;
78
Tom Joseph6f7deaa2017-06-30 19:03:54 +053079using InternalFailure =
Patrick Venture0b02be92018-08-31 11:55:55 -070080 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Tom Joseph6f7deaa2017-06-30 19:03:54 +053081using namespace phosphor::logging;
Marri Devender Raocac383b2017-07-03 13:24:27 -050082using namespace ipmi::fru;
83
84/**
85 * @enum Device access mode
86 */
87enum class AccessMode
88{
89 bytes, ///< Device is accessed by bytes
90 words ///< Device is accessed by words
91};
92
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -050093ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -070094 ipmi_request_t request,
95 ipmi_response_t response,
96 ipmi_data_len_t data_len,
97 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -050098{
Chris Austenb4f5b922015-10-13 12:44:43 -050099 // Status code.
Nan Li70aa8d92016-08-29 00:11:10 +0800100 ipmi_ret_t rc = IPMI_CC_INVALID;
Chris Austenb4f5b922015-10-13 12:44:43 -0500101 *data_len = 0;
102 return rc;
103}
104
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530105ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
106 ipmi_request_t request, ipmi_response_t response,
107 ipmi_data_len_t data_len, ipmi_context_t context)
108{
Jason M. Bills851acb12019-02-04 14:06:57 -0800109 if (*data_len != 0)
110 {
111 *data_len = 0;
112 return IPMI_CC_REQ_DATA_LEN_INVALID;
113 }
114
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530115 std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse));
Patrick Venture0b02be92018-08-31 11:55:55 -0700116 auto responseData =
117 reinterpret_cast<ipmi::sel::GetSELInfoResponse*>(outPayload.data());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530118
119 responseData->selVersion = ipmi::sel::selVersion;
120 // Last erase timestamp is not available from log manager.
121 responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp;
122 responseData->operationSupport = ipmi::sel::operationSupport;
123
Tom Josephe59abfb2018-08-06 18:46:27 +0530124 try
125 {
126 ipmi::sel::readLoggingObjectPaths(cache::paths);
127 }
128 catch (const sdbusplus::exception::SdBusError& e)
129 {
130 // No action if reading log objects have failed for this command.
131 // readLoggingObjectPaths will throw exception if there are no log
132 // entries. The command will be responded with number of SEL entries
133 // as 0.
134 }
135
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530136 responseData->entries = 0;
137 responseData->addTimeStamp = ipmi::sel::invalidTimeStamp;
138
139 if (!cache::paths.empty())
140 {
141 responseData->entries = static_cast<uint16_t>(cache::paths.size());
142
143 try
144 {
145 responseData->addTimeStamp = static_cast<uint32_t>(
Patrick Venture0b02be92018-08-31 11:55:55 -0700146 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530147 }
148 catch (InternalFailure& e)
149 {
150 }
151 catch (const std::runtime_error& e)
152 {
153 log<level::ERR>(e.what());
154 }
155 }
156
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700157 std::memcpy(response, outPayload.data(), outPayload.size());
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530158 *data_len = outPayload.size();
159
160 return IPMI_CC_OK;
161}
162
Tom Josepha4953392017-06-30 19:09:47 +0530163ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
164 ipmi_request_t request, ipmi_response_t response,
165 ipmi_data_len_t data_len, ipmi_context_t context)
166{
Jason M. Bills851acb12019-02-04 14:06:57 -0800167 if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
168 {
169 *data_len = 0;
170 return IPMI_CC_REQ_DATA_LEN_INVALID;
171 }
172
Patrick Venture0b02be92018-08-31 11:55:55 -0700173 auto requestData =
174 reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
Tom Josepha4953392017-06-30 19:09:47 +0530175
176 if (requestData->reservationID != 0)
177 {
Jason M. Bills13e67c82018-09-10 14:12:16 -0700178 if (!checkSELReservation(requestData->reservationID))
Tom Josepha4953392017-06-30 19:09:47 +0530179 {
180 *data_len = 0;
181 return IPMI_CC_INVALID_RESERVATION_ID;
182 }
183 }
184
185 if (cache::paths.empty())
186 {
187 *data_len = 0;
188 return IPMI_CC_SENSOR_INVALID;
189 }
190
191 ipmi::sel::ObjectPaths::const_iterator iter;
192
193 // Check for the requested SEL Entry.
194 if (requestData->selRecordID == ipmi::sel::firstEntry)
195 {
196 iter = cache::paths.begin();
197 }
198 else if (requestData->selRecordID == ipmi::sel::lastEntry)
199 {
200 iter = cache::paths.end();
201 }
202 else
203 {
204 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
205 std::to_string(requestData->selRecordID);
206
207 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
208 if (iter == cache::paths.end())
209 {
210 *data_len = 0;
211 return IPMI_CC_SENSOR_INVALID;
212 }
213 }
214
Patrick Venture0b02be92018-08-31 11:55:55 -0700215 ipmi::sel::GetSELEntryResponse record{};
Tom Josepha4953392017-06-30 19:09:47 +0530216
217 // Convert the log entry into SEL record.
218 try
219 {
220 record = ipmi::sel::convertLogEntrytoSEL(*iter);
221 }
222 catch (InternalFailure& e)
223 {
224 *data_len = 0;
225 return IPMI_CC_UNSPECIFIED_ERROR;
226 }
227 catch (const std::runtime_error& e)
228 {
229 log<level::ERR>(e.what());
230 *data_len = 0;
231 return IPMI_CC_UNSPECIFIED_ERROR;
232 }
233
Tom Josepha4953392017-06-30 19:09:47 +0530234 // Identify the next SEL record ID
Patrick Venture0b02be92018-08-31 11:55:55 -0700235 if (iter != cache::paths.end())
Tom Josepha4953392017-06-30 19:09:47 +0530236 {
237 ++iter;
238 if (iter == cache::paths.end())
239 {
240 record.nextRecordID = ipmi::sel::lastEntry;
241 }
242 else
243 {
Vernon Mauery185b9f82018-07-20 10:52:36 -0700244 namespace fs = std::filesystem;
Tom Josepha4953392017-06-30 19:09:47 +0530245 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700246 record.nextRecordID = static_cast<uint16_t>(
247 std::stoul(std::string(path.filename().c_str())));
Tom Josepha4953392017-06-30 19:09:47 +0530248 }
249 }
250 else
251 {
252 record.nextRecordID = ipmi::sel::lastEntry;
253 }
254
255 if (requestData->readLength == ipmi::sel::entireRecord)
256 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700257 std::memcpy(response, &record, sizeof(record));
Tom Josepha4953392017-06-30 19:09:47 +0530258 *data_len = sizeof(record);
259 }
260 else
261 {
262 if (requestData->offset >= ipmi::sel::selRecordSize ||
263 requestData->readLength > ipmi::sel::selRecordSize)
264 {
265 *data_len = 0;
266 return IPMI_CC_INVALID_FIELD_REQUEST;
267 }
268
269 auto diff = ipmi::sel::selRecordSize - requestData->offset;
Patrick Venture0b02be92018-08-31 11:55:55 -0700270 auto readLength =
271 std::min(diff, static_cast<int>(requestData->readLength));
Tom Josepha4953392017-06-30 19:09:47 +0530272
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700273 std::memcpy(response, &record.nextRecordID,
274 sizeof(record.nextRecordID));
275 std::memcpy(static_cast<uint8_t*>(response) +
276 sizeof(record.nextRecordID),
277 &record.recordID + requestData->offset, readLength);
Tom Josepha4953392017-06-30 19:09:47 +0530278 *data_len = sizeof(record.nextRecordID) + readLength;
279 }
280
281 return IPMI_CC_OK;
282}
283
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530284ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
285 ipmi_request_t request, ipmi_response_t response,
286 ipmi_data_len_t data_len, ipmi_context_t context)
287{
Jason M. Bills851acb12019-02-04 14:06:57 -0800288 if (*data_len != sizeof(ipmi::sel::DeleteSELEntryRequest))
289 {
290 *data_len = 0;
291 return IPMI_CC_REQ_DATA_LEN_INVALID;
292 }
293
Brad Bishop1a4117b2018-11-21 15:48:18 -0500294 namespace fs = std::filesystem;
Patrick Venture0b02be92018-08-31 11:55:55 -0700295 auto requestData =
296 reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*>(request);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530297
Jason M. Bills13e67c82018-09-10 14:12:16 -0700298 if (!checkSELReservation(requestData->reservationID))
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530299 {
300 *data_len = 0;
301 return IPMI_CC_INVALID_RESERVATION_ID;
302 }
303
Jason M. Bills13e67c82018-09-10 14:12:16 -0700304 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
305 // deleted
306 cancelSELReservation();
307
Tom Josephe59abfb2018-08-06 18:46:27 +0530308 try
309 {
310 ipmi::sel::readLoggingObjectPaths(cache::paths);
311 }
312 catch (const sdbusplus::exception::SdBusError& e)
313 {
314 // readLoggingObjectPaths will throw exception if there are no error
315 // log entries.
316 *data_len = 0;
317 return IPMI_CC_SENSOR_INVALID;
318 }
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530319
320 if (cache::paths.empty())
321 {
322 *data_len = 0;
323 return IPMI_CC_SENSOR_INVALID;
324 }
325
326 ipmi::sel::ObjectPaths::const_iterator iter;
327 uint16_t delRecordID = 0;
328
329 if (requestData->selRecordID == ipmi::sel::firstEntry)
330 {
331 iter = cache::paths.begin();
332 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700333 delRecordID = static_cast<uint16_t>(
334 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530335 }
336 else if (requestData->selRecordID == ipmi::sel::lastEntry)
337 {
338 iter = cache::paths.end();
339 fs::path path(*iter);
Patrick Venture0b02be92018-08-31 11:55:55 -0700340 delRecordID = static_cast<uint16_t>(
341 std::stoul(std::string(path.filename().c_str())));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530342 }
343 else
344 {
345 std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
346 std::to_string(requestData->selRecordID);
347
348 iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
349 if (iter == cache::paths.end())
350 {
351 *data_len = 0;
352 return IPMI_CC_SENSOR_INVALID;
353 }
354 delRecordID = requestData->selRecordID;
355 }
356
357 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
358 std::string service;
359
360 try
361 {
362 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter);
363 }
364 catch (const std::runtime_error& e)
365 {
366 log<level::ERR>(e.what());
367 *data_len = 0;
368 return IPMI_CC_UNSPECIFIED_ERROR;
369 }
370
Patrick Venture0b02be92018-08-31 11:55:55 -0700371 auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
372 ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530373 auto reply = bus.call(methodCall);
374 if (reply.is_method_error())
375 {
376 *data_len = 0;
377 return IPMI_CC_UNSPECIFIED_ERROR;
378 }
379
380 // Invalidate the cache of dbus entry objects.
381 cache::paths.clear();
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700382 std::memcpy(response, &delRecordID, sizeof(delRecordID));
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530383 *data_len = sizeof(delRecordID);
384
385 return IPMI_CC_OK;
386}
387
Tom Joseph2f05bb52017-06-30 19:14:49 +0530388ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
389 ipmi_response_t response, ipmi_data_len_t data_len,
390 ipmi_context_t context)
391{
Jason M. Bills851acb12019-02-04 14:06:57 -0800392 if (*data_len != sizeof(ipmi::sel::ClearSELRequest))
393 {
394 *data_len = 0;
395 return IPMI_CC_REQ_DATA_LEN_INVALID;
396 }
397
Patrick Venture0b02be92018-08-31 11:55:55 -0700398 auto requestData =
399 reinterpret_cast<const ipmi::sel::ClearSELRequest*>(request);
Tom Joseph2f05bb52017-06-30 19:14:49 +0530400
Jason M. Bills13e67c82018-09-10 14:12:16 -0700401 if (!checkSELReservation(requestData->reservationID))
Tom Joseph2f05bb52017-06-30 19:14:49 +0530402 {
403 *data_len = 0;
404 return IPMI_CC_INVALID_RESERVATION_ID;
405 }
406
Patrick Venture0b02be92018-08-31 11:55:55 -0700407 if (requestData->charC != 'C' || requestData->charL != 'L' ||
Tom Joseph2f05bb52017-06-30 19:14:49 +0530408 requestData->charR != 'R')
409 {
410 *data_len = 0;
411 return IPMI_CC_INVALID_FIELD_REQUEST;
412 }
413
414 uint8_t eraseProgress = ipmi::sel::eraseComplete;
415
416 /*
417 * Erasure status cannot be fetched from DBUS, so always return erasure
418 * status as `erase completed`.
419 */
420 if (requestData->eraseOperation == ipmi::sel::getEraseStatus)
421 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700422 std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530423 *data_len = sizeof(eraseProgress);
424 return IPMI_CC_OK;
425 }
426
Jason M. Bills13e67c82018-09-10 14:12:16 -0700427 // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
428 cancelSELReservation();
429
Tom Joseph2f05bb52017-06-30 19:14:49 +0530430 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
Tom Josephe59abfb2018-08-06 18:46:27 +0530431 ipmi::sel::ObjectPaths objectPaths;
Tom Joseph2f05bb52017-06-30 19:14:49 +0530432 auto depth = 0;
433
Patrick Venture0b02be92018-08-31 11:55:55 -0700434 auto mapperCall =
435 bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
436 ipmi::sel::mapperIntf, "GetSubTreePaths");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530437 mapperCall.append(ipmi::sel::logBasePath);
438 mapperCall.append(depth);
439 mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
440
Tom Josephe59abfb2018-08-06 18:46:27 +0530441 try
Tom Joseph2f05bb52017-06-30 19:14:49 +0530442 {
Tom Josephe59abfb2018-08-06 18:46:27 +0530443 auto reply = bus.call(mapperCall);
444 if (reply.is_method_error())
445 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700446 std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
Tom Josephe59abfb2018-08-06 18:46:27 +0530447 *data_len = sizeof(eraseProgress);
448 return IPMI_CC_OK;
449 }
Tom Joseph2f05bb52017-06-30 19:14:49 +0530450
Tom Josephe59abfb2018-08-06 18:46:27 +0530451 reply.read(objectPaths);
452 if (objectPaths.empty())
453 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700454 std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
Tom Josephe59abfb2018-08-06 18:46:27 +0530455 *data_len = sizeof(eraseProgress);
456 return IPMI_CC_OK;
457 }
458 }
459 catch (const sdbusplus::exception::SdBusError& e)
Tom Joseph2f05bb52017-06-30 19:14:49 +0530460 {
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700461 std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530462 *data_len = sizeof(eraseProgress);
463 return IPMI_CC_OK;
464 }
465
466 std::string service;
467
468 try
469 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700470 service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
Tom Joseph2f05bb52017-06-30 19:14:49 +0530471 objectPaths.front());
472 }
473 catch (const std::runtime_error& e)
474 {
475 log<level::ERR>(e.what());
476 *data_len = 0;
477 return IPMI_CC_UNSPECIFIED_ERROR;
478 }
479
480 for (const auto& iter : objectPaths)
481 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700482 auto methodCall = bus.new_method_call(
483 service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
Tom Joseph2f05bb52017-06-30 19:14:49 +0530484
485 auto reply = bus.call(methodCall);
486 if (reply.is_method_error())
487 {
488 *data_len = 0;
489 return IPMI_CC_UNSPECIFIED_ERROR;
490 }
491 }
492
493 // Invalidate the cache of dbus entry objects.
494 cache::paths.clear();
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700495 std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
Tom Joseph2f05bb52017-06-30 19:14:49 +0530496 *data_len = sizeof(eraseProgress);
497 return IPMI_CC_OK;
498}
499
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500500ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700501 ipmi_request_t request,
502 ipmi_response_t response,
503 ipmi_data_len_t data_len,
504 ipmi_context_t context)
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500505{
Jason M. Bills851acb12019-02-04 14:06:57 -0800506 if (*data_len != 0)
507 {
508 *data_len = 0;
509 return IPMI_CC_REQ_DATA_LEN_INVALID;
510 }
511
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530512 using namespace std::chrono;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530513 uint64_t host_time_usec = 0;
514 uint32_t resp = 0;
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500515 std::stringstream hostTime;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500516
Lei YUe8939392017-06-15 10:45:05 +0800517 try
518 {
519 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
520 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
521 sdbusplus::message::variant<uint64_t> value;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530522
Lei YUe8939392017-06-15 10:45:05 +0800523 // Get host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700524 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
525 DBUS_PROPERTIES, "Get");
Lei YUe8939392017-06-15 10:45:05 +0800526
527 method.append(TIME_INTERFACE, PROPERTY_ELAPSED);
528 auto reply = bus.call(method);
529 if (reply.is_method_error())
530 {
531 log<level::ERR>("Error getting time",
532 entry("SERVICE=%s", service.c_str()),
533 entry("PATH=%s", HOST_TIME_PATH));
534 return IPMI_CC_UNSPECIFIED_ERROR;
535 }
536 reply.read(value);
William A. Kennington III4c008022018-10-12 17:18:14 -0700537 host_time_usec = variant_ns::get<uint64_t>(value);
Lei YUe8939392017-06-15 10:45:05 +0800538 }
539 catch (InternalFailure& e)
540 {
541 log<level::ERR>(e.what());
542 return IPMI_CC_UNSPECIFIED_ERROR;
543 }
544 catch (const std::runtime_error& e)
545 {
546 log<level::ERR>(e.what());
547 return IPMI_CC_UNSPECIFIED_ERROR;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530548 }
549
Nagaraju Goruganti8960b7c2018-04-29 22:38:40 -0500550 hostTime << "Host time:" << getTimeString(host_time_usec);
551 log<level::DEBUG>(hostTime.str().c_str());
552
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530553 // Time is really long int but IPMI wants just uint32. This works okay until
554 // the number of seconds since 1970 overflows uint32 size.. Still a whole
555 // lot of time here to even think about that.
556 resp = duration_cast<seconds>(microseconds(host_time_usec)).count();
557 resp = htole32(resp);
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530558
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500559 // From the IPMI Spec 2.0, response should be a 32-bit value
560 *data_len = sizeof(resp);
561
562 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700563 std::memcpy(response, &resp, *data_len);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500564
Lei YUe8939392017-06-15 10:45:05 +0800565 return IPMI_CC_OK;
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500566}
567
568ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700569 ipmi_request_t request,
570 ipmi_response_t response,
571 ipmi_data_len_t data_len,
572 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500573{
Jason M. Bills851acb12019-02-04 14:06:57 -0800574 if (*data_len != sizeof(uint32_t))
575 {
576 *data_len = 0;
577 return IPMI_CC_REQ_DATA_LEN_INVALID;
578 }
Lei YUe8939392017-06-15 10:45:05 +0800579 using namespace std::chrono;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530580 ipmi_ret_t rc = IPMI_CC_OK;
Lei YUe8939392017-06-15 10:45:05 +0800581 uint32_t secs = *static_cast<uint32_t*>(request);
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530582 *data_len = 0;
Chris Austenb4f5b922015-10-13 12:44:43 -0500583
Lei YUe8939392017-06-15 10:45:05 +0800584 secs = le32toh(secs);
585 microseconds usec{seconds(secs)};
Norman James82330442015-11-19 16:53:26 -0600586
Lei YUe8939392017-06-15 10:45:05 +0800587 try
588 {
589 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
590 auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
591 sdbusplus::message::variant<uint64_t> value{usec.count()};
592
593 // Set host time
Patrick Venture0b02be92018-08-31 11:55:55 -0700594 auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
595 DBUS_PROPERTIES, "Set");
Lei YUe8939392017-06-15 10:45:05 +0800596
597 method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
598 auto reply = bus.call(method);
599 if (reply.is_method_error())
600 {
601 log<level::ERR>("Error setting time",
602 entry("SERVICE=%s", service.c_str()),
603 entry("PATH=%s", HOST_TIME_PATH));
604 rc = IPMI_CC_UNSPECIFIED_ERROR;
605 }
Norman James82330442015-11-19 16:53:26 -0600606 }
Lei YUe8939392017-06-15 10:45:05 +0800607 catch (InternalFailure& e)
608 {
609 log<level::ERR>(e.what());
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530610 rc = IPMI_CC_UNSPECIFIED_ERROR;
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530611 }
Lei YUe8939392017-06-15 10:45:05 +0800612 catch (const std::runtime_error& e)
613 {
614 log<level::ERR>(e.what());
Norman James82330442015-11-19 16:53:26 -0600615 rc = IPMI_CC_UNSPECIFIED_ERROR;
616 }
Vishwanatha Subbanna5fba7a62016-09-01 14:06:07 +0530617
Chris Austenb4f5b922015-10-13 12:44:43 -0500618 return rc;
619}
620
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500621ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700622 ipmi_request_t request,
623 ipmi_response_t response,
624 ipmi_data_len_t data_len,
625 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500626{
Jason M. Bills851acb12019-02-04 14:06:57 -0800627 if (*data_len != 0)
628 {
629 *data_len = 0;
630 return IPMI_CC_REQ_DATA_LEN_INVALID;
631 }
632
Chris Austenb4f5b922015-10-13 12:44:43 -0500633 ipmi_ret_t rc = IPMI_CC_OK;
Jason M. Bills13e67c82018-09-10 14:12:16 -0700634 unsigned short selResID = reserveSel();
Chris Austenb4f5b922015-10-13 12:44:43 -0500635
Jason M. Bills13e67c82018-09-10 14:12:16 -0700636 *data_len = sizeof(selResID);
Chris Austenb4f5b922015-10-13 12:44:43 -0500637
638 // Pack the actual response
Jason M. Bills13e67c82018-09-10 14:12:16 -0700639 std::memcpy(response, &selResID, *data_len);
Chris Austenb4f5b922015-10-13 12:44:43 -0500640
641 return rc;
642}
643
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500644ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700645 ipmi_request_t request,
646 ipmi_response_t response,
647 ipmi_data_len_t data_len,
648 ipmi_context_t context)
Chris Austenb4f5b922015-10-13 12:44:43 -0500649{
Jason M. Bills851acb12019-02-04 14:06:57 -0800650 if (*data_len != sizeof(ipmi_add_sel_request_t))
651 {
652 *data_len = 0;
653 return IPMI_CC_REQ_DATA_LEN_INVALID;
654 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500655
656 ipmi_ret_t rc = IPMI_CC_OK;
Patrick Venture0b02be92018-08-31 11:55:55 -0700657 ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
Chris Austen41a4b312015-10-25 03:45:42 -0500658 uint16_t recordid;
Chris Austenb4f5b922015-10-13 12:44:43 -0500659
Jason M. Bills13e67c82018-09-10 14:12:16 -0700660 // Per the IPMI spec, need to cancel the reservation when a SEL entry is
661 // added
662 cancelSELReservation();
663
Chris Austen313d95b2015-10-31 12:55:30 -0500664 recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
Chris Austen41a4b312015-10-25 03:45:42 -0500665
Jason M. Bills13e67c82018-09-10 14:12:16 -0700666 *data_len = sizeof(recordid);
Chris Austenb4f5b922015-10-13 12:44:43 -0500667
668 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700669 std::memcpy(response, &p->eventdata[1], 2);
Chris Austenb4f5b922015-10-13 12:44:43 -0500670
Tom Josephb647d5b2017-10-31 17:25:33 +0530671 // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
672 // a maintenance procedure associated with eSEL record.
673 static constexpr auto procedureType = 0xDE;
674 if (p->recordtype == procedureType)
675 {
676 // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
677 // procedure number.
678 createProcedureLogEntry(p->sensortype);
679 }
Tom Josephe19540e2019-02-04 14:06:58 +0530680 else
681 {
682 send_esel(recordid);
683 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500684
685 return rc;
686}
687
Patrick Venture0b02be92018-08-31 11:55:55 -0700688// Read FRU info area
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500689ipmi_ret_t ipmi_storage_get_fru_inv_area_info(
Patrick Venture0b02be92018-08-31 11:55:55 -0700690 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
691 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500692{
693 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500694 const FruInvenAreaInfoRequest* reqptr =
695 reinterpret_cast<const FruInvenAreaInfoRequest*>(request);
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 try
705 {
706 const auto& fruArea = getFruAreaData(reqptr->fruID);
707 auto size = static_cast<uint16_t>(fruArea.size());
708 FruInvenAreaInfoResponse resp;
709 resp.sizems = size >> 8;
710 resp.sizels = size;
711 resp.access = static_cast<uint8_t>(AccessMode::bytes);
712
713 *data_len = sizeof(resp);
714
715 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700716 std::memcpy(response, &resp, *data_len);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500717 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700718 catch (const InternalFailure& e)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500719 {
720 rc = IPMI_CC_UNSPECIFIED_ERROR;
721 *data_len = 0;
722 log<level::ERR>(e.what());
Marri Devender Raocac383b2017-07-03 13:24:27 -0500723 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500724 return rc;
725}
726
Patrick Venture0b02be92018-08-31 11:55:55 -0700727// Read FRU data
728ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
729 ipmi_request_t request,
730 ipmi_response_t response,
731 ipmi_data_len_t data_len,
732 ipmi_context_t context)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500733{
734 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500735 const ReadFruDataRequest* reqptr =
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500736 reinterpret_cast<const ReadFruDataRequest*>(request);
Patrick Venture0b02be92018-08-31 11:55:55 -0700737 auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
Tom Joseph187f5642018-03-29 13:49:06 +0530738
739 auto iter = frus.find(reqptr->fruID);
740 if (iter == frus.end())
741 {
742 *data_len = 0;
743 return IPMI_CC_SENSOR_INVALID;
744 }
745
Marri Devender Raocac383b2017-07-03 13:24:27 -0500746 auto offset =
747 static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS);
748 try
749 {
750 const auto& fruArea = getFruAreaData(reqptr->fruID);
751 auto size = fruArea.size();
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500752
Tom Josephefcd68b2018-04-26 18:46:27 +0530753 if (offset >= size)
754 {
755 return IPMI_CC_PARM_OUT_OF_RANGE;
756 }
757
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500758 // Write the count of response data.
759 if ((offset + reqptr->count) <= size)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500760 {
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500761 resptr->count = reqptr->count;
762 }
763 else
764 {
765 resptr->count = size - offset;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500766 }
Ratan Gupta2848d602018-01-31 20:39:20 +0530767
Ratan Gupta2848d602018-01-31 20:39:20 +0530768 std::copy((fruArea.begin() + offset),
Patrick Venture0b02be92018-08-31 11:55:55 -0700769 (fruArea.begin() + offset + resptr->count), resptr->data);
Ratan Gupta2848d602018-01-31 20:39:20 +0530770
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500771 *data_len = resptr->count + 1; // additional one byte for count
Marri Devender Raocac383b2017-07-03 13:24:27 -0500772 }
773 catch (const InternalFailure& e)
774 {
775 rc = IPMI_CC_UNSPECIFIED_ERROR;
776 *data_len = 0;
777 log<level::ERR>(e.what());
Marri Devender Raocac383b2017-07-03 13:24:27 -0500778 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500779 return rc;
780}
781
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600782ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700783 ipmi_request_t request,
784 ipmi_response_t response,
785 ipmi_data_len_t data_len,
786 ipmi_context_t context)
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600787{
788 constexpr auto sdrVersion = 0x51;
Patrick Venture0b02be92018-08-31 11:55:55 -0700789 auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600790
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700791 std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600792
793 responseData->sdrVersion = sdrVersion;
794
795 uint16_t records = frus.size() + sensors.size();
796 responseData->recordCountMs = records >> 8;
797 responseData->recordCountLs = records;
798
799 responseData->freeSpace[0] = 0xFF;
800 responseData->freeSpace[1] = 0xFF;
801
802 *data_len = sizeof(GetRepositoryInfoResponse);
803
804 return IPMI_CC_OK;
805}
Chris Austenb4f5b922015-10-13 12:44:43 -0500806
Chris Austenb4f5b922015-10-13 12:44:43 -0500807void register_netfn_storage_functions()
808{
Tom05732372016-09-06 17:21:23 +0530809 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700810 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
811 ipmi_storage_wildcard, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500812
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530813 // <Get SEL Info>
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
815 getSELInfo, PRIVILEGE_USER);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530816
Tom05732372016-09-06 17:21:23 +0530817 // <Get SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
819 ipmi_storage_get_sel_time, PRIVILEGE_USER);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500820
Tom05732372016-09-06 17:21:23 +0530821 // <Set SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700822 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
823 ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
Chris Austenb4f5b922015-10-13 12:44:43 -0500824
Tom05732372016-09-06 17:21:23 +0530825 // <Reserve SEL>
Patrick Venture0b02be92018-08-31 11:55:55 -0700826 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
827 ipmi_storage_reserve_sel, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500828
Tom Josepha4953392017-06-30 19:09:47 +0530829 // <Get SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700830 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
831 getSELEntry, PRIVILEGE_USER);
Tom Josepha4953392017-06-30 19:09:47 +0530832
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530833 // <Delete SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700834 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL,
835 deleteSELEntry, PRIVILEGE_OPERATOR);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530836
Tom05732372016-09-06 17:21:23 +0530837 // <Add SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700838 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
839 ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
Tom Joseph2f05bb52017-06-30 19:14:49 +0530840 // <Clear SEL>
Tom Joseph2f05bb52017-06-30 19:14:49 +0530841 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL,
842 PRIVILEGE_OPERATOR);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500843 // <Get FRU Inventory Area Info>
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500844 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL,
Patrick Venture0b02be92018-08-31 11:55:55 -0700845 ipmi_storage_get_fru_inv_area_info,
846 PRIVILEGE_OPERATOR);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500847
848 // <Add READ FRU Data
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500849 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
Patrick Venture0b02be92018-08-31 11:55:55 -0700850 ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500851
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600852 // <Get Repository Info>
853 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
Patrick Venture0b02be92018-08-31 11:55:55 -0700854 nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600855
Tom Joseph5ca50952018-02-22 00:33:38 +0530856 // <Reserve SDR Repository>
Patrick Venture0b02be92018-08-31 11:55:55 -0700857 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
858 ipmi_sen_reserve_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530859
860 // <Get SDR>
Patrick Venture0b02be92018-08-31 11:55:55 -0700861 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
862 ipmi_sen_get_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530863
Marri Devender Rao908f7502017-07-10 01:49:54 -0500864 ipmi::fru::registerCallbackHandler();
Chris Austenb4f5b922015-10-13 12:44:43 -0500865 return;
866}