blob: cff0d84bf5c6e504cc7ae9f80f8cde268ec560bd [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>
William A. Kennington III194375f2018-12-14 02:14:33 -080011#include <ipmid/api.h>
Patrick Venture3a5071a2018-09-12 13:27:42 -070012#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 }
Chris Austenb4f5b922015-10-13 12:44:43 -0500680
681 return rc;
682}
683
Patrick Venture0b02be92018-08-31 11:55:55 -0700684// Read FRU info area
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500685ipmi_ret_t ipmi_storage_get_fru_inv_area_info(
Patrick Venture0b02be92018-08-31 11:55:55 -0700686 ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
687 ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500688{
689 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500690 const FruInvenAreaInfoRequest* reqptr =
691 reinterpret_cast<const FruInvenAreaInfoRequest*>(request);
Tom Joseph187f5642018-03-29 13:49:06 +0530692
693 auto iter = frus.find(reqptr->fruID);
694 if (iter == frus.end())
695 {
696 *data_len = 0;
697 return IPMI_CC_SENSOR_INVALID;
698 }
699
Marri Devender Raocac383b2017-07-03 13:24:27 -0500700 try
701 {
702 const auto& fruArea = getFruAreaData(reqptr->fruID);
703 auto size = static_cast<uint16_t>(fruArea.size());
704 FruInvenAreaInfoResponse resp;
705 resp.sizems = size >> 8;
706 resp.sizels = size;
707 resp.access = static_cast<uint8_t>(AccessMode::bytes);
708
709 *data_len = sizeof(resp);
710
711 // Pack the actual response
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700712 std::memcpy(response, &resp, *data_len);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500713 }
Patrick Venture0b02be92018-08-31 11:55:55 -0700714 catch (const InternalFailure& e)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500715 {
716 rc = IPMI_CC_UNSPECIFIED_ERROR;
717 *data_len = 0;
718 log<level::ERR>(e.what());
Marri Devender Raocac383b2017-07-03 13:24:27 -0500719 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500720 return rc;
721}
722
Patrick Venture0b02be92018-08-31 11:55:55 -0700723// Read FRU data
724ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
725 ipmi_request_t request,
726 ipmi_response_t response,
727 ipmi_data_len_t data_len,
728 ipmi_context_t context)
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500729{
730 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500731 const ReadFruDataRequest* reqptr =
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500732 reinterpret_cast<const ReadFruDataRequest*>(request);
Patrick Venture0b02be92018-08-31 11:55:55 -0700733 auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
Tom Joseph187f5642018-03-29 13:49:06 +0530734
735 auto iter = frus.find(reqptr->fruID);
736 if (iter == frus.end())
737 {
738 *data_len = 0;
739 return IPMI_CC_SENSOR_INVALID;
740 }
741
Marri Devender Raocac383b2017-07-03 13:24:27 -0500742 auto offset =
743 static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS);
744 try
745 {
746 const auto& fruArea = getFruAreaData(reqptr->fruID);
747 auto size = fruArea.size();
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500748
Tom Josephefcd68b2018-04-26 18:46:27 +0530749 if (offset >= size)
750 {
751 return IPMI_CC_PARM_OUT_OF_RANGE;
752 }
753
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500754 // Write the count of response data.
755 if ((offset + reqptr->count) <= size)
Marri Devender Raocac383b2017-07-03 13:24:27 -0500756 {
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500757 resptr->count = reqptr->count;
758 }
759 else
760 {
761 resptr->count = size - offset;
Marri Devender Raocac383b2017-07-03 13:24:27 -0500762 }
Ratan Gupta2848d602018-01-31 20:39:20 +0530763
Ratan Gupta2848d602018-01-31 20:39:20 +0530764 std::copy((fruArea.begin() + offset),
Patrick Venture0b02be92018-08-31 11:55:55 -0700765 (fruArea.begin() + offset + resptr->count), resptr->data);
Ratan Gupta2848d602018-01-31 20:39:20 +0530766
Nagaraju Goruganti7f2d7c92018-03-21 11:18:30 -0500767 *data_len = resptr->count + 1; // additional one byte for count
Marri Devender Raocac383b2017-07-03 13:24:27 -0500768 }
769 catch (const InternalFailure& e)
770 {
771 rc = IPMI_CC_UNSPECIFIED_ERROR;
772 *data_len = 0;
773 log<level::ERR>(e.what());
Marri Devender Raocac383b2017-07-03 13:24:27 -0500774 }
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500775 return rc;
776}
777
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600778ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
Patrick Venture0b02be92018-08-31 11:55:55 -0700779 ipmi_request_t request,
780 ipmi_response_t response,
781 ipmi_data_len_t data_len,
782 ipmi_context_t context)
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600783{
784 constexpr auto sdrVersion = 0x51;
Patrick Venture0b02be92018-08-31 11:55:55 -0700785 auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600786
Patrick Ventureb51bf9c2018-09-10 15:53:14 -0700787 std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600788
789 responseData->sdrVersion = sdrVersion;
790
791 uint16_t records = frus.size() + sensors.size();
792 responseData->recordCountMs = records >> 8;
793 responseData->recordCountLs = records;
794
795 responseData->freeSpace[0] = 0xFF;
796 responseData->freeSpace[1] = 0xFF;
797
798 *data_len = sizeof(GetRepositoryInfoResponse);
799
800 return IPMI_CC_OK;
801}
Chris Austenb4f5b922015-10-13 12:44:43 -0500802
Chris Austenb4f5b922015-10-13 12:44:43 -0500803void register_netfn_storage_functions()
804{
Tom05732372016-09-06 17:21:23 +0530805 // <Wildcard Command>
Patrick Venture0b02be92018-08-31 11:55:55 -0700806 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
807 ipmi_storage_wildcard, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500808
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530809 // <Get SEL Info>
Patrick Venture0b02be92018-08-31 11:55:55 -0700810 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
811 getSELInfo, PRIVILEGE_USER);
Tom Joseph6f7deaa2017-06-30 19:03:54 +0530812
Tom05732372016-09-06 17:21:23 +0530813 // <Get SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700814 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
815 ipmi_storage_get_sel_time, PRIVILEGE_USER);
Adriana Kobylak8e30f2a2015-10-20 10:23:51 -0500816
Tom05732372016-09-06 17:21:23 +0530817 // <Set SEL Time>
Patrick Venture0b02be92018-08-31 11:55:55 -0700818 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
819 ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
Chris Austenb4f5b922015-10-13 12:44:43 -0500820
Tom05732372016-09-06 17:21:23 +0530821 // <Reserve SEL>
Patrick Venture0b02be92018-08-31 11:55:55 -0700822 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
823 ipmi_storage_reserve_sel, PRIVILEGE_USER);
Chris Austenb4f5b922015-10-13 12:44:43 -0500824
Tom Josepha4953392017-06-30 19:09:47 +0530825 // <Get SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700826 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
827 getSELEntry, PRIVILEGE_USER);
Tom Josepha4953392017-06-30 19:09:47 +0530828
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530829 // <Delete SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700830 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL,
831 deleteSELEntry, PRIVILEGE_OPERATOR);
Tom Joseph8f4a2aa2017-06-30 19:12:49 +0530832
Tom05732372016-09-06 17:21:23 +0530833 // <Add SEL Entry>
Patrick Venture0b02be92018-08-31 11:55:55 -0700834 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
835 ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
Tom Joseph2f05bb52017-06-30 19:14:49 +0530836 // <Clear SEL>
Tom Joseph2f05bb52017-06-30 19:14:49 +0530837 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL,
838 PRIVILEGE_OPERATOR);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500839 // <Get FRU Inventory Area Info>
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500840 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL,
Patrick Venture0b02be92018-08-31 11:55:55 -0700841 ipmi_storage_get_fru_inv_area_info,
842 PRIVILEGE_OPERATOR);
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500843
844 // <Add READ FRU Data
Marri Devender Raofa7b4e22017-07-03 00:52:20 -0500845 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
Patrick Venture0b02be92018-08-31 11:55:55 -0700846 ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
Marri Devender Raocac383b2017-07-03 13:24:27 -0500847
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600848 // <Get Repository Info>
849 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
Patrick Venture0b02be92018-08-31 11:55:55 -0700850 nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
Dhruvaraj Subhashchandrane66c3b02018-02-07 01:21:56 -0600851
Tom Joseph5ca50952018-02-22 00:33:38 +0530852 // <Reserve SDR Repository>
Patrick Venture0b02be92018-08-31 11:55:55 -0700853 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
854 ipmi_sen_reserve_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530855
856 // <Get SDR>
Patrick Venture0b02be92018-08-31 11:55:55 -0700857 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
858 ipmi_sen_get_sdr, PRIVILEGE_USER);
Tom Joseph5ca50952018-02-22 00:33:38 +0530859
Marri Devender Rao908f7502017-07-10 01:49:54 -0500860 ipmi::fru::registerCallbackHandler();
Chris Austenb4f5b922015-10-13 12:44:43 -0500861 return;
862}