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