1 #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 
9 #include <arpa/inet.h>
10 #include <mapper.h>
11 #include <systemd/sd-bus.h>
12 
13 #include <algorithm>
14 #include <chrono>
15 #include <cstdio>
16 #include <cstring>
17 #include <filesystem>
18 #include <ipmid/api.hpp>
19 #include <ipmid/utils.hpp>
20 #include <phosphor-logging/elog-errors.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/server.hpp>
23 #include <string>
24 #include <variant>
25 #include <xyz/openbmc_project/Common/error.hpp>
26 
27 void register_netfn_storage_functions() __attribute__((constructor));
28 
29 unsigned int g_sel_time = 0xFFFFFFFF;
30 extern const ipmi::sensor::IdInfoMap sensors;
31 extern const FruMap frus;
32 
33 namespace
34 {
35 constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
36 constexpr auto HOST_TIME_PATH = "/xyz/openbmc_project/time/host";
37 constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
38 constexpr auto PROPERTY_ELAPSED = "Elapsed";
39 
40 const 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 }
47 } // namespace
48 
49 namespace cache
50 {
51 /*
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  */
61 ipmi::sel::ObjectPaths paths;
62 
63 } // namespace cache
64 
65 using InternalFailure =
66     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
67 using namespace phosphor::logging;
68 using namespace ipmi::fru;
69 
70 /**
71  * @enum Device access mode
72  */
73 enum class AccessMode
74 {
75     bytes, ///< Device is accessed by bytes
76     words  ///< Device is accessed by words
77 };
78 
79 ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
80                                  ipmi_request_t request,
81                                  ipmi_response_t response,
82                                  ipmi_data_len_t data_len,
83                                  ipmi_context_t context)
84 {
85     // Status code.
86     ipmi_ret_t rc = IPMI_CC_INVALID;
87     *data_len = 0;
88     return rc;
89 }
90 
91 ipmi_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 {
95     if (*data_len != 0)
96     {
97         *data_len = 0;
98         return IPMI_CC_REQ_DATA_LEN_INVALID;
99     }
100 
101     std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse));
102     auto responseData =
103         reinterpret_cast<ipmi::sel::GetSELInfoResponse*>(outPayload.data());
104 
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 
110     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 
122     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>(
132                 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
133         }
134         catch (InternalFailure& e)
135         {
136         }
137         catch (const std::runtime_error& e)
138         {
139             log<level::ERR>(e.what());
140         }
141     }
142 
143     std::memcpy(response, outPayload.data(), outPayload.size());
144     *data_len = outPayload.size();
145 
146     return IPMI_CC_OK;
147 }
148 
149 ipmi_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 {
153     if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
154     {
155         *data_len = 0;
156         return IPMI_CC_REQ_DATA_LEN_INVALID;
157     }
158 
159     auto requestData =
160         reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
161 
162     if (requestData->reservationID != 0)
163     {
164         if (!checkSELReservation(requestData->reservationID))
165         {
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 
201     ipmi::sel::GetSELEntryResponse record{};
202 
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 
220     // Identify the next SEL record ID
221     if (iter != cache::paths.end())
222     {
223         ++iter;
224         if (iter == cache::paths.end())
225         {
226             record.nextRecordID = ipmi::sel::lastEntry;
227         }
228         else
229         {
230             namespace fs = std::filesystem;
231             fs::path path(*iter);
232             record.nextRecordID = static_cast<uint16_t>(
233                 std::stoul(std::string(path.filename().c_str())));
234         }
235     }
236     else
237     {
238         record.nextRecordID = ipmi::sel::lastEntry;
239     }
240 
241     if (requestData->readLength == ipmi::sel::entireRecord)
242     {
243         std::memcpy(response, &record, sizeof(record));
244         *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;
256         auto readLength =
257             std::min(diff, static_cast<int>(requestData->readLength));
258 
259         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);
264         *data_len = sizeof(record.nextRecordID) + readLength;
265     }
266 
267     return IPMI_CC_OK;
268 }
269 
270 /** @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  */
278 ipmi::RspType<uint16_t // deleted record ID
279               >
280     deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
281 {
282 
283     namespace fs = std::filesystem;
284 
285     if (!checkSELReservation(reservationID))
286     {
287         return ipmi::responseInvalidReservationId();
288     }
289 
290     // Per the IPMI spec, need to cancel the reservation when a SEL entry is
291     // deleted
292     cancelSELReservation();
293 
294     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.
302         return ipmi::responseSensorInvalid();
303     }
304 
305     if (cache::paths.empty())
306     {
307         return ipmi::responseSensorInvalid();
308     }
309 
310     ipmi::sel::ObjectPaths::const_iterator iter;
311     uint16_t delRecordID = 0;
312 
313     if (selRecordID == ipmi::sel::firstEntry)
314     {
315         iter = cache::paths.begin();
316         fs::path path(*iter);
317         delRecordID = static_cast<uint16_t>(
318             std::stoul(std::string(path.filename().c_str())));
319     }
320     else if (selRecordID == ipmi::sel::lastEntry)
321     {
322         iter = cache::paths.end();
323         fs::path path(*iter);
324         delRecordID = static_cast<uint16_t>(
325             std::stoul(std::string(path.filename().c_str())));
326     }
327     else
328     {
329         std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
330                               std::to_string(selRecordID);
331 
332         iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
333         if (iter == cache::paths.end())
334         {
335             return ipmi::responseSensorInvalid();
336         }
337         delRecordID = selRecordID;
338     }
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());
350         return ipmi::responseUnspecifiedError();
351     }
352 
353     auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
354                                           ipmi::sel::logDeleteIntf, "Delete");
355     auto reply = bus.call(methodCall);
356     if (reply.is_method_error())
357     {
358         return ipmi::responseUnspecifiedError();
359     }
360 
361     // Invalidate the cache of dbus entry objects.
362     cache::paths.clear();
363 
364     return ipmi::responseSuccess(delRecordID);
365 }
366 
367 /** @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 
377 ipmi::RspType<uint8_t // erase status
378               >
379     clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
380              uint8_t eraseOperation)
381 {
382     static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
383     if (clr != clrOk)
384     {
385         return ipmi::responseInvalidFieldRequest();
386     }
387 
388     if (!checkSELReservation(reservationID))
389     {
390         return ipmi::responseInvalidReservationId();
391     }
392 
393     /*
394      * Erasure status cannot be fetched from DBUS, so always return erasure
395      * status as `erase completed`.
396      */
397     if (eraseOperation == ipmi::sel::getEraseStatus)
398     {
399         return ipmi::responseSuccess(
400             static_cast<uint8_t>(ipmi::sel::eraseComplete));
401     }
402 
403     // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
404     cancelSELReservation();
405 
406     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
407     ipmi::sel::ObjectPaths objectPaths;
408     auto depth = 0;
409 
410     auto mapperCall =
411         bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
412                             ipmi::sel::mapperIntf, "GetSubTreePaths");
413     mapperCall.append(ipmi::sel::logBasePath);
414     mapperCall.append(depth);
415     mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
416 
417     try
418     {
419         auto reply = bus.call(mapperCall);
420         if (reply.is_method_error())
421         {
422             return ipmi::responseSuccess(
423                 static_cast<uint8_t>(ipmi::sel::eraseComplete));
424         }
425 
426         reply.read(objectPaths);
427         if (objectPaths.empty())
428         {
429             return ipmi::responseSuccess(
430                 static_cast<uint8_t>(ipmi::sel::eraseComplete));
431         }
432     }
433     catch (const sdbusplus::exception::SdBusError& e)
434     {
435         return ipmi::responseSuccess(
436             static_cast<uint8_t>(ipmi::sel::eraseComplete));
437     }
438 
439     std::string service;
440 
441     try
442     {
443         service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
444                                    objectPaths.front());
445     }
446     catch (const std::runtime_error& e)
447     {
448         log<level::ERR>(e.what());
449         return ipmi::responseUnspecifiedError();
450     }
451 
452     for (const auto& iter : objectPaths)
453     {
454         auto methodCall = bus.new_method_call(
455             service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
456 
457         auto reply = bus.call(methodCall);
458         if (reply.is_method_error())
459         {
460             return ipmi::responseUnspecifiedError();
461         }
462     }
463 
464     // Invalidate the cache of dbus entry objects.
465     cache::paths.clear();
466     return ipmi::responseSuccess(
467         static_cast<uint8_t>(ipmi::sel::eraseComplete));
468 }
469 
470 ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
471                                      ipmi_request_t request,
472                                      ipmi_response_t response,
473                                      ipmi_data_len_t data_len,
474                                      ipmi_context_t context)
475 {
476     if (*data_len != 0)
477     {
478         *data_len = 0;
479         return IPMI_CC_REQ_DATA_LEN_INVALID;
480     }
481 
482     using namespace std::chrono;
483     uint64_t host_time_usec = 0;
484     uint32_t resp = 0;
485     std::stringstream hostTime;
486 
487     try
488     {
489         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
490         auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
491         std::variant<uint64_t> value;
492 
493         // Get host time
494         auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
495                                           DBUS_PROPERTIES, "Get");
496 
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);
507         host_time_usec = std::get<uint64_t>(value);
508     }
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;
518     }
519 
520     hostTime << "Host time:" << getTimeString(host_time_usec);
521     log<level::DEBUG>(hostTime.str().c_str());
522 
523     // 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);
528 
529     // From the IPMI Spec 2.0, response should be a 32-bit value
530     *data_len = sizeof(resp);
531 
532     // Pack the actual response
533     std::memcpy(response, &resp, *data_len);
534 
535     return IPMI_CC_OK;
536 }
537 
538 ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
539                                      ipmi_request_t request,
540                                      ipmi_response_t response,
541                                      ipmi_data_len_t data_len,
542                                      ipmi_context_t context)
543 {
544     if (*data_len != sizeof(uint32_t))
545     {
546         *data_len = 0;
547         return IPMI_CC_REQ_DATA_LEN_INVALID;
548     }
549     using namespace std::chrono;
550     ipmi_ret_t rc = IPMI_CC_OK;
551     uint32_t secs = *static_cast<uint32_t*>(request);
552     *data_len = 0;
553 
554     secs = le32toh(secs);
555     microseconds usec{seconds(secs)};
556 
557     try
558     {
559         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
560         auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
561         std::variant<uint64_t> value{usec.count()};
562 
563         // Set host time
564         auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
565                                           DBUS_PROPERTIES, "Set");
566 
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         }
576     }
577     catch (InternalFailure& e)
578     {
579         log<level::ERR>(e.what());
580         rc = IPMI_CC_UNSPECIFIED_ERROR;
581     }
582     catch (const std::runtime_error& e)
583     {
584         log<level::ERR>(e.what());
585         rc = IPMI_CC_UNSPECIFIED_ERROR;
586     }
587 
588     return rc;
589 }
590 
591 ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
592                                     ipmi_request_t request,
593                                     ipmi_response_t response,
594                                     ipmi_data_len_t data_len,
595                                     ipmi_context_t context)
596 {
597     if (*data_len != 0)
598     {
599         *data_len = 0;
600         return IPMI_CC_REQ_DATA_LEN_INVALID;
601     }
602 
603     ipmi_ret_t rc = IPMI_CC_OK;
604     unsigned short selResID = reserveSel();
605 
606     *data_len = sizeof(selResID);
607 
608     // Pack the actual response
609     std::memcpy(response, &selResID, *data_len);
610 
611     return rc;
612 }
613 
614 ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
615                                 ipmi_request_t request,
616                                 ipmi_response_t response,
617                                 ipmi_data_len_t data_len,
618                                 ipmi_context_t context)
619 {
620     if (*data_len != sizeof(ipmi_add_sel_request_t))
621     {
622         *data_len = 0;
623         return IPMI_CC_REQ_DATA_LEN_INVALID;
624     }
625 
626     ipmi_ret_t rc = IPMI_CC_OK;
627     ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
628     uint16_t recordid;
629 
630     // Per the IPMI spec, need to cancel the reservation when a SEL entry is
631     // added
632     cancelSELReservation();
633 
634     recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
635 
636     *data_len = sizeof(recordid);
637 
638     // Pack the actual response
639     std::memcpy(response, &p->eventdata[1], 2);
640 
641     // 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     }
650 
651     return rc;
652 }
653 
654 /** @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  **/
660 ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
661               uint8_t   // access size (bytes / words)
662               >
663     ipmiStorageGetFruInvAreaInfo(uint8_t fruID)
664 {
665 
666     auto iter = frus.find(fruID);
667     if (iter == frus.end())
668     {
669         return ipmi::responseSensorInvalid();
670     }
671 
672     try
673     {
674         return ipmi::responseSuccess(
675             static_cast<uint16_t>(getFruAreaData(fruID).size()),
676             static_cast<uint8_t>(AccessMode::bytes));
677     }
678     catch (const InternalFailure& e)
679     {
680         log<level::ERR>(e.what());
681         return ipmi::responseUnspecifiedError();
682     }
683 }
684 
685 // Read FRU data
686 ipmi_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)
691 {
692     ipmi_ret_t rc = IPMI_CC_OK;
693     const ReadFruDataRequest* reqptr =
694         reinterpret_cast<const ReadFruDataRequest*>(request);
695     auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
696 
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 
704     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();
710 
711         if (offset >= size)
712         {
713             return IPMI_CC_PARM_OUT_OF_RANGE;
714         }
715 
716         // Write the count of response data.
717         if ((offset + reqptr->count) <= size)
718         {
719             resptr->count = reqptr->count;
720         }
721         else
722         {
723             resptr->count = size - offset;
724         }
725 
726         std::copy((fruArea.begin() + offset),
727                   (fruArea.begin() + offset + resptr->count), resptr->data);
728 
729         *data_len = resptr->count + 1; // additional one byte for count
730     }
731     catch (const InternalFailure& e)
732     {
733         rc = IPMI_CC_UNSPECIFIED_ERROR;
734         *data_len = 0;
735         log<level::ERR>(e.what());
736     }
737     return rc;
738 }
739 
740 ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
741                                     ipmi_request_t request,
742                                     ipmi_response_t response,
743                                     ipmi_data_len_t data_len,
744                                     ipmi_context_t context)
745 {
746     constexpr auto sdrVersion = 0x51;
747     auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
748 
749     std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
750 
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 }
764 
765 void register_netfn_storage_functions()
766 {
767     // <Wildcard Command>
768     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
769                            ipmi_storage_wildcard, PRIVILEGE_USER);
770 
771     // <Get SEL Info>
772     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
773                            getSELInfo, PRIVILEGE_USER);
774 
775     // <Get SEL Time>
776     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
777                            ipmi_storage_get_sel_time, PRIVILEGE_USER);
778 
779     // <Set SEL Time>
780     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
781                            ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
782 
783     // <Reserve SEL>
784     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
785                            ipmi_storage_reserve_sel, PRIVILEGE_USER);
786 
787     // <Get SEL Entry>
788     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
789                            getSELEntry, PRIVILEGE_USER);
790 
791     // <Delete SEL Entry>
792     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
793                           ipmi::storage::cmdDeleteSelEntry,
794                           ipmi::Privilege::Operator, deleteSELEntry);
795 
796     // <Add SEL Entry>
797     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
798                            ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
799     // <Clear SEL>
800     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
801                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
802                           clearSEL);
803 
804     // <Get FRU Inventory Area Info>
805     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
806                           ipmi::storage::cmdGetFruInventoryAreaInfo,
807                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
808 
809     // <Add READ FRU Data
810     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
811                            ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
812 
813     // <Get Repository Info>
814     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
815                            nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
816 
817     // <Reserve SDR Repository>
818     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
819                            ipmi_sen_reserve_sdr, PRIVILEGE_USER);
820 
821     // <Get SDR>
822     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
823                            ipmi_sen_get_sdr, PRIVILEGE_USER);
824 
825     ipmi::fru::registerCallbackHandler();
826     return;
827 }
828