blob: 9efd391b10f72dbcc350be8be2e54425da3d9ad4 [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>
Alexander Hansen93aa9832025-11-07 15:29:49 +010011#include <xyz/openbmc_project/ObjectMapper/common.hpp>
Patrick Venture46470a32018-09-07 19:26:25 -070012
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050013#include <charconv>
14#include <chrono>
15#include <filesystem>
16#include <vector>
17
Tom Joseph6b7a1432017-05-19 10:43:36 +053018extern const ipmi::sensor::InvObjectIDMap invSensors;
19using namespace phosphor::logging;
20using InternalFailure =
Willy Tu523e2d12023-09-05 11:36:48 -070021 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
Tom Joseph6b7a1432017-05-19 10:43:36 +053022
Alexander Hansen93aa9832025-11-07 15:29:49 +010023using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
24
Lei YU5015e952020-12-03 14:01:31 +080025namespace
26{
27
28constexpr auto systemEventRecord = 0x02;
Lei YU07c15b72020-12-06 15:08:06 +080029constexpr auto generatorID = 0x2000;
30constexpr auto eventMsgRevision = 0x04;
31constexpr auto assertEvent = 0x00;
32constexpr auto deassertEvent = 0x80;
33constexpr auto selDataSize = 3;
34constexpr auto oemCDDataSize = 9;
35constexpr auto oemEFDataSize = 13;
Lei YU5015e952020-12-03 14:01:31 +080036
Patrick Williams39deff22024-12-11 21:12:59 -050037constexpr auto propAdditionalData = "AdditionalData";
Lei YU07c15b72020-12-06 15:08:06 +080038constexpr auto propResolved = "Resolved";
39
Lei YU719a41c2020-12-03 17:50:44 +080040constexpr auto strEventDir = "EVENT_DIR";
41constexpr auto strGenerateId = "GENERATOR_ID";
42constexpr auto strRecordType = "RECORD_TYPE";
43constexpr auto strSensorData = "SENSOR_DATA";
44constexpr auto strSensorPath = "SENSOR_PATH";
45
Lei YU5015e952020-12-03 14:01:31 +080046} // namespace
47
Tom Joseph6b7a1432017-05-19 10:43:36 +053048namespace ipmi
49{
50
51namespace sel
52{
53
54namespace internal
55{
56
Lei YU5015e952020-12-03 14:01:31 +080057inline bool isRecordOEM(uint8_t recordType)
58{
59 return recordType != systemEventRecord;
60}
61
Lei YU07c15b72020-12-06 15:08:06 +080062using entryDataMap = std::map<PropertyName, PropertyType>;
Lei YU5015e952020-12-03 14:01:31 +080063
Lei YU07c15b72020-12-06 15:08:06 +080064int convert(const std::string_view& str, int base = 10)
Lei YU5015e952020-12-03 14:01:31 +080065{
William A. Kennington III4c521022023-07-28 13:07:30 -070066 int ret = 0;
Lei YU5015e952020-12-03 14:01:31 +080067 std::from_chars(str.data(), str.data() + str.size(), ret, base);
Lei YU07c15b72020-12-06 15:08:06 +080068 return ret;
Lei YU5015e952020-12-03 14:01:31 +080069}
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 {
Lei YU07c15b72020-12-06 15:08:06 +080079 ret.emplace_back(
80 static_cast<uint8_t>(convert(str.substr(i * 2, 2), 16)));
Lei YU5015e952020-12-03 14:01:31 +080081 }
82 return ret;
83}
84
Lei YU719a41c2020-12-03 17:50:44 +080085/** Construct OEM SEL record according to IPMI spec 32.2, 32.3. */
Lei YU07c15b72020-12-06 15:08:06 +080086void constructOEMSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Potin Lai0c6561d2025-01-16 00:08:52 +080087 const AdditionalData& data, GetSELEntryResponse& record)
Lei YU719a41c2020-12-03 17:50:44 +080088{
Potin Lai0c6561d2025-01-16 00:08:52 +080089 auto dataIter = data.find(strSensorData);
90 assert(dataIter != data.end());
Lei YU719a41c2020-12-03 17:50:44 +080091 auto sensorData = convertVec(dataIter->second);
92 if (recordType >= 0xC0 && recordType < 0xE0)
93 {
94 record.event.oemCD.timeStamp = static_cast<uint32_t>(
95 std::chrono::duration_cast<std::chrono::seconds>(timestamp)
96 .count());
97 record.event.oemCD.recordType = recordType;
98 // The ManufactureID and OEM Defined are packed in the sensor data
99 // Fill the 9 bytes of Manufacture ID and oemDefined
100 memcpy(&record.event.oemCD.manufacturerID, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +0800101 std::min(sensorData.size(), static_cast<size_t>(oemCDDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +0800102 }
103 else if (recordType >= 0xE0)
104 {
105 record.event.oemEF.recordType = recordType;
106 // The remaining 13 bytes are the OEM Defined data
107 memcpy(&record.event.oemEF.oemDefined, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +0800108 std::min(sensorData.size(), static_cast<size_t>(oemEFDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +0800109 }
110}
111
Lei YU07c15b72020-12-06 15:08:06 +0800112void constructSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Potin Lai0c6561d2025-01-16 00:08:52 +0800113 const AdditionalData& data, const entryDataMap&,
Lei YU07c15b72020-12-06 15:08:06 +0800114 GetSELEntryResponse& record)
115{
116 if (recordType != systemEventRecord)
117 {
George Liu7acfcf02024-07-17 20:14:56 +0800118 lg2::error("Invalid recordType");
Lei YU07c15b72020-12-06 15:08:06 +0800119 elog<InternalFailure>();
120 }
121
122 // Default values when there is no matched sensor
123 record.event.eventRecord.sensorType = 0;
124 record.event.eventRecord.sensorNum = 0xFF;
125 record.event.eventRecord.eventType = 0;
126
Potin Lai0c6561d2025-01-16 00:08:52 +0800127 auto iter = data.find(strSensorPath);
128 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800129 const auto& sensorPath = iter->second;
130 auto sensorIter = invSensors.find(sensorPath);
131
132 if (sensorIter != invSensors.end())
133 {
134 // There is a matched sensor
135 record.event.eventRecord.sensorType = sensorIter->second.sensorType;
136 record.event.eventRecord.sensorNum = sensorIter->second.sensorID;
137
Potin Lai0c6561d2025-01-16 00:08:52 +0800138 iter = data.find(strEventDir);
139 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800140 auto eventDir = static_cast<uint8_t>(convert(iter->second));
141 uint8_t assert = eventDir ? assertEvent : deassertEvent;
142 record.event.eventRecord.eventType =
143 assert | sensorIter->second.eventReadingType;
144 }
145 record.event.eventRecord.recordType = recordType;
146 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
147 std::chrono::duration_cast<std::chrono::seconds>(timestamp).count());
Potin Lai0c6561d2025-01-16 00:08:52 +0800148 iter = data.find(strGenerateId);
149 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800150 record.event.eventRecord.generatorID =
151 static_cast<uint16_t>(convert(iter->second));
152 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
Potin Lai0c6561d2025-01-16 00:08:52 +0800153 iter = data.find(strSensorData);
154 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800155 auto sensorData = convertVec(iter->second);
156 // The remaining 3 bytes are the sensor data
157 memcpy(&record.event.eventRecord.eventData1, sensorData.data(),
158 std::min(sensorData.size(), static_cast<size_t>(selDataSize)));
159}
160
Patrick Williams69b4c282025-03-03 11:19:13 -0500161GetSELEntryResponse prepareSELEntry(
162 const std::string& objPath,
163 ipmi::sensor::InvObjectIDMap::const_iterator iter)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530164{
Patrick Venture0b02be92018-08-31 11:55:55 -0700165 GetSELEntryResponse record{};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530166
Patrick Williams5d82f472022-07-22 19:26:53 -0500167 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530168 auto service = ipmi::getService(bus, logEntryIntf, objPath);
169
170 // Read all the log entry properties.
Patrick Venture0b02be92018-08-31 11:55:55 -0700171 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
172 propIntf, "GetAll");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530173 methodCall.append(logEntryIntf);
174
George Liu3e3cc352023-07-26 15:59:31 +0800175 entryDataMap entryData;
176 try
Tom Joseph6b7a1432017-05-19 10:43:36 +0530177 {
George Liu3e3cc352023-07-26 15:59:31 +0800178 auto reply = bus.call(methodCall);
179 reply.read(entryData);
180 }
181 catch (const std::exception& e)
182 {
George Liu7acfcf02024-07-17 20:14:56 +0800183 lg2::error("Error in reading logging property entries: {ERROR}",
184 "ERROR", e);
Tom Joseph6b7a1432017-05-19 10:43:36 +0530185 elog<InternalFailure>();
186 }
187
Tom Joseph6b7a1432017-05-19 10:43:36 +0530188 // Read Id from the log entry.
189 static constexpr auto propId = "Id";
190 auto iterId = entryData.find(propId);
191 if (iterId == entryData.end())
192 {
George Liu7acfcf02024-07-17 20:14:56 +0800193 lg2::error("Error in reading Id of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530194 elog<InternalFailure>();
195 }
196
Tom Joseph6b7a1432017-05-19 10:43:36 +0530197 // Read Timestamp from the log entry.
198 static constexpr auto propTimeStamp = "Timestamp";
199 auto iterTimeStamp = entryData.find(propTimeStamp);
200 if (iterTimeStamp == entryData.end())
201 {
George Liu7acfcf02024-07-17 20:14:56 +0800202 lg2::error("Error in reading Timestamp of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530203 elog<InternalFailure>();
204 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530205 std::chrono::milliseconds chronoTimeStamp(
Vernon Maueryf442e112019-04-09 11:44:36 -0700206 std::get<uint64_t>(iterTimeStamp->second));
Tom Joseph6b7a1432017-05-19 10:43:36 +0530207
Lei YU690f4d52021-04-28 19:05:24 +0800208 bool isFromSELLogger = false;
Lei YU690f4d52021-04-28 19:05:24 +0800209
210 // The recordID are with the same offset between different types,
211 // so we are safe to set the recordID here
212 record.event.eventRecord.recordID =
213 static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
214
215 iterId = entryData.find(propAdditionalData);
216 if (iterId != entryData.end())
217 {
218 // Check if it's a SEL from phosphor-sel-logger which shall contain
219 // the record ID, etc
Patrick Williams39deff22024-12-11 21:12:59 -0500220 const auto& addData = std::get<AdditionalData>(iterId->second);
Patrick Williamsbe5a2e02024-11-22 11:23:17 -0500221 auto recordTypeIter = addData.find(strRecordType);
222 if (recordTypeIter != addData.end())
Lei YU690f4d52021-04-28 19:05:24 +0800223 {
224 // It is a SEL from phosphor-sel-logger
225 isFromSELLogger = true;
226 }
227 else
228 {
229 // Not a SEL from phosphor-sel-logger, it shall have a valid
230 // invSensor
231 if (iter == invSensors.end())
232 {
George Liu7acfcf02024-07-17 20:14:56 +0800233 lg2::error("System event sensor not found");
Lei YU690f4d52021-04-28 19:05:24 +0800234 elog<InternalFailure>();
235 }
236 }
237 }
238
239 if (isFromSELLogger)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530240 {
Lei YUaf378fa2020-12-02 16:28:57 +0800241 // It is expected to be a custom SEL entry
Potin Lai0c6561d2025-01-16 00:08:52 +0800242 const auto& addData = std::get<AdditionalData>(iterId->second);
243 auto recordType =
244 static_cast<uint8_t>(convert(addData.find(strRecordType)->second));
Lei YU5015e952020-12-03 14:01:31 +0800245 auto isOEM = isRecordOEM(recordType);
246 if (isOEM)
247 {
Potin Lai0c6561d2025-01-16 00:08:52 +0800248 constructOEMSEL(recordType, chronoTimeStamp, addData, record);
Lei YU5015e952020-12-03 14:01:31 +0800249 }
250 else
251 {
Potin Lai0c6561d2025-01-16 00:08:52 +0800252 constructSEL(recordType, chronoTimeStamp, addData, entryData,
253 record);
Lei YU5015e952020-12-03 14:01:31 +0800254 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530255 }
256 else
257 {
Lei YUaf378fa2020-12-02 16:28:57 +0800258 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
259 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
260 .count());
261
Lei YUaf378fa2020-12-02 16:28:57 +0800262 record.event.eventRecord.recordType = systemEventRecord;
263 record.event.eventRecord.generatorID = generatorID;
264 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
265
266 record.event.eventRecord.sensorType = iter->second.sensorType;
267 record.event.eventRecord.sensorNum = iter->second.sensorID;
268 record.event.eventRecord.eventData1 = iter->second.eventOffset;
269
270 // Read Resolved from the log entry.
Lei YUaf378fa2020-12-02 16:28:57 +0800271 auto iterResolved = entryData.find(propResolved);
272 if (iterResolved == entryData.end())
273 {
George Liu7acfcf02024-07-17 20:14:56 +0800274 lg2::error("Error in reading Resolved field of logging entry");
Lei YUaf378fa2020-12-02 16:28:57 +0800275 elog<InternalFailure>();
276 }
277
Lei YUaf378fa2020-12-02 16:28:57 +0800278 // Evaluate if the event is assertion or deassertion event
279 if (std::get<bool>(iterResolved->second))
280 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400281 record.event.eventRecord.eventType =
282 deassertEvent | iter->second.eventReadingType;
Lei YUaf378fa2020-12-02 16:28:57 +0800283 }
284 else
285 {
286 record.event.eventRecord.eventType = iter->second.eventReadingType;
287 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530288 }
289
290 return record;
291}
292
293} // namespace internal
294
Tom Joseph6edc8a02017-06-30 18:52:56 +0530295GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
296{
Patrick Williams5d82f472022-07-22 19:26:53 -0500297 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6edc8a02017-06-30 18:52:56 +0530298
Vernon Mauerya7f81cc2019-11-06 12:51:43 -0800299 static constexpr auto assocIntf =
300 "xyz.openbmc_project.Association.Definitions";
301 static constexpr auto assocProp = "Associations";
Tom Joseph6edc8a02017-06-30 18:52:56 +0530302
George Liu42f64ef2024-02-05 15:03:18 +0800303 std::vector<ipmi::Association> assocs;
George Liu3e3cc352023-07-26 15:59:31 +0800304 try
305 {
George Liu42f64ef2024-02-05 15:03:18 +0800306 auto service = ipmi::getService(bus, assocIntf, objPath);
Patrick Williams1318a5e2024-08-16 15:19:54 -0400307 auto propValue =
308 ipmi::getDbusProperty(bus, service, objPath, assocIntf, assocProp);
George Liu42f64ef2024-02-05 15:03:18 +0800309 assocs = std::get<std::vector<ipmi::Association>>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800310 }
311 catch (const std::exception& e)
312 {
George Liu7acfcf02024-07-17 20:14:56 +0800313 lg2::error("Error in reading Associations interface: {ERROR}", "ERROR",
314 e);
George Liu3e3cc352023-07-26 15:59:31 +0800315 elog<InternalFailure>();
316 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530317
Tom Joseph6edc8a02017-06-30 18:52:56 +0530318 /*
319 * Check if the log entry has any callout associations, if there is a
320 * callout association try to match the inventory path to the corresponding
321 * IPMI sensor.
322 */
323 for (const auto& item : assocs)
324 {
325 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
326 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700327 auto iter = invSensors.find(std::get<2>(item));
328 if (iter == invSensors.end())
329 {
330 iter = invSensors.find(BOARD_SENSOR);
331 if (iter == invSensors.end())
332 {
George Liu7acfcf02024-07-17 20:14:56 +0800333 lg2::error("Motherboard sensor not found");
Patrick Venture0b02be92018-08-31 11:55:55 -0700334 elog<InternalFailure>();
335 }
336 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530337
Patrick Venture0b02be92018-08-31 11:55:55 -0700338 return internal::prepareSELEntry(objPath, iter);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530339 }
340 }
341
342 // If there are no callout associations link the log entry to system event
343 // sensor
344 auto iter = invSensors.find(SYSTEM_SENSOR);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530345 return internal::prepareSELEntry(objPath, iter);
346}
347
Tom Joseph399fd922017-06-30 18:40:30 +0530348std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
349{
Patrick Williams5d82f472022-07-22 19:26:53 -0500350 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph399fd922017-06-30 18:40:30 +0530351
George Liu42f64ef2024-02-05 15:03:18 +0800352 static constexpr auto propTimeStamp = "Timestamp";
Tom Joseph399fd922017-06-30 18:40:30 +0530353
George Liu42f64ef2024-02-05 15:03:18 +0800354 uint64_t timeStamp;
George Liu3e3cc352023-07-26 15:59:31 +0800355 try
Tom Joseph399fd922017-06-30 18:40:30 +0530356 {
George Liu42f64ef2024-02-05 15:03:18 +0800357 auto service = ipmi::getService(bus, logEntryIntf, objPath);
358 auto propValue = ipmi::getDbusProperty(bus, service, objPath,
359 logEntryIntf, propTimeStamp);
360 timeStamp = std::get<uint64_t>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800361 }
362 catch (const std::exception& e)
363 {
George Liu7acfcf02024-07-17 20:14:56 +0800364 lg2::error("Error in reading Timestamp from Entry interface: {ERROR}",
365 "ERROR", e);
Tom Joseph399fd922017-06-30 18:40:30 +0530366 elog<InternalFailure>();
367 }
368
George Liu42f64ef2024-02-05 15:03:18 +0800369 std::chrono::milliseconds chronoTimeStamp(timeStamp);
Tom Joseph399fd922017-06-30 18:40:30 +0530370
371 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
372}
373
Tom Joseph232f5292017-07-07 20:14:02 +0530374void readLoggingObjectPaths(ObjectPaths& paths)
375{
Patrick Williams5d82f472022-07-22 19:26:53 -0500376 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph232f5292017-07-07 20:14:02 +0530377 auto depth = 0;
378 paths.clear();
379
Alexander Hansen93aa9832025-11-07 15:29:49 +0100380 auto mapperCall = bus.new_method_call(
381 ObjectMapper::default_service, ObjectMapper::instance_path,
382 ObjectMapper::interface,
383 ObjectMapper::method_names::get_sub_tree_paths);
Tom Joseph232f5292017-07-07 20:14:02 +0530384 mapperCall.append(logBasePath);
385 mapperCall.append(depth);
386 mapperCall.append(ObjectPaths({logEntryIntf}));
387
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300388 try
Tom Joseph232f5292017-07-07 20:14:02 +0530389 {
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300390 auto reply = bus.call(mapperCall);
Tom Joseph232f5292017-07-07 20:14:02 +0530391 reply.read(paths);
Tom Joseph232f5292017-07-07 20:14:02 +0530392 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500393 catch (const sdbusplus::exception_t& e)
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300394 {
395 if (strcmp(e.name(),
396 "xyz.openbmc_project.Common.Error.ResourceNotFound"))
397 {
398 throw;
399 }
400 }
401
402 std::sort(paths.begin(), paths.end(),
403 [](const std::string& a, const std::string& b) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400404 namespace fs = std::filesystem;
405 fs::path pathA(a);
406 fs::path pathB(b);
407 auto idA = std::stoul(pathA.filename().string());
408 auto idB = std::stoul(pathB.filename().string());
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300409
Patrick Williams1318a5e2024-08-16 15:19:54 -0400410 return idA < idB;
411 });
Tom Joseph232f5292017-07-07 20:14:02 +0530412}
413
Tom Joseph6b7a1432017-05-19 10:43:36 +0530414} // namespace sel
415
416} // namespace ipmi