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 <ipmid/api.h>
11 #include <mapper.h>
12 #include <systemd/sd-bus.h>
13 
14 #include <algorithm>
15 #include <chrono>
16 #include <cstdio>
17 #include <cstring>
18 #include <filesystem>
19 #include <ipmid/utils.hpp>
20 #include <phosphor-logging/elog-errors.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/message/types.hpp>
23 #include <sdbusplus/server.hpp>
24 #include <string>
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 namespace variant_ns = sdbusplus::message::variant_ns;
66 
67 using InternalFailure =
68     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
69 using namespace phosphor::logging;
70 using namespace ipmi::fru;
71 
72 /**
73  * @enum Device access mode
74  */
75 enum class AccessMode
76 {
77     bytes, ///< Device is accessed by bytes
78     words  ///< Device is accessed by words
79 };
80 
81 ipmi_ret_t ipmi_storage_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
82                                  ipmi_request_t request,
83                                  ipmi_response_t response,
84                                  ipmi_data_len_t data_len,
85                                  ipmi_context_t context)
86 {
87     // Status code.
88     ipmi_ret_t rc = IPMI_CC_INVALID;
89     *data_len = 0;
90     return rc;
91 }
92 
93 ipmi_ret_t getSELInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
94                       ipmi_request_t request, ipmi_response_t response,
95                       ipmi_data_len_t data_len, ipmi_context_t context)
96 {
97     if (*data_len != 0)
98     {
99         *data_len = 0;
100         return IPMI_CC_REQ_DATA_LEN_INVALID;
101     }
102 
103     std::vector<uint8_t> outPayload(sizeof(ipmi::sel::GetSELInfoResponse));
104     auto responseData =
105         reinterpret_cast<ipmi::sel::GetSELInfoResponse*>(outPayload.data());
106 
107     responseData->selVersion = ipmi::sel::selVersion;
108     // Last erase timestamp is not available from log manager.
109     responseData->eraseTimeStamp = ipmi::sel::invalidTimeStamp;
110     responseData->operationSupport = ipmi::sel::operationSupport;
111 
112     try
113     {
114         ipmi::sel::readLoggingObjectPaths(cache::paths);
115     }
116     catch (const sdbusplus::exception::SdBusError& e)
117     {
118         // No action if reading log objects have failed for this command.
119         // readLoggingObjectPaths will throw exception if there are no log
120         // entries. The command will be responded with number of SEL entries
121         // as 0.
122     }
123 
124     responseData->entries = 0;
125     responseData->addTimeStamp = ipmi::sel::invalidTimeStamp;
126 
127     if (!cache::paths.empty())
128     {
129         responseData->entries = static_cast<uint16_t>(cache::paths.size());
130 
131         try
132         {
133             responseData->addTimeStamp = static_cast<uint32_t>(
134                 (ipmi::sel::getEntryTimeStamp(cache::paths.back()).count()));
135         }
136         catch (InternalFailure& e)
137         {
138         }
139         catch (const std::runtime_error& e)
140         {
141             log<level::ERR>(e.what());
142         }
143     }
144 
145     std::memcpy(response, outPayload.data(), outPayload.size());
146     *data_len = outPayload.size();
147 
148     return IPMI_CC_OK;
149 }
150 
151 ipmi_ret_t getSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
152                        ipmi_request_t request, ipmi_response_t response,
153                        ipmi_data_len_t data_len, ipmi_context_t context)
154 {
155     if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
156     {
157         *data_len = 0;
158         return IPMI_CC_REQ_DATA_LEN_INVALID;
159     }
160 
161     auto requestData =
162         reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
163 
164     if (requestData->reservationID != 0)
165     {
166         if (!checkSELReservation(requestData->reservationID))
167         {
168             *data_len = 0;
169             return IPMI_CC_INVALID_RESERVATION_ID;
170         }
171     }
172 
173     if (cache::paths.empty())
174     {
175         *data_len = 0;
176         return IPMI_CC_SENSOR_INVALID;
177     }
178 
179     ipmi::sel::ObjectPaths::const_iterator iter;
180 
181     // Check for the requested SEL Entry.
182     if (requestData->selRecordID == ipmi::sel::firstEntry)
183     {
184         iter = cache::paths.begin();
185     }
186     else if (requestData->selRecordID == ipmi::sel::lastEntry)
187     {
188         iter = cache::paths.end();
189     }
190     else
191     {
192         std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
193                               std::to_string(requestData->selRecordID);
194 
195         iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
196         if (iter == cache::paths.end())
197         {
198             *data_len = 0;
199             return IPMI_CC_SENSOR_INVALID;
200         }
201     }
202 
203     ipmi::sel::GetSELEntryResponse record{};
204 
205     // Convert the log entry into SEL record.
206     try
207     {
208         record = ipmi::sel::convertLogEntrytoSEL(*iter);
209     }
210     catch (InternalFailure& e)
211     {
212         *data_len = 0;
213         return IPMI_CC_UNSPECIFIED_ERROR;
214     }
215     catch (const std::runtime_error& e)
216     {
217         log<level::ERR>(e.what());
218         *data_len = 0;
219         return IPMI_CC_UNSPECIFIED_ERROR;
220     }
221 
222     // Identify the next SEL record ID
223     if (iter != cache::paths.end())
224     {
225         ++iter;
226         if (iter == cache::paths.end())
227         {
228             record.nextRecordID = ipmi::sel::lastEntry;
229         }
230         else
231         {
232             namespace fs = std::filesystem;
233             fs::path path(*iter);
234             record.nextRecordID = static_cast<uint16_t>(
235                 std::stoul(std::string(path.filename().c_str())));
236         }
237     }
238     else
239     {
240         record.nextRecordID = ipmi::sel::lastEntry;
241     }
242 
243     if (requestData->readLength == ipmi::sel::entireRecord)
244     {
245         std::memcpy(response, &record, sizeof(record));
246         *data_len = sizeof(record);
247     }
248     else
249     {
250         if (requestData->offset >= ipmi::sel::selRecordSize ||
251             requestData->readLength > ipmi::sel::selRecordSize)
252         {
253             *data_len = 0;
254             return IPMI_CC_INVALID_FIELD_REQUEST;
255         }
256 
257         auto diff = ipmi::sel::selRecordSize - requestData->offset;
258         auto readLength =
259             std::min(diff, static_cast<int>(requestData->readLength));
260 
261         std::memcpy(response, &record.nextRecordID,
262                     sizeof(record.nextRecordID));
263         std::memcpy(static_cast<uint8_t*>(response) +
264                         sizeof(record.nextRecordID),
265                     &record.recordID + requestData->offset, readLength);
266         *data_len = sizeof(record.nextRecordID) + readLength;
267     }
268 
269     return IPMI_CC_OK;
270 }
271 
272 ipmi_ret_t deleteSELEntry(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
273                           ipmi_request_t request, ipmi_response_t response,
274                           ipmi_data_len_t data_len, ipmi_context_t context)
275 {
276     if (*data_len != sizeof(ipmi::sel::DeleteSELEntryRequest))
277     {
278         *data_len = 0;
279         return IPMI_CC_REQ_DATA_LEN_INVALID;
280     }
281 
282     namespace fs = std::filesystem;
283     auto requestData =
284         reinterpret_cast<const ipmi::sel::DeleteSELEntryRequest*>(request);
285 
286     if (!checkSELReservation(requestData->reservationID))
287     {
288         *data_len = 0;
289         return IPMI_CC_INVALID_RESERVATION_ID;
290     }
291 
292     // Per the IPMI spec, need to cancel the reservation when a SEL entry is
293     // deleted
294     cancelSELReservation();
295 
296     try
297     {
298         ipmi::sel::readLoggingObjectPaths(cache::paths);
299     }
300     catch (const sdbusplus::exception::SdBusError& e)
301     {
302         // readLoggingObjectPaths will throw exception if there are no error
303         // log entries.
304         *data_len = 0;
305         return IPMI_CC_SENSOR_INVALID;
306     }
307 
308     if (cache::paths.empty())
309     {
310         *data_len = 0;
311         return IPMI_CC_SENSOR_INVALID;
312     }
313 
314     ipmi::sel::ObjectPaths::const_iterator iter;
315     uint16_t delRecordID = 0;
316 
317     if (requestData->selRecordID == ipmi::sel::firstEntry)
318     {
319         iter = cache::paths.begin();
320         fs::path path(*iter);
321         delRecordID = static_cast<uint16_t>(
322             std::stoul(std::string(path.filename().c_str())));
323     }
324     else if (requestData->selRecordID == ipmi::sel::lastEntry)
325     {
326         iter = cache::paths.end();
327         fs::path path(*iter);
328         delRecordID = static_cast<uint16_t>(
329             std::stoul(std::string(path.filename().c_str())));
330     }
331     else
332     {
333         std::string objPath = std::string(ipmi::sel::logBasePath) + "/" +
334                               std::to_string(requestData->selRecordID);
335 
336         iter = std::find(cache::paths.begin(), cache::paths.end(), objPath);
337         if (iter == cache::paths.end())
338         {
339             *data_len = 0;
340             return IPMI_CC_SENSOR_INVALID;
341         }
342         delRecordID = requestData->selRecordID;
343     }
344 
345     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
346     std::string service;
347 
348     try
349     {
350         service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, *iter);
351     }
352     catch (const std::runtime_error& e)
353     {
354         log<level::ERR>(e.what());
355         *data_len = 0;
356         return IPMI_CC_UNSPECIFIED_ERROR;
357     }
358 
359     auto methodCall = bus.new_method_call(service.c_str(), (*iter).c_str(),
360                                           ipmi::sel::logDeleteIntf, "Delete");
361     auto reply = bus.call(methodCall);
362     if (reply.is_method_error())
363     {
364         *data_len = 0;
365         return IPMI_CC_UNSPECIFIED_ERROR;
366     }
367 
368     // Invalidate the cache of dbus entry objects.
369     cache::paths.clear();
370     std::memcpy(response, &delRecordID, sizeof(delRecordID));
371     *data_len = sizeof(delRecordID);
372 
373     return IPMI_CC_OK;
374 }
375 
376 ipmi_ret_t clearSEL(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
377                     ipmi_response_t response, ipmi_data_len_t data_len,
378                     ipmi_context_t context)
379 {
380     if (*data_len != sizeof(ipmi::sel::ClearSELRequest))
381     {
382         *data_len = 0;
383         return IPMI_CC_REQ_DATA_LEN_INVALID;
384     }
385 
386     auto requestData =
387         reinterpret_cast<const ipmi::sel::ClearSELRequest*>(request);
388 
389     if (!checkSELReservation(requestData->reservationID))
390     {
391         *data_len = 0;
392         return IPMI_CC_INVALID_RESERVATION_ID;
393     }
394 
395     if (requestData->charC != 'C' || requestData->charL != 'L' ||
396         requestData->charR != 'R')
397     {
398         *data_len = 0;
399         return IPMI_CC_INVALID_FIELD_REQUEST;
400     }
401 
402     uint8_t eraseProgress = ipmi::sel::eraseComplete;
403 
404     /*
405      * Erasure status cannot be fetched from DBUS, so always return erasure
406      * status as `erase completed`.
407      */
408     if (requestData->eraseOperation == ipmi::sel::getEraseStatus)
409     {
410         std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
411         *data_len = sizeof(eraseProgress);
412         return IPMI_CC_OK;
413     }
414 
415     // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
416     cancelSELReservation();
417 
418     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
419     ipmi::sel::ObjectPaths objectPaths;
420     auto depth = 0;
421 
422     auto mapperCall =
423         bus.new_method_call(ipmi::sel::mapperBusName, ipmi::sel::mapperObjPath,
424                             ipmi::sel::mapperIntf, "GetSubTreePaths");
425     mapperCall.append(ipmi::sel::logBasePath);
426     mapperCall.append(depth);
427     mapperCall.append(ipmi::sel::ObjectPaths({ipmi::sel::logEntryIntf}));
428 
429     try
430     {
431         auto reply = bus.call(mapperCall);
432         if (reply.is_method_error())
433         {
434             std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
435             *data_len = sizeof(eraseProgress);
436             return IPMI_CC_OK;
437         }
438 
439         reply.read(objectPaths);
440         if (objectPaths.empty())
441         {
442             std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
443             *data_len = sizeof(eraseProgress);
444             return IPMI_CC_OK;
445         }
446     }
447     catch (const sdbusplus::exception::SdBusError& e)
448     {
449         std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
450         *data_len = sizeof(eraseProgress);
451         return IPMI_CC_OK;
452     }
453 
454     std::string service;
455 
456     try
457     {
458         service = ipmi::getService(bus, ipmi::sel::logDeleteIntf,
459                                    objectPaths.front());
460     }
461     catch (const std::runtime_error& e)
462     {
463         log<level::ERR>(e.what());
464         *data_len = 0;
465         return IPMI_CC_UNSPECIFIED_ERROR;
466     }
467 
468     for (const auto& iter : objectPaths)
469     {
470         auto methodCall = bus.new_method_call(
471             service.c_str(), iter.c_str(), ipmi::sel::logDeleteIntf, "Delete");
472 
473         auto reply = bus.call(methodCall);
474         if (reply.is_method_error())
475         {
476             *data_len = 0;
477             return IPMI_CC_UNSPECIFIED_ERROR;
478         }
479     }
480 
481     // Invalidate the cache of dbus entry objects.
482     cache::paths.clear();
483     std::memcpy(response, &eraseProgress, sizeof(eraseProgress));
484     *data_len = sizeof(eraseProgress);
485     return IPMI_CC_OK;
486 }
487 
488 ipmi_ret_t ipmi_storage_get_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
489                                      ipmi_request_t request,
490                                      ipmi_response_t response,
491                                      ipmi_data_len_t data_len,
492                                      ipmi_context_t context)
493 {
494     if (*data_len != 0)
495     {
496         *data_len = 0;
497         return IPMI_CC_REQ_DATA_LEN_INVALID;
498     }
499 
500     using namespace std::chrono;
501     uint64_t host_time_usec = 0;
502     uint32_t resp = 0;
503     std::stringstream hostTime;
504 
505     try
506     {
507         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
508         auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
509         sdbusplus::message::variant<uint64_t> value;
510 
511         // Get host time
512         auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
513                                           DBUS_PROPERTIES, "Get");
514 
515         method.append(TIME_INTERFACE, PROPERTY_ELAPSED);
516         auto reply = bus.call(method);
517         if (reply.is_method_error())
518         {
519             log<level::ERR>("Error getting time",
520                             entry("SERVICE=%s", service.c_str()),
521                             entry("PATH=%s", HOST_TIME_PATH));
522             return IPMI_CC_UNSPECIFIED_ERROR;
523         }
524         reply.read(value);
525         host_time_usec = variant_ns::get<uint64_t>(value);
526     }
527     catch (InternalFailure& e)
528     {
529         log<level::ERR>(e.what());
530         return IPMI_CC_UNSPECIFIED_ERROR;
531     }
532     catch (const std::runtime_error& e)
533     {
534         log<level::ERR>(e.what());
535         return IPMI_CC_UNSPECIFIED_ERROR;
536     }
537 
538     hostTime << "Host time:" << getTimeString(host_time_usec);
539     log<level::DEBUG>(hostTime.str().c_str());
540 
541     // Time is really long int but IPMI wants just uint32. This works okay until
542     // the number of seconds since 1970 overflows uint32 size.. Still a whole
543     // lot of time here to even think about that.
544     resp = duration_cast<seconds>(microseconds(host_time_usec)).count();
545     resp = htole32(resp);
546 
547     // From the IPMI Spec 2.0, response should be a 32-bit value
548     *data_len = sizeof(resp);
549 
550     // Pack the actual response
551     std::memcpy(response, &resp, *data_len);
552 
553     return IPMI_CC_OK;
554 }
555 
556 ipmi_ret_t ipmi_storage_set_sel_time(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
557                                      ipmi_request_t request,
558                                      ipmi_response_t response,
559                                      ipmi_data_len_t data_len,
560                                      ipmi_context_t context)
561 {
562     if (*data_len != sizeof(uint32_t))
563     {
564         *data_len = 0;
565         return IPMI_CC_REQ_DATA_LEN_INVALID;
566     }
567     using namespace std::chrono;
568     ipmi_ret_t rc = IPMI_CC_OK;
569     uint32_t secs = *static_cast<uint32_t*>(request);
570     *data_len = 0;
571 
572     secs = le32toh(secs);
573     microseconds usec{seconds(secs)};
574 
575     try
576     {
577         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
578         auto service = ipmi::getService(bus, TIME_INTERFACE, HOST_TIME_PATH);
579         sdbusplus::message::variant<uint64_t> value{usec.count()};
580 
581         // Set host time
582         auto method = bus.new_method_call(service.c_str(), HOST_TIME_PATH,
583                                           DBUS_PROPERTIES, "Set");
584 
585         method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
586         auto reply = bus.call(method);
587         if (reply.is_method_error())
588         {
589             log<level::ERR>("Error setting time",
590                             entry("SERVICE=%s", service.c_str()),
591                             entry("PATH=%s", HOST_TIME_PATH));
592             rc = IPMI_CC_UNSPECIFIED_ERROR;
593         }
594     }
595     catch (InternalFailure& e)
596     {
597         log<level::ERR>(e.what());
598         rc = IPMI_CC_UNSPECIFIED_ERROR;
599     }
600     catch (const std::runtime_error& e)
601     {
602         log<level::ERR>(e.what());
603         rc = IPMI_CC_UNSPECIFIED_ERROR;
604     }
605 
606     return rc;
607 }
608 
609 ipmi_ret_t ipmi_storage_reserve_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
610                                     ipmi_request_t request,
611                                     ipmi_response_t response,
612                                     ipmi_data_len_t data_len,
613                                     ipmi_context_t context)
614 {
615     if (*data_len != 0)
616     {
617         *data_len = 0;
618         return IPMI_CC_REQ_DATA_LEN_INVALID;
619     }
620 
621     ipmi_ret_t rc = IPMI_CC_OK;
622     unsigned short selResID = reserveSel();
623 
624     *data_len = sizeof(selResID);
625 
626     // Pack the actual response
627     std::memcpy(response, &selResID, *data_len);
628 
629     return rc;
630 }
631 
632 ipmi_ret_t ipmi_storage_add_sel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
633                                 ipmi_request_t request,
634                                 ipmi_response_t response,
635                                 ipmi_data_len_t data_len,
636                                 ipmi_context_t context)
637 {
638     if (*data_len != sizeof(ipmi_add_sel_request_t))
639     {
640         *data_len = 0;
641         return IPMI_CC_REQ_DATA_LEN_INVALID;
642     }
643 
644     ipmi_ret_t rc = IPMI_CC_OK;
645     ipmi_add_sel_request_t* p = (ipmi_add_sel_request_t*)request;
646     uint16_t recordid;
647 
648     // Per the IPMI spec, need to cancel the reservation when a SEL entry is
649     // added
650     cancelSELReservation();
651 
652     recordid = ((uint16_t)p->eventdata[1] << 8) | p->eventdata[2];
653 
654     *data_len = sizeof(recordid);
655 
656     // Pack the actual response
657     std::memcpy(response, &p->eventdata[1], 2);
658 
659     // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
660     // a maintenance procedure associated with eSEL record.
661     static constexpr auto procedureType = 0xDE;
662     if (p->recordtype == procedureType)
663     {
664         // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
665         // procedure number.
666         createProcedureLogEntry(p->sensortype);
667     }
668 
669     return rc;
670 }
671 
672 // Read FRU info area
673 ipmi_ret_t ipmi_storage_get_fru_inv_area_info(
674     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
675     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
676 {
677     ipmi_ret_t rc = IPMI_CC_OK;
678     const FruInvenAreaInfoRequest* reqptr =
679         reinterpret_cast<const FruInvenAreaInfoRequest*>(request);
680 
681     auto iter = frus.find(reqptr->fruID);
682     if (iter == frus.end())
683     {
684         *data_len = 0;
685         return IPMI_CC_SENSOR_INVALID;
686     }
687 
688     try
689     {
690         const auto& fruArea = getFruAreaData(reqptr->fruID);
691         auto size = static_cast<uint16_t>(fruArea.size());
692         FruInvenAreaInfoResponse resp;
693         resp.sizems = size >> 8;
694         resp.sizels = size;
695         resp.access = static_cast<uint8_t>(AccessMode::bytes);
696 
697         *data_len = sizeof(resp);
698 
699         // Pack the actual response
700         std::memcpy(response, &resp, *data_len);
701     }
702     catch (const InternalFailure& e)
703     {
704         rc = IPMI_CC_UNSPECIFIED_ERROR;
705         *data_len = 0;
706         log<level::ERR>(e.what());
707     }
708     return rc;
709 }
710 
711 // Read FRU data
712 ipmi_ret_t ipmi_storage_read_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
713                                       ipmi_request_t request,
714                                       ipmi_response_t response,
715                                       ipmi_data_len_t data_len,
716                                       ipmi_context_t context)
717 {
718     ipmi_ret_t rc = IPMI_CC_OK;
719     const ReadFruDataRequest* reqptr =
720         reinterpret_cast<const ReadFruDataRequest*>(request);
721     auto resptr = reinterpret_cast<ReadFruDataResponse*>(response);
722 
723     auto iter = frus.find(reqptr->fruID);
724     if (iter == frus.end())
725     {
726         *data_len = 0;
727         return IPMI_CC_SENSOR_INVALID;
728     }
729 
730     auto offset =
731         static_cast<uint16_t>(reqptr->offsetMS << 8 | reqptr->offsetLS);
732     try
733     {
734         const auto& fruArea = getFruAreaData(reqptr->fruID);
735         auto size = fruArea.size();
736 
737         if (offset >= size)
738         {
739             return IPMI_CC_PARM_OUT_OF_RANGE;
740         }
741 
742         // Write the count of response data.
743         if ((offset + reqptr->count) <= size)
744         {
745             resptr->count = reqptr->count;
746         }
747         else
748         {
749             resptr->count = size - offset;
750         }
751 
752         std::copy((fruArea.begin() + offset),
753                   (fruArea.begin() + offset + resptr->count), resptr->data);
754 
755         *data_len = resptr->count + 1; // additional one byte for count
756     }
757     catch (const InternalFailure& e)
758     {
759         rc = IPMI_CC_UNSPECIFIED_ERROR;
760         *data_len = 0;
761         log<level::ERR>(e.what());
762     }
763     return rc;
764 }
765 
766 ipmi_ret_t ipmi_get_repository_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
767                                     ipmi_request_t request,
768                                     ipmi_response_t response,
769                                     ipmi_data_len_t data_len,
770                                     ipmi_context_t context)
771 {
772     constexpr auto sdrVersion = 0x51;
773     auto responseData = reinterpret_cast<GetRepositoryInfoResponse*>(response);
774 
775     std::memset(responseData, 0, sizeof(GetRepositoryInfoResponse));
776 
777     responseData->sdrVersion = sdrVersion;
778 
779     uint16_t records = frus.size() + sensors.size();
780     responseData->recordCountMs = records >> 8;
781     responseData->recordCountLs = records;
782 
783     responseData->freeSpace[0] = 0xFF;
784     responseData->freeSpace[1] = 0xFF;
785 
786     *data_len = sizeof(GetRepositoryInfoResponse);
787 
788     return IPMI_CC_OK;
789 }
790 
791 void register_netfn_storage_functions()
792 {
793     // <Wildcard Command>
794     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WILDCARD, NULL,
795                            ipmi_storage_wildcard, PRIVILEGE_USER);
796 
797     // <Get SEL Info>
798     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_INFO, NULL,
799                            getSELInfo, PRIVILEGE_USER);
800 
801     // <Get SEL Time>
802     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_TIME, NULL,
803                            ipmi_storage_get_sel_time, PRIVILEGE_USER);
804 
805     // <Set SEL Time>
806     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_SET_SEL_TIME, NULL,
807                            ipmi_storage_set_sel_time, PRIVILEGE_OPERATOR);
808 
809     // <Reserve SEL>
810     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SEL, NULL,
811                            ipmi_storage_reserve_sel, PRIVILEGE_USER);
812 
813     // <Get SEL Entry>
814     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
815                            getSELEntry, PRIVILEGE_USER);
816 
817     // <Delete SEL Entry>
818     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_DELETE_SEL, NULL,
819                            deleteSELEntry, PRIVILEGE_OPERATOR);
820 
821     // <Add SEL Entry>
822     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_ADD_SEL, NULL,
823                            ipmi_storage_add_sel, PRIVILEGE_OPERATOR);
824     // <Clear SEL>
825     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_CLEAR_SEL, NULL, clearSEL,
826                            PRIVILEGE_OPERATOR);
827     // <Get FRU Inventory Area Info>
828     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_FRU_INV_AREA_INFO, NULL,
829                            ipmi_storage_get_fru_inv_area_info,
830                            PRIVILEGE_OPERATOR);
831 
832     // <Add READ FRU Data
833     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_READ_FRU_DATA, NULL,
834                            ipmi_storage_read_fru_data, PRIVILEGE_OPERATOR);
835 
836     // <Get Repository Info>
837     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_REPOSITORY_INFO,
838                            nullptr, ipmi_get_repository_info, PRIVILEGE_USER);
839 
840     // <Reserve SDR Repository>
841     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_RESERVE_SDR, nullptr,
842                            ipmi_sen_reserve_sdr, PRIVILEGE_USER);
843 
844     // <Get SDR>
845     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
846                            ipmi_sen_get_sdr, PRIVILEGE_USER);
847 
848     ipmi::fru::registerCallbackHandler();
849     return;
850 }
851