1 #include "config.h"
2 
3 #include "storagehandler.hpp"
4 
5 #include "fruread.hpp"
6 #include "read_fru_data.hpp"
7 #include "selutility.hpp"
8 #include "sensorhandler.hpp"
9 #include "storageaddsel.hpp"
10 
11 #include <arpa/inet.h>
12 #include <systemd/sd-bus.h>
13 
14 #include <ipmid/api.hpp>
15 #include <ipmid/entity_map_json.hpp>
16 #include <ipmid/utils.hpp>
17 #include <phosphor-logging/elog-errors.hpp>
18 #include <phosphor-logging/elog.hpp>
19 #include <phosphor-logging/lg2.hpp>
20 #include <sdbusplus/server.hpp>
21 #include <xyz/openbmc_project/Common/error.hpp>
22 #include <xyz/openbmc_project/Logging/SEL/error.hpp>
23 
24 #include <algorithm>
25 #include <chrono>
26 #include <cstdio>
27 #include <cstring>
28 #include <filesystem>
29 #include <optional>
30 #include <string>
31 #include <variant>
32 
33 void register_netfn_storage_functions() __attribute__((constructor));
34 
35 unsigned int g_sel_time = 0xFFFFFFFF;
36 namespace ipmi
37 {
38 namespace sensor
39 {
40 extern const IdInfoMap sensors;
41 } // namespace sensor
42 } // namespace ipmi
43 extern const ipmi::sensor::InvObjectIDMap invSensors;
44 extern const FruMap frus;
45 constexpr uint8_t eventDataSize = 3;
46 namespace
47 {
48 constexpr auto SystemdTimeService = "org.freedesktop.timedate1";
49 constexpr auto SystemdTimePath = "/org/freedesktop/timedate1";
50 constexpr auto SystemdTimeInterface = "org.freedesktop.timedate1";
51 
52 constexpr auto TIME_INTERFACE = "xyz.openbmc_project.Time.EpochTime";
53 constexpr auto BMC_TIME_PATH = "/xyz/openbmc_project/time/bmc";
54 constexpr auto DBUS_PROPERTIES = "org.freedesktop.DBus.Properties";
55 constexpr auto PROPERTY_ELAPSED = "Elapsed";
56 } // namespace
57 
58 using InternalFailure =
59     sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
60 using namespace phosphor::logging;
61 using namespace ipmi::fru;
62 using namespace xyz::openbmc_project::logging::sel;
63 using SELCreated =
64     sdbusplus::error::xyz::openbmc_project::logging::sel::Created;
65 
66 using SELRecordID = uint16_t;
67 using SELEntry = ipmi::sel::SELEventRecordFormat;
68 using SELCacheMap = std::map<SELRecordID, SELEntry>;
69 
70 SELCacheMap selCacheMap __attribute__((init_priority(101)));
71 bool selCacheMapInitialized;
72 std::unique_ptr<sdbusplus::bus::match_t> selAddedMatch
73     __attribute__((init_priority(101)));
74 std::unique_ptr<sdbusplus::bus::match_t> selRemovedMatch
75     __attribute__((init_priority(101)));
76 std::unique_ptr<sdbusplus::bus::match_t> selUpdatedMatch
77     __attribute__((init_priority(101)));
78 
getLoggingId(const std::string & p)79 static inline uint16_t getLoggingId(const std::string& p)
80 {
81     namespace fs = std::filesystem;
82     fs::path entryPath(p);
83     return std::stoul(entryPath.filename().string());
84 }
85 
getLoggingObjPath(uint16_t id)86 static inline std::string getLoggingObjPath(uint16_t id)
87 {
88     return std::string(ipmi::sel::logBasePath) + "/" + std::to_string(id);
89 }
90 
91 std::optional<std::pair<uint16_t, SELEntry>>
parseLoggingEntry(const std::string & p)92     parseLoggingEntry(const std::string& p)
93 {
94     try
95     {
96         auto id = getLoggingId(p);
97         ipmi::sel::GetSELEntryResponse record{};
98         record = ipmi::sel::convertLogEntrytoSEL(p);
99         return std::pair<uint16_t, SELEntry>({id, std::move(record.event)});
100     }
101     catch (const std::exception& e)
102     {
103         fprintf(stderr, "Failed to convert %s to SEL: %s\n", p.c_str(),
104                 e.what());
105     }
106     return std::nullopt;
107 }
108 
selAddedCallback(sdbusplus::message_t & m)109 static void selAddedCallback(sdbusplus::message_t& m)
110 {
111     sdbusplus::message::object_path objPath;
112     try
113     {
114         m.read(objPath);
115     }
116     catch (const sdbusplus::exception_t& e)
117     {
118         lg2::error("Failed to read object path");
119         return;
120     }
121     std::string p = objPath;
122     auto entry = parseLoggingEntry(p);
123     if (entry)
124     {
125         selCacheMap.insert(std::move(*entry));
126     }
127 }
128 
selRemovedCallback(sdbusplus::message_t & m)129 static void selRemovedCallback(sdbusplus::message_t& m)
130 {
131     sdbusplus::message::object_path objPath;
132     try
133     {
134         m.read(objPath);
135     }
136     catch (const sdbusplus::exception_t& e)
137     {
138         lg2::error("Failed to read object path");
139     }
140     try
141     {
142         std::string p = objPath;
143         selCacheMap.erase(getLoggingId(p));
144     }
145     catch (const std::invalid_argument& e)
146     {
147         lg2::error("Invalid logging entry ID");
148     }
149 }
150 
selUpdatedCallback(sdbusplus::message_t & m)151 static void selUpdatedCallback(sdbusplus::message_t& m)
152 {
153     std::string p = m.get_path();
154     auto entry = parseLoggingEntry(p);
155     if (entry)
156     {
157         selCacheMap.insert_or_assign(entry->first, std::move(entry->second));
158     }
159 }
160 
registerSelCallbackHandler()161 void registerSelCallbackHandler()
162 {
163     using namespace sdbusplus::bus::match::rules;
164     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
165     if (!selAddedMatch)
166     {
167         selAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
168             bus, interfacesAdded(ipmi::sel::logWatchPath),
169             std::bind(selAddedCallback, std::placeholders::_1));
170     }
171     if (!selRemovedMatch)
172     {
173         selRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
174             bus, interfacesRemoved(ipmi::sel::logWatchPath),
175             std::bind(selRemovedCallback, std::placeholders::_1));
176     }
177     if (!selUpdatedMatch)
178     {
179         selUpdatedMatch = std::make_unique<sdbusplus::bus::match_t>(
180             bus,
181             type::signal() + member("PropertiesChanged"s) +
182                 interface("org.freedesktop.DBus.Properties"s) +
183                 argN(0, ipmi::sel::logEntryIntf),
184             std::bind(selUpdatedCallback, std::placeholders::_1));
185     }
186 }
187 
initSELCache()188 void initSELCache()
189 {
190     registerSelCallbackHandler();
191     ipmi::sel::ObjectPaths paths;
192     try
193     {
194         ipmi::sel::readLoggingObjectPaths(paths);
195     }
196     catch (const sdbusplus::exception_t& e)
197     {
198         lg2::error("Failed to get logging object paths");
199         return;
200     }
201     for (const auto& p : paths)
202     {
203         auto entry = parseLoggingEntry(p);
204         if (entry)
205         {
206             selCacheMap.insert(std::move(*entry));
207         }
208     }
209     selCacheMapInitialized = true;
210 }
211 
212 /**
213  * @enum Device access mode
214  */
215 enum class AccessMode
216 {
217     bytes, ///< Device is accessed by bytes
218     words  ///< Device is accessed by words
219 };
220 
221 /** @brief implements the get SEL Info command
222  *  @returns IPMI completion code plus response data
223  *   - selVersion - SEL revision
224  *   - entries    - Number of log entries in SEL.
225  *   - freeSpace  - Free Space in bytes.
226  *   - addTimeStamp - Most recent addition timestamp
227  *   - eraseTimeStamp - Most recent erase timestamp
228  *   - operationSupport - Reserve & Delete SEL operations supported
229  */
230 
231 ipmi::RspType<uint8_t,  // SEL revision.
232               uint16_t, // number of log entries in SEL.
233               uint16_t, // free Space in bytes.
234               uint32_t, // most recent addition timestamp
235               uint32_t, // most recent erase timestamp.
236 
237               bool,     // SEL allocation info supported
238               bool,     // reserve SEL supported
239               bool,     // partial Add SEL Entry supported
240               bool,     // delete SEL supported
241               uint3_t,  // reserved
242               bool      // overflow flag
243               >
ipmiStorageGetSelInfo()244     ipmiStorageGetSelInfo()
245 {
246     uint16_t entries = 0;
247     // Most recent addition timestamp.
248     uint32_t addTimeStamp = ipmi::sel::invalidTimeStamp;
249 
250     if (!selCacheMapInitialized)
251     {
252         // In case the initSELCache() fails, try it again
253         initSELCache();
254     }
255     if (!selCacheMap.empty())
256     {
257         entries = static_cast<uint16_t>(selCacheMap.size());
258 
259         try
260         {
261             auto objPath = getLoggingObjPath(selCacheMap.rbegin()->first);
262             addTimeStamp = static_cast<uint32_t>(
263                 (ipmi::sel::getEntryTimeStamp(objPath).count()));
264         }
265         catch (const InternalFailure& e)
266         {}
267         catch (const std::runtime_error& e)
268         {
269             lg2::error("runtime error: {ERROR}", "ERROR", e);
270         }
271     }
272 
273     constexpr uint8_t selVersion = ipmi::sel::selVersion;
274     constexpr uint16_t freeSpace = 0xFFFF;
275     constexpr uint32_t eraseTimeStamp = ipmi::sel::invalidTimeStamp;
276     constexpr uint3_t reserved{0};
277 
278     return ipmi::responseSuccess(
279         selVersion, entries, freeSpace, addTimeStamp, eraseTimeStamp,
280         ipmi::sel::operationSupport::getSelAllocationInfo,
281         ipmi::sel::operationSupport::reserveSel,
282         ipmi::sel::operationSupport::partialAddSelEntry,
283         ipmi::sel::operationSupport::deleteSel, reserved,
284         ipmi::sel::operationSupport::overflow);
285 }
286 
getSELEntry(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)287 ipmi_ret_t getSELEntry(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request,
288                        ipmi_response_t response, ipmi_data_len_t data_len,
289                        ipmi_context_t)
290 {
291     if (*data_len != sizeof(ipmi::sel::GetSELEntryRequest))
292     {
293         *data_len = 0;
294         return IPMI_CC_REQ_DATA_LEN_INVALID;
295     }
296 
297     auto requestData =
298         reinterpret_cast<const ipmi::sel::GetSELEntryRequest*>(request);
299 
300     if (requestData->reservationID != 0)
301     {
302         if (!checkSELReservation(requestData->reservationID))
303         {
304             *data_len = 0;
305             return IPMI_CC_INVALID_RESERVATION_ID;
306         }
307     }
308 
309     if (!selCacheMapInitialized)
310     {
311         // In case the initSELCache() fails, try it again
312         initSELCache();
313     }
314 
315     if (selCacheMap.empty())
316     {
317         *data_len = 0;
318         return IPMI_CC_SENSOR_INVALID;
319     }
320 
321     SELCacheMap::const_iterator iter;
322 
323     // Check for the requested SEL Entry.
324     if (requestData->selRecordID == ipmi::sel::firstEntry)
325     {
326         iter = selCacheMap.begin();
327     }
328     else if (requestData->selRecordID == ipmi::sel::lastEntry)
329     {
330         if (selCacheMap.size() > 1)
331         {
332             iter = selCacheMap.end();
333             --iter;
334         }
335         else
336         {
337             // Only one entry exists, return the first
338             iter = selCacheMap.begin();
339         }
340     }
341     else
342     {
343         iter = selCacheMap.find(requestData->selRecordID);
344         if (iter == selCacheMap.end())
345         {
346             *data_len = 0;
347             return IPMI_CC_SENSOR_INVALID;
348         }
349     }
350 
351     ipmi::sel::GetSELEntryResponse record{0, iter->second};
352     // Identify the next SEL record ID
353     ++iter;
354     if (iter == selCacheMap.end())
355     {
356         record.nextRecordID = ipmi::sel::lastEntry;
357     }
358     else
359     {
360         record.nextRecordID = iter->first;
361     }
362 
363     if (requestData->readLength == ipmi::sel::entireRecord)
364     {
365         std::memcpy(response, &record, sizeof(record));
366         *data_len = sizeof(record);
367     }
368     else
369     {
370         if (requestData->offset >= ipmi::sel::selRecordSize ||
371             requestData->readLength > ipmi::sel::selRecordSize)
372         {
373             *data_len = 0;
374             return IPMI_CC_INVALID_FIELD_REQUEST;
375         }
376 
377         auto diff = ipmi::sel::selRecordSize - requestData->offset;
378         auto readLength =
379             std::min(diff, static_cast<int>(requestData->readLength));
380 
381         std::memcpy(response, &record.nextRecordID,
382                     sizeof(record.nextRecordID));
383         std::memcpy(static_cast<uint8_t*>(response) +
384                         sizeof(record.nextRecordID),
385                     &record.event.eventRecord.recordID + requestData->offset,
386                     readLength);
387         *data_len = sizeof(record.nextRecordID) + readLength;
388     }
389 
390     return IPMI_CC_OK;
391 }
392 
393 /** @brief implements the delete SEL entry command
394  * @request
395  *   - reservationID; // reservation ID.
396  *   - selRecordID;   // SEL record ID.
397  *
398  *  @returns ipmi completion code plus response data
399  *   - Record ID of the deleted record
400  */
401 ipmi::RspType<uint16_t // deleted record ID
402               >
deleteSELEntry(uint16_t reservationID,uint16_t selRecordID)403     deleteSELEntry(uint16_t reservationID, uint16_t selRecordID)
404 {
405     namespace fs = std::filesystem;
406 
407     if (!checkSELReservation(reservationID))
408     {
409         return ipmi::responseInvalidReservationId();
410     }
411 
412     // Per the IPMI spec, need to cancel the reservation when a SEL entry is
413     // deleted
414     cancelSELReservation();
415 
416     if (!selCacheMapInitialized)
417     {
418         // In case the initSELCache() fails, try it again
419         initSELCache();
420     }
421 
422     if (selCacheMap.empty())
423     {
424         return ipmi::responseSensorInvalid();
425     }
426 
427     SELCacheMap::const_iterator iter;
428     uint16_t delRecordID = 0;
429 
430     if (selRecordID == ipmi::sel::firstEntry)
431     {
432         delRecordID = selCacheMap.begin()->first;
433     }
434     else if (selRecordID == ipmi::sel::lastEntry)
435     {
436         delRecordID = selCacheMap.rbegin()->first;
437     }
438     else
439     {
440         delRecordID = selRecordID;
441     }
442 
443     iter = selCacheMap.find(delRecordID);
444     if (iter == selCacheMap.end())
445     {
446         return ipmi::responseSensorInvalid();
447     }
448 
449     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
450     std::string service;
451 
452     auto objPath = getLoggingObjPath(iter->first);
453     try
454     {
455         service = ipmi::getService(bus, ipmi::sel::logDeleteIntf, objPath);
456     }
457     catch (const std::runtime_error& e)
458     {
459         lg2::error("runtime error: {ERROR}", "ERROR", e);
460         return ipmi::responseUnspecifiedError();
461     }
462 
463     auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
464                                           ipmi::sel::logDeleteIntf, "Delete");
465     try
466     {
467         auto reply = bus.call(methodCall);
468     }
469     catch (const std::exception& e)
470     {
471         return ipmi::responseUnspecifiedError();
472     }
473 
474     return ipmi::responseSuccess(delRecordID);
475 }
476 
477 /** @brief implements the Clear SEL command
478  * @request
479  *   - reservationID   // Reservation ID.
480  *   - clr             // char array { 'C'(0x43h), 'L'(0x4Ch), 'R'(0x52h) }
481  *   - eraseOperation; // requested operation.
482  *
483  *  @returns ipmi completion code plus response data
484  *   - erase status
485  */
486 
487 ipmi::RspType<uint8_t // erase status
488               >
clearSEL(uint16_t reservationID,const std::array<char,3> & clr,uint8_t eraseOperation)489     clearSEL(uint16_t reservationID, const std::array<char, 3>& clr,
490              uint8_t eraseOperation)
491 {
492     static constexpr std::array<char, 3> clrOk = {'C', 'L', 'R'};
493     if (clr != clrOk)
494     {
495         return ipmi::responseInvalidFieldRequest();
496     }
497 
498     if (!checkSELReservation(reservationID))
499     {
500         return ipmi::responseInvalidReservationId();
501     }
502 
503     /*
504      * Erasure status cannot be fetched from DBUS, so always return erasure
505      * status as `erase completed`.
506      */
507     if (eraseOperation == ipmi::sel::getEraseStatus)
508     {
509         return ipmi::responseSuccess(
510             static_cast<uint8_t>(ipmi::sel::eraseComplete));
511     }
512 
513     // Check that initiate erase is correct
514     if (eraseOperation != ipmi::sel::initiateErase)
515     {
516         return ipmi::responseInvalidFieldRequest();
517     }
518 
519     // Per the IPMI spec, need to cancel any reservation when the SEL is cleared
520     cancelSELReservation();
521 
522     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
523     auto service = ipmi::getService(bus, ipmi::sel::logIntf, ipmi::sel::logObj);
524     auto method =
525         bus.new_method_call(service.c_str(), ipmi::sel::logObj,
526                             ipmi::sel::logIntf, ipmi::sel::logDeleteAllMethod);
527     try
528     {
529         bus.call_noreply(method);
530     }
531     catch (const sdbusplus::exception_t& e)
532     {
533         lg2::error("Error eraseAll: {ERROR}", "ERROR", e);
534         return ipmi::responseUnspecifiedError();
535     }
536 
537     return ipmi::responseSuccess(
538         static_cast<uint8_t>(ipmi::sel::eraseComplete));
539 }
540 
541 /** @brief implements the get SEL time command
542  *  @returns IPMI completion code plus response data
543  *   -current time
544  */
545 ipmi::RspType<uint32_t> // current time
ipmiStorageGetSelTime()546     ipmiStorageGetSelTime()
547 {
548     using namespace std::chrono;
549     uint64_t bmc_time_usec = 0;
550     std::stringstream bmcTime;
551 
552     try
553     {
554         sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
555         auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH);
556         auto propValue = ipmi::getDbusProperty(
557             bus, service, BMC_TIME_PATH, TIME_INTERFACE, PROPERTY_ELAPSED);
558         bmc_time_usec = std::get<uint64_t>(propValue);
559     }
560     catch (const InternalFailure& e)
561     {
562         lg2::error("Internal Failure: {ERROR}", "ERROR", e);
563         return ipmi::responseUnspecifiedError();
564     }
565     catch (const std::exception& e)
566     {
567         lg2::error("exception message: {ERROR}", "ERROR", e);
568         return ipmi::responseUnspecifiedError();
569     }
570 
571     lg2::debug("BMC time: {BMC_TIME}", "BMC_TIME",
572                duration_cast<seconds>(microseconds(bmc_time_usec)).count());
573 
574     // Time is really long int but IPMI wants just uint32. This works okay until
575     // the number of seconds since 1970 overflows uint32 size.. Still a whole
576     // lot of time here to even think about that.
577     return ipmi::responseSuccess(
578         duration_cast<seconds>(microseconds(bmc_time_usec)).count());
579 }
580 
581 /** @brief implements the set SEL time command
582  *  @param selDeviceTime - epoch time
583  *        -local time as the number of seconds from 00:00:00, January 1, 1970
584  *  @returns IPMI completion code
585  */
ipmiStorageSetSelTime(uint32_t selDeviceTime)586 ipmi::RspType<> ipmiStorageSetSelTime(uint32_t selDeviceTime)
587 {
588     using namespace std::chrono;
589     microseconds usec{seconds(selDeviceTime)};
590 
591     try
592     {
593         sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
594         bool ntp = std::get<bool>(
595             ipmi::getDbusProperty(bus, SystemdTimeService, SystemdTimePath,
596                                   SystemdTimeInterface, "NTP"));
597         if (ntp)
598         {
599             return ipmi::responseCommandNotAvailable();
600         }
601 
602         auto service = ipmi::getService(bus, TIME_INTERFACE, BMC_TIME_PATH);
603         std::variant<uint64_t> value{(uint64_t)usec.count()};
604 
605         // Set bmc time
606         auto method = bus.new_method_call(service.c_str(), BMC_TIME_PATH,
607                                           DBUS_PROPERTIES, "Set");
608 
609         method.append(TIME_INTERFACE, PROPERTY_ELAPSED, value);
610         auto reply = bus.call(method);
611     }
612     catch (const InternalFailure& e)
613     {
614         lg2::error("Internal Failure: {ERROR}", "ERROR", e);
615         return ipmi::responseUnspecifiedError();
616     }
617     catch (const std::exception& e)
618     {
619         lg2::error("exception message: {ERROR}", "ERROR", e);
620         return ipmi::responseUnspecifiedError();
621     }
622 
623     return ipmi::responseSuccess();
624 }
625 
626 /** @brief implements the get SEL timezone command
627  *  @returns IPMI completion code plus response data
628  *   -current timezone
629  */
ipmiStorageGetSelTimeUtcOffset()630 ipmi::RspType<int16_t> ipmiStorageGetSelTimeUtcOffset()
631 {
632     time_t timep;
633     struct tm* gmTime;
634     struct tm* localTime;
635 
636     time(&timep);
637     localTime = localtime(&timep);
638     auto validLocalTime = mktime(localTime);
639     gmTime = gmtime(&timep);
640     auto validGmTime = mktime(gmTime);
641     auto timeEquation = (validLocalTime - validGmTime) / 60;
642 
643     return ipmi::responseSuccess(timeEquation);
644 }
645 
646 /** @brief implements the reserve SEL command
647  *  @returns IPMI completion code plus response data
648  *   - SEL reservation ID.
649  */
ipmiStorageReserveSel()650 ipmi::RspType<uint16_t> ipmiStorageReserveSel()
651 {
652     return ipmi::responseSuccess(reserveSel());
653 }
654 
655 /** @brief implements the Add SEL entry command
656  * @request
657  *
658  *   - recordID      ID used for SEL Record access
659  *   - recordType    Record Type
660  *   - timeStamp     Time when event was logged. LS byte first
661  *   - generatorID   software ID if event was generated from
662  *                   system software
663  *   - evmRev        event message format version
664  *   - sensorType    sensor type code for service that generated
665  *                   the event
666  *   - sensorNumber  number of sensors that generated the event
667  *   - eventDir     event dir
668  *   - eventData    event data field contents
669  *
670  *  @returns ipmi completion code plus response data
671  *   - RecordID of the Added SEL entry
672  */
673 ipmi::RspType<uint16_t // recordID of the Added SEL entry
674               >
ipmiStorageAddSEL(uint16_t recordID,uint8_t recordType,uint32_t timeStamp,uint16_t generatorID,uint8_t evmRev,uint8_t sensorType,uint8_t sensorNumber,uint8_t eventDir,std::array<uint8_t,eventDataSize> eventData)675     ipmiStorageAddSEL(uint16_t recordID, uint8_t recordType,
676                       [[maybe_unused]] uint32_t timeStamp, uint16_t generatorID,
677                       [[maybe_unused]] uint8_t evmRev, uint8_t sensorType,
678                       uint8_t sensorNumber, uint8_t eventDir,
679                       std::array<uint8_t, eventDataSize> eventData)
680 {
681     std::string objpath;
682     static constexpr auto systemRecordType = 0x02;
683     // Hostboot sends SEL with OEM record type 0xDE to indicate that there is
684     // a maintenance procedure associated with eSEL record.
685     static constexpr auto procedureType = 0xDE;
686     cancelSELReservation();
687     if (recordType == systemRecordType)
688     {
689         for (const auto& it : invSensors)
690         {
691             if (it.second.sensorID == sensorNumber)
692             {
693                 objpath = it.first;
694                 break;
695             }
696         }
697         auto selDataStr = ipmi::sel::toHexStr(eventData);
698 
699         bool assert = (eventDir & 0x80) ? false : true;
700 
701         recordID = report<SELCreated>(
702             Created::RECORD_TYPE(recordType),
703             Created::GENERATOR_ID(generatorID),
704             Created::SENSOR_DATA(selDataStr.c_str()),
705             Created::EVENT_DIR(assert), Created::SENSOR_PATH(objpath.c_str()));
706     }
707 #ifdef OPEN_POWER_SUPPORT
708     else if (recordType == procedureType)
709     {
710         // In the OEM record type 0xDE, byte 11 in the SEL record indicate the
711         // procedure number.
712         createProcedureLogEntry(sensorType);
713     }
714 #endif
715 
716     return ipmi::responseSuccess(recordID);
717 }
718 
isFruPresent(ipmi::Context::ptr & ctx,const std::string & fruPath)719 bool isFruPresent(ipmi::Context::ptr& ctx, const std::string& fruPath)
720 {
721     using namespace ipmi::fru;
722 
723     std::string service;
724     boost::system::error_code ec =
725         getService(ctx, invItemInterface, invObjPath + fruPath, service);
726     if (!ec)
727     {
728         bool result;
729         ec = ipmi::getDbusProperty(ctx, service, invObjPath + fruPath,
730                                    invItemInterface, itemPresentProp, result);
731         if (!ec)
732         {
733             return result;
734         }
735     }
736 
737     ipmi::ObjectValueTree managedObjects;
738     ec = getManagedObjects(ctx, "xyz.openbmc_project.EntityManager",
739                            "/xyz/openbmc_project/inventory", managedObjects);
740     if (!ec)
741     {
742         auto connection = managedObjects.find(fruPath);
743         if (connection != managedObjects.end())
744         {
745             return true;
746         }
747     }
748 
749     return false;
750 }
751 
752 /** @brief implements the get FRU Inventory Area Info command
753  *
754  *  @returns IPMI completion code plus response data
755  *   - FRU Inventory area size in bytes,
756  *   - access bit
757  **/
758 ipmi::RspType<uint16_t, // FRU Inventory area size in bytes,
759               uint8_t   // access size (bytes / words)
760               >
ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr ctx,uint8_t fruID)761     ipmiStorageGetFruInvAreaInfo(ipmi::Context::ptr ctx, uint8_t fruID)
762 {
763     auto iter = frus.find(fruID);
764     if (iter == frus.end())
765     {
766         return ipmi::responseSensorInvalid();
767     }
768 
769     auto path = iter->second[0].path;
770     if (!isFruPresent(ctx, path))
771     {
772         return ipmi::responseSensorInvalid();
773     }
774 
775     try
776     {
777         return ipmi::responseSuccess(
778             static_cast<uint16_t>(getFruAreaData(fruID).size()),
779             static_cast<uint8_t>(AccessMode::bytes));
780     }
781     catch (const InternalFailure& e)
782     {
783         lg2::error("Internal Failure: {ERROR}", "ERROR", e);
784         return ipmi::responseUnspecifiedError();
785     }
786 }
787 
788 /**@brief implements the Read FRU Data command
789  * @param fruDeviceId - FRU device ID. FFh = reserved
790  * @param offset      - FRU inventory offset to read
791  * @param readCount   - count to read
792  *
793  * @return IPMI completion code plus response data
794  * - returnCount - response data count.
795  * - data        -  response data
796  */
797 ipmi::RspType<uint8_t,              // count returned
798               std::vector<uint8_t>> // FRU data
ipmiStorageReadFruData(uint8_t fruDeviceId,uint16_t offset,uint8_t readCount)799     ipmiStorageReadFruData(uint8_t fruDeviceId, uint16_t offset,
800                            uint8_t readCount)
801 {
802     if (fruDeviceId == 0xFF)
803     {
804         return ipmi::responseInvalidFieldRequest();
805     }
806 
807     auto iter = frus.find(fruDeviceId);
808     if (iter == frus.end())
809     {
810         return ipmi::responseSensorInvalid();
811     }
812 
813     try
814     {
815         const auto& fruArea = getFruAreaData(fruDeviceId);
816         auto size = fruArea.size();
817 
818         if (offset >= size)
819         {
820             return ipmi::responseParmOutOfRange();
821         }
822 
823         // Write the count of response data.
824         uint8_t returnCount;
825         if ((offset + readCount) <= size)
826         {
827             returnCount = readCount;
828         }
829         else
830         {
831             returnCount = size - offset;
832         }
833 
834         std::vector<uint8_t> fruData((fruArea.begin() + offset),
835                                      (fruArea.begin() + offset + returnCount));
836 
837         return ipmi::responseSuccess(returnCount, fruData);
838     }
839     catch (const InternalFailure& e)
840     {
841         lg2::error("Internal Failure: {ERROR}", "ERROR", e);
842         return ipmi::responseUnspecifiedError();
843     }
844 }
845 
846 ipmi::RspType<uint8_t,  // SDR version
847               uint16_t, // record count LS first
848               uint16_t, // free space in bytes, LS first
849               uint32_t, // addition timestamp LS first
850               uint32_t, // deletion timestamp LS first
851               uint8_t>  // operation Support
ipmiGetRepositoryInfo()852     ipmiGetRepositoryInfo()
853 {
854     constexpr uint8_t sdrVersion = 0x51;
855     constexpr uint16_t freeSpace = 0xFFFF;
856     constexpr uint32_t additionTimestamp = 0x0;
857     constexpr uint32_t deletionTimestamp = 0x0;
858     constexpr uint8_t operationSupport = 0;
859 
860     // Get SDR count. This returns the total number of SDRs in the device.
861     const auto& entityRecords =
862         ipmi::sensor::EntityInfoMapContainer::getContainer()
863             ->getIpmiEntityRecords();
864     uint16_t records =
865         ipmi::sensor::sensors.size() + frus.size() + entityRecords.size();
866 
867     return ipmi::responseSuccess(sdrVersion, records, freeSpace,
868                                  additionTimestamp, deletionTimestamp,
869                                  operationSupport);
870 }
871 
register_netfn_storage_functions()872 void register_netfn_storage_functions()
873 {
874     selCacheMapInitialized = false;
875     initSELCache();
876     // Handlers with dbus-sdr handler implementation.
877     // Do not register the hander if it dynamic sensors stack is used.
878 
879 #ifndef FEATURE_DYNAMIC_SENSORS
880 
881 #ifndef FEATURE_DYNAMIC_STORAGES_ONLY
882     // <Get SEL Info>
883     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
884                           ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
885                           ipmiStorageGetSelInfo);
886 
887     // <Get SEL Timezone>
888     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
889                           ipmi::storage::cmdGetSelTimeUtcOffset,
890                           ipmi::Privilege::User,
891                           ipmiStorageGetSelTimeUtcOffset);
892 
893     // <Get SEL Entry>
894     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SEL_ENTRY, NULL,
895                            getSELEntry, PRIVILEGE_USER);
896 
897     // <Delete SEL Entry>
898     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
899                           ipmi::storage::cmdDeleteSelEntry,
900                           ipmi::Privilege::Operator, deleteSELEntry);
901 
902     // <Add SEL Entry>
903     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
904                           ipmi::storage::cmdAddSelEntry,
905                           ipmi::Privilege::Operator, ipmiStorageAddSEL);
906 
907     // <Clear SEL>
908     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
909                           ipmi::storage::cmdClearSel, ipmi::Privilege::Operator,
910                           clearSEL);
911 
912     // <Get FRU Inventory Area Info>
913     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
914                           ipmi::storage::cmdGetFruInventoryAreaInfo,
915                           ipmi::Privilege::User, ipmiStorageGetFruInvAreaInfo);
916 
917     // <READ FRU Data>
918     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
919                           ipmi::storage::cmdReadFruData,
920                           ipmi::Privilege::Operator, ipmiStorageReadFruData);
921 
922 #endif // FEATURE_DYNAMIC_STORAGES_ONLY
923 
924     // <Get Repository Info>
925     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
926                           ipmi::storage::cmdGetSdrRepositoryInfo,
927                           ipmi::Privilege::User, ipmiGetRepositoryInfo);
928 
929     // <Reserve SDR Repository>
930     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
931                           ipmi::storage::cmdReserveSdrRepository,
932                           ipmi::Privilege::User, ipmiSensorReserveSdr);
933 
934     // <Get SDR>
935     ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_GET_SDR, nullptr,
936                            ipmi_sen_get_sdr, PRIVILEGE_USER);
937 
938 #endif
939 
940     // Common Handers used by both implementation.
941 
942     // <Reserve SEL>
943     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
944                           ipmi::storage::cmdReserveSel, ipmi::Privilege::User,
945                           ipmiStorageReserveSel);
946 
947     // <Get SEL Time>
948     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
949                           ipmi::storage::cmdGetSelTime, ipmi::Privilege::User,
950                           ipmiStorageGetSelTime);
951 
952     // <Set SEL Time>
953     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
954                           ipmi::storage::cmdSetSelTime,
955                           ipmi::Privilege::Operator, ipmiStorageSetSelTime);
956 
957     ipmi::fru::registerCallbackHandler();
958     return;
959 }
960