blob: f868708816c0e9d2d2715a22879757de441fa08c [file] [log] [blame]
Patrick Venture46470a32018-09-07 19:26:25 -07001#include "config.h"
2
3#include "selutility.hpp"
4
Vernon Mauerye08fbff2019-04-03 09:19:34 -07005#include <ipmid/api.hpp>
Vernon Mauery33250242019-03-12 16:49:26 -07006#include <ipmid/types.hpp>
Vernon Mauery6a98fe72019-03-11 15:57:48 -07007#include <ipmid/utils.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -07008#include <phosphor-logging/elog-errors.hpp>
George Liu7acfcf02024-07-17 20:14:56 +08009#include <phosphor-logging/lg2.hpp>
Patrick Venture3a5071a2018-09-12 13:27:42 -070010#include <xyz/openbmc_project/Common/error.hpp>
Patrick Venture46470a32018-09-07 19:26:25 -070011
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050012#include <charconv>
13#include <chrono>
14#include <filesystem>
15#include <vector>
16
Tom Joseph6b7a1432017-05-19 10:43:36 +053017extern const ipmi::sensor::InvObjectIDMap invSensors;
18using namespace phosphor::logging;
19using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -070020 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Tom Joseph6b7a1432017-05-19 10:43:36 +053021
Lei YU5015e952020-12-03 14:01:31 +080022namespace
23{
24
25constexpr auto systemEventRecord = 0x02;
Lei YU07c15b72020-12-06 15:08:06 +080026constexpr auto generatorID = 0x2000;
27constexpr auto eventMsgRevision = 0x04;
28constexpr auto assertEvent = 0x00;
29constexpr auto deassertEvent = 0x80;
30constexpr auto selDataSize = 3;
31constexpr auto oemCDDataSize = 9;
32constexpr auto oemEFDataSize = 13;
Lei YU5015e952020-12-03 14:01:31 +080033
Lei YU719a41c2020-12-03 17:50:44 +080034constexpr auto propAdditionalData = "AdditionalData";
Lei YU07c15b72020-12-06 15:08:06 +080035constexpr auto propResolved = "Resolved";
36
Lei YU719a41c2020-12-03 17:50:44 +080037constexpr auto strEventDir = "EVENT_DIR";
38constexpr auto strGenerateId = "GENERATOR_ID";
39constexpr auto strRecordType = "RECORD_TYPE";
40constexpr auto strSensorData = "SENSOR_DATA";
41constexpr auto strSensorPath = "SENSOR_PATH";
42
Lei YU5015e952020-12-03 14:01:31 +080043} // namespace
44
Tom Joseph6b7a1432017-05-19 10:43:36 +053045namespace ipmi
46{
47
48namespace sel
49{
50
51namespace internal
52{
53
Lei YU5015e952020-12-03 14:01:31 +080054inline bool isRecordOEM(uint8_t recordType)
55{
56 return recordType != systemEventRecord;
57}
58
Lei YU719a41c2020-12-03 17:50:44 +080059using additionalDataMap = std::map<std::string, std::string>;
Lei YU07c15b72020-12-06 15:08:06 +080060using entryDataMap = std::map<PropertyName, PropertyType>;
Lei YU5015e952020-12-03 14:01:31 +080061/** Parse the entry with format like key=val */
62std::pair<std::string, std::string> parseEntry(const std::string& entry)
63{
64 constexpr auto equalSign = "=";
65 auto pos = entry.find(equalSign);
66 assert(pos != std::string::npos);
67 auto key = entry.substr(0, pos);
68 auto val = entry.substr(pos + 1);
69 return {key, val};
70}
71
Lei YU719a41c2020-12-03 17:50:44 +080072additionalDataMap parseAdditionalData(const AdditionalData& data)
Lei YU5015e952020-12-03 14:01:31 +080073{
74 std::map<std::string, std::string> ret;
75
76 for (const auto& d : data)
77 {
78 ret.insert(parseEntry(d));
79 }
80 return ret;
81}
82
Lei YU07c15b72020-12-06 15:08:06 +080083int convert(const std::string_view& str, int base = 10)
Lei YU5015e952020-12-03 14:01:31 +080084{
William A. Kennington III4c521022023-07-28 13:07:30 -070085 int ret = 0;
Lei YU5015e952020-12-03 14:01:31 +080086 std::from_chars(str.data(), str.data() + str.size(), ret, base);
Lei YU07c15b72020-12-06 15:08:06 +080087 return ret;
Lei YU5015e952020-12-03 14:01:31 +080088}
89
90// Convert the string to a vector of uint8_t, where the str is formatted as hex
91std::vector<uint8_t> convertVec(const std::string_view& str)
92{
93 std::vector<uint8_t> ret;
94 auto len = str.size() / 2;
95 ret.reserve(len);
96 for (size_t i = 0; i < len; ++i)
97 {
Lei YU07c15b72020-12-06 15:08:06 +080098 ret.emplace_back(
99 static_cast<uint8_t>(convert(str.substr(i * 2, 2), 16)));
Lei YU5015e952020-12-03 14:01:31 +0800100 }
101 return ret;
102}
103
Lei YU719a41c2020-12-03 17:50:44 +0800104/** Construct OEM SEL record according to IPMI spec 32.2, 32.3. */
Lei YU07c15b72020-12-06 15:08:06 +0800105void constructOEMSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Lei YU719a41c2020-12-03 17:50:44 +0800106 const additionalDataMap& m, GetSELEntryResponse& record)
107{
108 auto dataIter = m.find(strSensorData);
109 assert(dataIter != m.end());
110 auto sensorData = convertVec(dataIter->second);
111 if (recordType >= 0xC0 && recordType < 0xE0)
112 {
113 record.event.oemCD.timeStamp = static_cast<uint32_t>(
114 std::chrono::duration_cast<std::chrono::seconds>(timestamp)
115 .count());
116 record.event.oemCD.recordType = recordType;
117 // The ManufactureID and OEM Defined are packed in the sensor data
118 // Fill the 9 bytes of Manufacture ID and oemDefined
119 memcpy(&record.event.oemCD.manufacturerID, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +0800120 std::min(sensorData.size(), static_cast<size_t>(oemCDDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +0800121 }
122 else if (recordType >= 0xE0)
123 {
124 record.event.oemEF.recordType = recordType;
125 // The remaining 13 bytes are the OEM Defined data
126 memcpy(&record.event.oemEF.oemDefined, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +0800127 std::min(sensorData.size(), static_cast<size_t>(oemEFDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +0800128 }
129}
130
Lei YU07c15b72020-12-06 15:08:06 +0800131void constructSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Willy Tu11d68892022-01-20 10:37:34 -0800132 const additionalDataMap& m, const entryDataMap&,
Lei YU07c15b72020-12-06 15:08:06 +0800133 GetSELEntryResponse& record)
134{
135 if (recordType != systemEventRecord)
136 {
George Liu7acfcf02024-07-17 20:14:56 +0800137 lg2::error("Invalid recordType");
Lei YU07c15b72020-12-06 15:08:06 +0800138 elog<InternalFailure>();
139 }
140
141 // Default values when there is no matched sensor
142 record.event.eventRecord.sensorType = 0;
143 record.event.eventRecord.sensorNum = 0xFF;
144 record.event.eventRecord.eventType = 0;
145
146 auto iter = m.find(strSensorPath);
147 assert(iter != m.end());
148 const auto& sensorPath = iter->second;
149 auto sensorIter = invSensors.find(sensorPath);
150
151 if (sensorIter != invSensors.end())
152 {
153 // There is a matched sensor
154 record.event.eventRecord.sensorType = sensorIter->second.sensorType;
155 record.event.eventRecord.sensorNum = sensorIter->second.sensorID;
156
157 iter = m.find(strEventDir);
158 assert(iter != m.end());
159 auto eventDir = static_cast<uint8_t>(convert(iter->second));
160 uint8_t assert = eventDir ? assertEvent : deassertEvent;
161 record.event.eventRecord.eventType =
162 assert | sensorIter->second.eventReadingType;
163 }
164 record.event.eventRecord.recordType = recordType;
165 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
166 std::chrono::duration_cast<std::chrono::seconds>(timestamp).count());
167 iter = m.find(strGenerateId);
168 assert(iter != m.end());
169 record.event.eventRecord.generatorID =
170 static_cast<uint16_t>(convert(iter->second));
171 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
172 iter = m.find(strSensorData);
173 assert(iter != m.end());
174 auto sensorData = convertVec(iter->second);
175 // The remaining 3 bytes are the sensor data
176 memcpy(&record.event.eventRecord.eventData1, sensorData.data(),
177 std::min(sensorData.size(), static_cast<size_t>(selDataSize)));
178}
179
Patrick Venture0b02be92018-08-31 11:55:55 -0700180GetSELEntryResponse
181 prepareSELEntry(const std::string& objPath,
182 ipmi::sensor::InvObjectIDMap::const_iterator iter)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530183{
Patrick Venture0b02be92018-08-31 11:55:55 -0700184 GetSELEntryResponse record{};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530185
Patrick Williams5d82f472022-07-22 19:26:53 -0500186 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530187 auto service = ipmi::getService(bus, logEntryIntf, objPath);
188
189 // Read all the log entry properties.
Patrick Venture0b02be92018-08-31 11:55:55 -0700190 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
191 propIntf, "GetAll");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530192 methodCall.append(logEntryIntf);
193
George Liu3e3cc352023-07-26 15:59:31 +0800194 entryDataMap entryData;
195 try
Tom Joseph6b7a1432017-05-19 10:43:36 +0530196 {
George Liu3e3cc352023-07-26 15:59:31 +0800197 auto reply = bus.call(methodCall);
198 reply.read(entryData);
199 }
200 catch (const std::exception& e)
201 {
George Liu7acfcf02024-07-17 20:14:56 +0800202 lg2::error("Error in reading logging property entries: {ERROR}",
203 "ERROR", e);
Tom Joseph6b7a1432017-05-19 10:43:36 +0530204 elog<InternalFailure>();
205 }
206
Tom Joseph6b7a1432017-05-19 10:43:36 +0530207 // Read Id from the log entry.
208 static constexpr auto propId = "Id";
209 auto iterId = entryData.find(propId);
210 if (iterId == entryData.end())
211 {
George Liu7acfcf02024-07-17 20:14:56 +0800212 lg2::error("Error in reading Id of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530213 elog<InternalFailure>();
214 }
215
Tom Joseph6b7a1432017-05-19 10:43:36 +0530216 // Read Timestamp from the log entry.
217 static constexpr auto propTimeStamp = "Timestamp";
218 auto iterTimeStamp = entryData.find(propTimeStamp);
219 if (iterTimeStamp == entryData.end())
220 {
George Liu7acfcf02024-07-17 20:14:56 +0800221 lg2::error("Error in reading Timestamp of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530222 elog<InternalFailure>();
223 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530224 std::chrono::milliseconds chronoTimeStamp(
Vernon Maueryf442e112019-04-09 11:44:36 -0700225 std::get<uint64_t>(iterTimeStamp->second));
Tom Joseph6b7a1432017-05-19 10:43:36 +0530226
Lei YU690f4d52021-04-28 19:05:24 +0800227 bool isFromSELLogger = false;
228 additionalDataMap m;
229
230 // The recordID are with the same offset between different types,
231 // so we are safe to set the recordID here
232 record.event.eventRecord.recordID =
233 static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
234
235 iterId = entryData.find(propAdditionalData);
236 if (iterId != entryData.end())
237 {
238 // Check if it's a SEL from phosphor-sel-logger which shall contain
239 // the record ID, etc
240 const auto& addData = std::get<AdditionalData>(iterId->second);
241 m = parseAdditionalData(addData);
242 auto recordTypeIter = m.find(strRecordType);
243 if (recordTypeIter != m.end())
244 {
245 // It is a SEL from phosphor-sel-logger
246 isFromSELLogger = true;
247 }
248 else
249 {
250 // Not a SEL from phosphor-sel-logger, it shall have a valid
251 // invSensor
252 if (iter == invSensors.end())
253 {
George Liu7acfcf02024-07-17 20:14:56 +0800254 lg2::error("System event sensor not found");
Lei YU690f4d52021-04-28 19:05:24 +0800255 elog<InternalFailure>();
256 }
257 }
258 }
259
260 if (isFromSELLogger)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530261 {
Lei YUaf378fa2020-12-02 16:28:57 +0800262 // It is expected to be a custom SEL entry
Lei YU07c15b72020-12-06 15:08:06 +0800263 auto recordType = static_cast<uint8_t>(convert(m[strRecordType]));
Lei YU5015e952020-12-03 14:01:31 +0800264 auto isOEM = isRecordOEM(recordType);
265 if (isOEM)
266 {
Lei YU07c15b72020-12-06 15:08:06 +0800267 constructOEMSEL(recordType, chronoTimeStamp, m, record);
Lei YU5015e952020-12-03 14:01:31 +0800268 }
269 else
270 {
Lei YU07c15b72020-12-06 15:08:06 +0800271 constructSEL(recordType, chronoTimeStamp, m, entryData, record);
Lei YU5015e952020-12-03 14:01:31 +0800272 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530273 }
274 else
275 {
Lei YUaf378fa2020-12-02 16:28:57 +0800276 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
277 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
278 .count());
279
Lei YUaf378fa2020-12-02 16:28:57 +0800280 record.event.eventRecord.recordType = systemEventRecord;
281 record.event.eventRecord.generatorID = generatorID;
282 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
283
284 record.event.eventRecord.sensorType = iter->second.sensorType;
285 record.event.eventRecord.sensorNum = iter->second.sensorID;
286 record.event.eventRecord.eventData1 = iter->second.eventOffset;
287
288 // Read Resolved from the log entry.
Lei YUaf378fa2020-12-02 16:28:57 +0800289 auto iterResolved = entryData.find(propResolved);
290 if (iterResolved == entryData.end())
291 {
George Liu7acfcf02024-07-17 20:14:56 +0800292 lg2::error("Error in reading Resolved field of logging entry");
Lei YUaf378fa2020-12-02 16:28:57 +0800293 elog<InternalFailure>();
294 }
295
Lei YUaf378fa2020-12-02 16:28:57 +0800296 // Evaluate if the event is assertion or deassertion event
297 if (std::get<bool>(iterResolved->second))
298 {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500299 record.event.eventRecord.eventType = deassertEvent |
300 iter->second.eventReadingType;
Lei YUaf378fa2020-12-02 16:28:57 +0800301 }
302 else
303 {
304 record.event.eventRecord.eventType = iter->second.eventReadingType;
305 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530306 }
307
308 return record;
309}
310
311} // namespace internal
312
Tom Joseph6edc8a02017-06-30 18:52:56 +0530313GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
314{
Patrick Williams5d82f472022-07-22 19:26:53 -0500315 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6edc8a02017-06-30 18:52:56 +0530316
Vernon Mauerya7f81cc2019-11-06 12:51:43 -0800317 static constexpr auto assocIntf =
318 "xyz.openbmc_project.Association.Definitions";
319 static constexpr auto assocProp = "Associations";
Tom Joseph6edc8a02017-06-30 18:52:56 +0530320
George Liu42f64ef2024-02-05 15:03:18 +0800321 std::vector<ipmi::Association> assocs;
George Liu3e3cc352023-07-26 15:59:31 +0800322 try
323 {
George Liu42f64ef2024-02-05 15:03:18 +0800324 auto service = ipmi::getService(bus, assocIntf, objPath);
325 auto propValue = ipmi::getDbusProperty(bus, service, objPath, assocIntf,
326 assocProp);
327 assocs = std::get<std::vector<ipmi::Association>>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800328 }
329 catch (const std::exception& e)
330 {
George Liu7acfcf02024-07-17 20:14:56 +0800331 lg2::error("Error in reading Associations interface: {ERROR}", "ERROR",
332 e);
George Liu3e3cc352023-07-26 15:59:31 +0800333 elog<InternalFailure>();
334 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530335
Tom Joseph6edc8a02017-06-30 18:52:56 +0530336 /*
337 * Check if the log entry has any callout associations, if there is a
338 * callout association try to match the inventory path to the corresponding
339 * IPMI sensor.
340 */
341 for (const auto& item : assocs)
342 {
343 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
344 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700345 auto iter = invSensors.find(std::get<2>(item));
346 if (iter == invSensors.end())
347 {
348 iter = invSensors.find(BOARD_SENSOR);
349 if (iter == invSensors.end())
350 {
George Liu7acfcf02024-07-17 20:14:56 +0800351 lg2::error("Motherboard sensor not found");
Patrick Venture0b02be92018-08-31 11:55:55 -0700352 elog<InternalFailure>();
353 }
354 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530355
Patrick Venture0b02be92018-08-31 11:55:55 -0700356 return internal::prepareSELEntry(objPath, iter);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530357 }
358 }
359
360 // If there are no callout associations link the log entry to system event
361 // sensor
362 auto iter = invSensors.find(SYSTEM_SENSOR);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530363 return internal::prepareSELEntry(objPath, iter);
364}
365
Tom Joseph399fd922017-06-30 18:40:30 +0530366std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
367{
Patrick Williams5d82f472022-07-22 19:26:53 -0500368 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph399fd922017-06-30 18:40:30 +0530369
George Liu42f64ef2024-02-05 15:03:18 +0800370 static constexpr auto propTimeStamp = "Timestamp";
Tom Joseph399fd922017-06-30 18:40:30 +0530371
George Liu42f64ef2024-02-05 15:03:18 +0800372 uint64_t timeStamp;
George Liu3e3cc352023-07-26 15:59:31 +0800373 try
Tom Joseph399fd922017-06-30 18:40:30 +0530374 {
George Liu42f64ef2024-02-05 15:03:18 +0800375 auto service = ipmi::getService(bus, logEntryIntf, objPath);
376 auto propValue = ipmi::getDbusProperty(bus, service, objPath,
377 logEntryIntf, propTimeStamp);
378 timeStamp = std::get<uint64_t>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800379 }
380 catch (const std::exception& e)
381 {
George Liu7acfcf02024-07-17 20:14:56 +0800382 lg2::error("Error in reading Timestamp from Entry interface: {ERROR}",
383 "ERROR", e);
Tom Joseph399fd922017-06-30 18:40:30 +0530384 elog<InternalFailure>();
385 }
386
George Liu42f64ef2024-02-05 15:03:18 +0800387 std::chrono::milliseconds chronoTimeStamp(timeStamp);
Tom Joseph399fd922017-06-30 18:40:30 +0530388
389 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
390}
391
Tom Joseph232f5292017-07-07 20:14:02 +0530392void readLoggingObjectPaths(ObjectPaths& paths)
393{
Patrick Williams5d82f472022-07-22 19:26:53 -0500394 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph232f5292017-07-07 20:14:02 +0530395 auto depth = 0;
396 paths.clear();
397
Patrick Venture0b02be92018-08-31 11:55:55 -0700398 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
399 mapperIntf, "GetSubTreePaths");
Tom Joseph232f5292017-07-07 20:14:02 +0530400 mapperCall.append(logBasePath);
401 mapperCall.append(depth);
402 mapperCall.append(ObjectPaths({logEntryIntf}));
403
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300404 try
Tom Joseph232f5292017-07-07 20:14:02 +0530405 {
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300406 auto reply = bus.call(mapperCall);
Tom Joseph232f5292017-07-07 20:14:02 +0530407 reply.read(paths);
Tom Joseph232f5292017-07-07 20:14:02 +0530408 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500409 catch (const sdbusplus::exception_t& e)
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300410 {
411 if (strcmp(e.name(),
412 "xyz.openbmc_project.Common.Error.ResourceNotFound"))
413 {
414 throw;
415 }
416 }
417
418 std::sort(paths.begin(), paths.end(),
419 [](const std::string& a, const std::string& b) {
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500420 namespace fs = std::filesystem;
421 fs::path pathA(a);
422 fs::path pathB(b);
423 auto idA = std::stoul(pathA.filename().string());
424 auto idB = std::stoul(pathB.filename().string());
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300425
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -0500426 return idA < idB;
427 });
Tom Joseph232f5292017-07-07 20:14:02 +0530428}
429
Tom Joseph6b7a1432017-05-19 10:43:36 +0530430} // namespace sel
431
432} // namespace ipmi