1 #include "config.h"
2 
3 #include "selutility.hpp"
4 
5 #include <ipmid/api.h>
6 
7 #include <chrono>
8 #include <filesystem>
9 #include <ipmid/types.hpp>
10 #include <ipmid/utils.hpp>
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <vector>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 extern const ipmi::sensor::InvObjectIDMap invSensors;
16 using namespace phosphor::logging;
17 using InternalFailure =
18     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
19 
20 namespace ipmi
21 {
22 
23 namespace sel
24 {
25 
26 namespace internal
27 {
28 
29 GetSELEntryResponse
30     prepareSELEntry(const std::string& objPath,
31                     ipmi::sensor::InvObjectIDMap::const_iterator iter)
32 {
33     GetSELEntryResponse record{};
34 
35     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
36     auto service = ipmi::getService(bus, logEntryIntf, objPath);
37 
38     // Read all the log entry properties.
39     auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
40                                           propIntf, "GetAll");
41     methodCall.append(logEntryIntf);
42 
43     auto reply = bus.call(methodCall);
44     if (reply.is_method_error())
45     {
46         log<level::ERR>("Error in reading logging property entries");
47         elog<InternalFailure>();
48     }
49 
50     std::map<PropertyName, PropertyType> entryData;
51     reply.read(entryData);
52 
53     // Read Id from the log entry.
54     static constexpr auto propId = "Id";
55     auto iterId = entryData.find(propId);
56     if (iterId == entryData.end())
57     {
58         log<level::ERR>("Error in reading Id of logging entry");
59         elog<InternalFailure>();
60     }
61 
62     record.recordID = static_cast<uint16_t>(
63         sdbusplus::message::variant_ns::get<uint32_t>(iterId->second));
64 
65     // Read Timestamp from the log entry.
66     static constexpr auto propTimeStamp = "Timestamp";
67     auto iterTimeStamp = entryData.find(propTimeStamp);
68     if (iterTimeStamp == entryData.end())
69     {
70         log<level::ERR>("Error in reading Timestamp of logging entry");
71         elog<InternalFailure>();
72     }
73 
74     std::chrono::milliseconds chronoTimeStamp(
75         sdbusplus::message::variant_ns::get<uint64_t>(iterTimeStamp->second));
76     record.timeStamp = static_cast<uint32_t>(
77         std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
78             .count());
79 
80     static constexpr auto systemEventRecord = 0x02;
81     static constexpr auto generatorID = 0x2000;
82     static constexpr auto eventMsgRevision = 0x04;
83 
84     record.recordType = systemEventRecord;
85     record.generatorID = generatorID;
86     record.eventMsgRevision = eventMsgRevision;
87 
88     record.sensorType = iter->second.sensorType;
89     record.sensorNum = iter->second.sensorID;
90     record.eventData1 = iter->second.eventOffset;
91 
92     // Read Resolved from the log entry.
93     static constexpr auto propResolved = "Resolved";
94     auto iterResolved = entryData.find(propResolved);
95     if (iterResolved == entryData.end())
96     {
97         log<level::ERR>("Error in reading Resolved field of logging entry");
98         elog<InternalFailure>();
99     }
100 
101     static constexpr auto deassertEvent = 0x80;
102 
103     // Evaluate if the event is assertion or deassertion event
104     if (sdbusplus::message::variant_ns::get<bool>(iterResolved->second))
105     {
106         record.eventType = deassertEvent | iter->second.eventReadingType;
107     }
108     else
109     {
110         record.eventType = iter->second.eventReadingType;
111     }
112 
113     return record;
114 }
115 
116 } // namespace internal
117 
118 GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
119 {
120     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
121 
122     static constexpr auto assocIntf = "org.openbmc.Associations";
123     static constexpr auto assocProp = "associations";
124 
125     auto service = ipmi::getService(bus, assocIntf, objPath);
126 
127     // Read the Associations interface.
128     auto methodCall =
129         bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
130     methodCall.append(assocIntf);
131     methodCall.append(assocProp);
132 
133     auto reply = bus.call(methodCall);
134     if (reply.is_method_error())
135     {
136         log<level::ERR>("Error in reading Associations interface");
137         elog<InternalFailure>();
138     }
139 
140     using AssociationList =
141         std::vector<std::tuple<std::string, std::string, std::string>>;
142 
143     sdbusplus::message::variant<AssociationList> list;
144     reply.read(list);
145 
146     auto& assocs = sdbusplus::message::variant_ns::get<AssociationList>(list);
147 
148     /*
149      * Check if the log entry has any callout associations, if there is a
150      * callout association try to match the inventory path to the corresponding
151      * IPMI sensor.
152      */
153     for (const auto& item : assocs)
154     {
155         if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
156         {
157             auto iter = invSensors.find(std::get<2>(item));
158             if (iter == invSensors.end())
159             {
160                 iter = invSensors.find(BOARD_SENSOR);
161                 if (iter == invSensors.end())
162                 {
163                     log<level::ERR>("Motherboard sensor not found");
164                     elog<InternalFailure>();
165                 }
166             }
167 
168             return internal::prepareSELEntry(objPath, iter);
169         }
170     }
171 
172     // If there are no callout associations link the log entry to system event
173     // sensor
174     auto iter = invSensors.find(SYSTEM_SENSOR);
175     if (iter == invSensors.end())
176     {
177         log<level::ERR>("System event sensor not found");
178         elog<InternalFailure>();
179     }
180 
181     return internal::prepareSELEntry(objPath, iter);
182 }
183 
184 std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
185 {
186     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
187 
188     auto service = ipmi::getService(bus, logEntryIntf, objPath);
189 
190     using namespace std::string_literals;
191     static const auto propTimeStamp = "Timestamp"s;
192 
193     auto methodCall =
194         bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
195     methodCall.append(logEntryIntf);
196     methodCall.append(propTimeStamp);
197 
198     auto reply = bus.call(methodCall);
199     if (reply.is_method_error())
200     {
201         log<level::ERR>("Error in reading Timestamp from Entry interface");
202         elog<InternalFailure>();
203     }
204 
205     sdbusplus::message::variant<uint64_t> timeStamp;
206     reply.read(timeStamp);
207 
208     std::chrono::milliseconds chronoTimeStamp(
209         sdbusplus::message::variant_ns::get<uint64_t>(timeStamp));
210 
211     return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
212 }
213 
214 void readLoggingObjectPaths(ObjectPaths& paths)
215 {
216     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
217     auto depth = 0;
218     paths.clear();
219 
220     auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
221                                           mapperIntf, "GetSubTreePaths");
222     mapperCall.append(logBasePath);
223     mapperCall.append(depth);
224     mapperCall.append(ObjectPaths({logEntryIntf}));
225 
226     auto reply = bus.call(mapperCall);
227     if (reply.is_method_error())
228     {
229         log<level::INFO>("Error in reading logging entry object paths");
230     }
231     else
232     {
233         reply.read(paths);
234 
235         std::sort(paths.begin(), paths.end(),
236                   [](const std::string& a, const std::string& b) {
237                       namespace fs = std::filesystem;
238                       fs::path pathA(a);
239                       fs::path pathB(b);
240                       auto idA = std::stoul(pathA.filename().string());
241                       auto idB = std::stoul(pathB.filename().string());
242 
243                       return idA < idB;
244                   });
245     }
246 }
247 
248 } // namespace sel
249 
250 } // namespace ipmi
251