blob: 528cffd4ba65f492ae637842cb284b994152cd96 [file] [log] [blame]
Patrick Venture46470a32018-09-07 19:26:25 -07001#include "config.h"
2
3#include "selutility.hpp"
4
Lei YU5015e952020-12-03 14:01:31 +08005#include <charconv>
Tom Joseph6b7a1432017-05-19 10:43:36 +05306#include <chrono>
Vernon Mauerybdda8002019-02-26 10:18:51 -08007#include <filesystem>
Vernon Mauerye08fbff2019-04-03 09:19:34 -07008#include <ipmid/api.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -07009#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -070010#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070011#include <phosphor-logging/elog-errors.hpp>
Tom Joseph6b7a1432017-05-19 10:43:36 +053012#include <vector>
Patrick Venture3a5071a2018-09-12 13:27:42 -070013#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture46470a32018-09-07 19:26:25 -070014
Tom Joseph6b7a1432017-05-19 10:43:36 +053015extern const ipmi::sensor::InvObjectIDMap invSensors;
16using namespace phosphor::logging;
17using InternalFailure =
Patrick Venture0b02be92018-08-31 11:55:55 -070018 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Tom Joseph6b7a1432017-05-19 10:43:36 +053019
Lei YU5015e952020-12-03 14:01:31 +080020namespace
21{
22
23constexpr auto systemEventRecord = 0x02;
24
25} // namespace
26
Tom Joseph6b7a1432017-05-19 10:43:36 +053027namespace ipmi
28{
29
30namespace sel
31{
32
33namespace internal
34{
35
Lei YU5015e952020-12-03 14:01:31 +080036inline bool isRecordOEM(uint8_t recordType)
37{
38 return recordType != systemEventRecord;
39}
40
41/** Parse the entry with format like key=val */
42std::pair<std::string, std::string> parseEntry(const std::string& entry)
43{
44 constexpr auto equalSign = "=";
45 auto pos = entry.find(equalSign);
46 assert(pos != std::string::npos);
47 auto key = entry.substr(0, pos);
48 auto val = entry.substr(pos + 1);
49 return {key, val};
50}
51
52std::map<std::string, std::string>
53 parseAdditionalData(const AdditionalData& data)
54{
55 std::map<std::string, std::string> ret;
56
57 for (const auto& d : data)
58 {
59 ret.insert(parseEntry(d));
60 }
61 return ret;
62}
63
64uint8_t convert(const std::string_view& str, int base = 10)
65{
66 int ret;
67 std::from_chars(str.data(), str.data() + str.size(), ret, base);
68 return static_cast<uint8_t>(ret);
69}
70
71// Convert the string to a vector of uint8_t, where the str is formatted as hex
72std::vector<uint8_t> convertVec(const std::string_view& str)
73{
74 std::vector<uint8_t> ret;
75 auto len = str.size() / 2;
76 ret.reserve(len);
77 for (size_t i = 0; i < len; ++i)
78 {
79 ret.emplace_back(convert(str.substr(i * 2, 2), 16));
80 }
81 return ret;
82}
83
Patrick Venture0b02be92018-08-31 11:55:55 -070084GetSELEntryResponse
85 prepareSELEntry(const std::string& objPath,
86 ipmi::sensor::InvObjectIDMap::const_iterator iter)
Tom Joseph6b7a1432017-05-19 10:43:36 +053087{
Patrick Venture0b02be92018-08-31 11:55:55 -070088 GetSELEntryResponse record{};
Tom Joseph6b7a1432017-05-19 10:43:36 +053089
90 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
91 auto service = ipmi::getService(bus, logEntryIntf, objPath);
92
93 // Read all the log entry properties.
Patrick Venture0b02be92018-08-31 11:55:55 -070094 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
95 propIntf, "GetAll");
Tom Joseph6b7a1432017-05-19 10:43:36 +053096 methodCall.append(logEntryIntf);
97
98 auto reply = bus.call(methodCall);
99 if (reply.is_method_error())
100 {
101 log<level::ERR>("Error in reading logging property entries");
102 elog<InternalFailure>();
103 }
104
Tom Joseph306878b2017-07-10 19:30:54 +0530105 std::map<PropertyName, PropertyType> entryData;
Tom Joseph6b7a1432017-05-19 10:43:36 +0530106 reply.read(entryData);
107
108 // Read Id from the log entry.
109 static constexpr auto propId = "Id";
110 auto iterId = entryData.find(propId);
111 if (iterId == entryData.end())
112 {
113 log<level::ERR>("Error in reading Id of logging entry");
114 elog<InternalFailure>();
115 }
116
Tom Joseph6b7a1432017-05-19 10:43:36 +0530117 // Read Timestamp from the log entry.
118 static constexpr auto propTimeStamp = "Timestamp";
119 auto iterTimeStamp = entryData.find(propTimeStamp);
120 if (iterTimeStamp == entryData.end())
121 {
122 log<level::ERR>("Error in reading Timestamp of logging entry");
123 elog<InternalFailure>();
124 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530125 std::chrono::milliseconds chronoTimeStamp(
Vernon Maueryf442e112019-04-09 11:44:36 -0700126 std::get<uint64_t>(iterTimeStamp->second));
Tom Joseph6b7a1432017-05-19 10:43:36 +0530127
Lei YUaf378fa2020-12-02 16:28:57 +0800128 if (iter == invSensors.end())
Tom Joseph6b7a1432017-05-19 10:43:36 +0530129 {
Lei YUaf378fa2020-12-02 16:28:57 +0800130 // It is expected to be a custom SEL entry
Lei YU5015e952020-12-03 14:01:31 +0800131 record.event.oemCD.recordID =
132 static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
133 static constexpr auto propAdditionalData = "AdditionalData";
134 // static constexpr auto strEventDir = "EVENT_DIR";
135 // static constexpr auto strGenerateId = "GENERATOR_ID";
136 static constexpr auto strRecordType = "RECORD_TYPE";
137 static constexpr auto strSensorData = "SENSOR_DATA";
138 // static constexpr auto strSensorPath = "SENSOR_PATH";
139 iterId = entryData.find(propAdditionalData);
140 if (iterId == entryData.end())
141 {
142 log<level::ERR>("Error finding AdditionalData");
143 elog<InternalFailure>();
144 }
145 const auto& addData = std::get<AdditionalData>(iterId->second);
146 auto m = parseAdditionalData(addData);
147 auto recordType = convert(m[strRecordType]);
148 auto isOEM = isRecordOEM(recordType);
149 if (isOEM)
150 {
151 if (recordType >= 0xC0 && recordType < 0xE0)
152 {
153 record.event.oemCD.timeStamp = static_cast<uint32_t>(
154 std::chrono::duration_cast<std::chrono::seconds>(
155 chronoTimeStamp)
156 .count());
157 record.event.oemCD.recordType = recordType;
158 // The ManufactureID and OEM Defined are packed in the sensor
159 // data
160 auto sensorData = convertVec(m[strSensorData]);
161 // Fill the 9 bytes of Manufacture ID and oemDefined
162 memcpy(&record.event.oemCD.manufacturerID, sensorData.data(),
163 std::min(sensorData.size(), static_cast<size_t>(9)));
164 }
165 else if (recordType >= 0xE0)
166 {
167 // TODO
168 }
169 }
170 else
171 {
172 // TODO
173 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530174 }
175 else
176 {
Lei YUaf378fa2020-12-02 16:28:57 +0800177
178 record.event.eventRecord.recordID =
179 static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
180 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
181 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
182 .count());
183
Lei YUaf378fa2020-12-02 16:28:57 +0800184 static constexpr auto generatorID = 0x2000;
185 static constexpr auto eventMsgRevision = 0x04;
186
187 record.event.eventRecord.recordType = systemEventRecord;
188 record.event.eventRecord.generatorID = generatorID;
189 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
190
191 record.event.eventRecord.sensorType = iter->second.sensorType;
192 record.event.eventRecord.sensorNum = iter->second.sensorID;
193 record.event.eventRecord.eventData1 = iter->second.eventOffset;
194
195 // Read Resolved from the log entry.
196 static constexpr auto propResolved = "Resolved";
197 auto iterResolved = entryData.find(propResolved);
198 if (iterResolved == entryData.end())
199 {
200 log<level::ERR>("Error in reading Resolved field of logging entry");
201 elog<InternalFailure>();
202 }
203
204 static constexpr auto deassertEvent = 0x80;
205
206 // Evaluate if the event is assertion or deassertion event
207 if (std::get<bool>(iterResolved->second))
208 {
209 record.event.eventRecord.eventType =
210 deassertEvent | iter->second.eventReadingType;
211 }
212 else
213 {
214 record.event.eventRecord.eventType = iter->second.eventReadingType;
215 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530216 }
217
218 return record;
219}
220
221} // namespace internal
222
Tom Joseph6edc8a02017-06-30 18:52:56 +0530223GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
224{
225 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
226
Vernon Mauerya7f81cc2019-11-06 12:51:43 -0800227 static constexpr auto assocIntf =
228 "xyz.openbmc_project.Association.Definitions";
229 static constexpr auto assocProp = "Associations";
Tom Joseph6edc8a02017-06-30 18:52:56 +0530230
231 auto service = ipmi::getService(bus, assocIntf, objPath);
232
233 // Read the Associations interface.
Patrick Venture0b02be92018-08-31 11:55:55 -0700234 auto methodCall =
235 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
Tom Joseph6edc8a02017-06-30 18:52:56 +0530236 methodCall.append(assocIntf);
237 methodCall.append(assocProp);
238
239 auto reply = bus.call(methodCall);
240 if (reply.is_method_error())
241 {
242 log<level::ERR>("Error in reading Associations interface");
243 elog<InternalFailure>();
244 }
245
Patrick Venture0b02be92018-08-31 11:55:55 -0700246 using AssociationList =
247 std::vector<std::tuple<std::string, std::string, std::string>>;
Tom Joseph6edc8a02017-06-30 18:52:56 +0530248
Vernon Mauery16b86932019-05-01 08:36:11 -0700249 std::variant<AssociationList> list;
Tom Joseph6edc8a02017-06-30 18:52:56 +0530250 reply.read(list);
251
Vernon Maueryf442e112019-04-09 11:44:36 -0700252 auto& assocs = std::get<AssociationList>(list);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530253
254 /*
255 * Check if the log entry has any callout associations, if there is a
256 * callout association try to match the inventory path to the corresponding
257 * IPMI sensor.
258 */
259 for (const auto& item : assocs)
260 {
261 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
262 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700263 auto iter = invSensors.find(std::get<2>(item));
264 if (iter == invSensors.end())
265 {
266 iter = invSensors.find(BOARD_SENSOR);
267 if (iter == invSensors.end())
268 {
269 log<level::ERR>("Motherboard sensor not found");
270 elog<InternalFailure>();
271 }
272 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530273
Patrick Venture0b02be92018-08-31 11:55:55 -0700274 return internal::prepareSELEntry(objPath, iter);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530275 }
276 }
277
278 // If there are no callout associations link the log entry to system event
279 // sensor
280 auto iter = invSensors.find(SYSTEM_SENSOR);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530281 return internal::prepareSELEntry(objPath, iter);
282}
283
Tom Joseph399fd922017-06-30 18:40:30 +0530284std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
285{
286 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
287
288 auto service = ipmi::getService(bus, logEntryIntf, objPath);
289
290 using namespace std::string_literals;
291 static const auto propTimeStamp = "Timestamp"s;
292
Patrick Venture0b02be92018-08-31 11:55:55 -0700293 auto methodCall =
294 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get");
Tom Joseph399fd922017-06-30 18:40:30 +0530295 methodCall.append(logEntryIntf);
296 methodCall.append(propTimeStamp);
297
298 auto reply = bus.call(methodCall);
299 if (reply.is_method_error())
300 {
301 log<level::ERR>("Error in reading Timestamp from Entry interface");
302 elog<InternalFailure>();
303 }
304
Vernon Mauery16b86932019-05-01 08:36:11 -0700305 std::variant<uint64_t> timeStamp;
Tom Joseph399fd922017-06-30 18:40:30 +0530306 reply.read(timeStamp);
307
Vernon Maueryf442e112019-04-09 11:44:36 -0700308 std::chrono::milliseconds chronoTimeStamp(std::get<uint64_t>(timeStamp));
Tom Joseph399fd922017-06-30 18:40:30 +0530309
310 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
311}
312
Tom Joseph232f5292017-07-07 20:14:02 +0530313void readLoggingObjectPaths(ObjectPaths& paths)
314{
315 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
316 auto depth = 0;
317 paths.clear();
318
Patrick Venture0b02be92018-08-31 11:55:55 -0700319 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
320 mapperIntf, "GetSubTreePaths");
Tom Joseph232f5292017-07-07 20:14:02 +0530321 mapperCall.append(logBasePath);
322 mapperCall.append(depth);
323 mapperCall.append(ObjectPaths({logEntryIntf}));
324
325 auto reply = bus.call(mapperCall);
326 if (reply.is_method_error())
327 {
328 log<level::INFO>("Error in reading logging entry object paths");
329 }
330 else
331 {
332 reply.read(paths);
333
Patrick Venture0b02be92018-08-31 11:55:55 -0700334 std::sort(paths.begin(), paths.end(),
335 [](const std::string& a, const std::string& b) {
336 namespace fs = std::filesystem;
337 fs::path pathA(a);
338 fs::path pathB(b);
339 auto idA = std::stoul(pathA.filename().string());
340 auto idB = std::stoul(pathB.filename().string());
Tom Joseph232f5292017-07-07 20:14:02 +0530341
Patrick Venture0b02be92018-08-31 11:55:55 -0700342 return idA < idB;
343 });
Tom Joseph232f5292017-07-07 20:14:02 +0530344 }
345}
346
Tom Joseph6b7a1432017-05-19 10:43:36 +0530347} // namespace sel
348
349} // namespace ipmi