blob: cf19307161498d2cd18733fb8b53c9e2ecb6717e [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
Patrick Williams39deff22024-12-11 21:12:59 -050034constexpr 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 YU07c15b72020-12-06 15:08:06 +080059using entryDataMap = std::map<PropertyName, PropertyType>;
Lei YU5015e952020-12-03 14:01:31 +080060
Lei YU07c15b72020-12-06 15:08:06 +080061int convert(const std::string_view& str, int base = 10)
Lei YU5015e952020-12-03 14:01:31 +080062{
William A. Kennington III4c521022023-07-28 13:07:30 -070063 int ret = 0;
Lei YU5015e952020-12-03 14:01:31 +080064 std::from_chars(str.data(), str.data() + str.size(), ret, base);
Lei YU07c15b72020-12-06 15:08:06 +080065 return ret;
Lei YU5015e952020-12-03 14:01:31 +080066}
67
68// Convert the string to a vector of uint8_t, where the str is formatted as hex
69std::vector<uint8_t> convertVec(const std::string_view& str)
70{
71 std::vector<uint8_t> ret;
72 auto len = str.size() / 2;
73 ret.reserve(len);
74 for (size_t i = 0; i < len; ++i)
75 {
Lei YU07c15b72020-12-06 15:08:06 +080076 ret.emplace_back(
77 static_cast<uint8_t>(convert(str.substr(i * 2, 2), 16)));
Lei YU5015e952020-12-03 14:01:31 +080078 }
79 return ret;
80}
81
Lei YU719a41c2020-12-03 17:50:44 +080082/** Construct OEM SEL record according to IPMI spec 32.2, 32.3. */
Lei YU07c15b72020-12-06 15:08:06 +080083void constructOEMSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Potin Lai0c6561d2025-01-16 00:08:52 +080084 const AdditionalData& data, GetSELEntryResponse& record)
Lei YU719a41c2020-12-03 17:50:44 +080085{
Potin Lai0c6561d2025-01-16 00:08:52 +080086 auto dataIter = data.find(strSensorData);
87 assert(dataIter != data.end());
Lei YU719a41c2020-12-03 17:50:44 +080088 auto sensorData = convertVec(dataIter->second);
89 if (recordType >= 0xC0 && recordType < 0xE0)
90 {
91 record.event.oemCD.timeStamp = static_cast<uint32_t>(
92 std::chrono::duration_cast<std::chrono::seconds>(timestamp)
93 .count());
94 record.event.oemCD.recordType = recordType;
95 // The ManufactureID and OEM Defined are packed in the sensor data
96 // Fill the 9 bytes of Manufacture ID and oemDefined
97 memcpy(&record.event.oemCD.manufacturerID, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +080098 std::min(sensorData.size(), static_cast<size_t>(oemCDDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +080099 }
100 else if (recordType >= 0xE0)
101 {
102 record.event.oemEF.recordType = recordType;
103 // The remaining 13 bytes are the OEM Defined data
104 memcpy(&record.event.oemEF.oemDefined, sensorData.data(),
Lei YU07c15b72020-12-06 15:08:06 +0800105 std::min(sensorData.size(), static_cast<size_t>(oemEFDataSize)));
Lei YU719a41c2020-12-03 17:50:44 +0800106 }
107}
108
Lei YU07c15b72020-12-06 15:08:06 +0800109void constructSEL(uint8_t recordType, std::chrono::milliseconds timestamp,
Potin Lai0c6561d2025-01-16 00:08:52 +0800110 const AdditionalData& data, const entryDataMap&,
Lei YU07c15b72020-12-06 15:08:06 +0800111 GetSELEntryResponse& record)
112{
113 if (recordType != systemEventRecord)
114 {
George Liu7acfcf02024-07-17 20:14:56 +0800115 lg2::error("Invalid recordType");
Lei YU07c15b72020-12-06 15:08:06 +0800116 elog<InternalFailure>();
117 }
118
119 // Default values when there is no matched sensor
120 record.event.eventRecord.sensorType = 0;
121 record.event.eventRecord.sensorNum = 0xFF;
122 record.event.eventRecord.eventType = 0;
123
Potin Lai0c6561d2025-01-16 00:08:52 +0800124 auto iter = data.find(strSensorPath);
125 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800126 const auto& sensorPath = iter->second;
127 auto sensorIter = invSensors.find(sensorPath);
128
129 if (sensorIter != invSensors.end())
130 {
131 // There is a matched sensor
132 record.event.eventRecord.sensorType = sensorIter->second.sensorType;
133 record.event.eventRecord.sensorNum = sensorIter->second.sensorID;
134
Potin Lai0c6561d2025-01-16 00:08:52 +0800135 iter = data.find(strEventDir);
136 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800137 auto eventDir = static_cast<uint8_t>(convert(iter->second));
138 uint8_t assert = eventDir ? assertEvent : deassertEvent;
139 record.event.eventRecord.eventType =
140 assert | sensorIter->second.eventReadingType;
141 }
142 record.event.eventRecord.recordType = recordType;
143 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
144 std::chrono::duration_cast<std::chrono::seconds>(timestamp).count());
Potin Lai0c6561d2025-01-16 00:08:52 +0800145 iter = data.find(strGenerateId);
146 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800147 record.event.eventRecord.generatorID =
148 static_cast<uint16_t>(convert(iter->second));
149 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
Potin Lai0c6561d2025-01-16 00:08:52 +0800150 iter = data.find(strSensorData);
151 assert(iter != data.end());
Lei YU07c15b72020-12-06 15:08:06 +0800152 auto sensorData = convertVec(iter->second);
153 // The remaining 3 bytes are the sensor data
154 memcpy(&record.event.eventRecord.eventData1, sensorData.data(),
155 std::min(sensorData.size(), static_cast<size_t>(selDataSize)));
156}
157
Patrick Venture0b02be92018-08-31 11:55:55 -0700158GetSELEntryResponse
159 prepareSELEntry(const std::string& objPath,
160 ipmi::sensor::InvObjectIDMap::const_iterator iter)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530161{
Patrick Venture0b02be92018-08-31 11:55:55 -0700162 GetSELEntryResponse record{};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530163
Patrick Williams5d82f472022-07-22 19:26:53 -0500164 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6b7a1432017-05-19 10:43:36 +0530165 auto service = ipmi::getService(bus, logEntryIntf, objPath);
166
167 // Read all the log entry properties.
Patrick Venture0b02be92018-08-31 11:55:55 -0700168 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(),
169 propIntf, "GetAll");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530170 methodCall.append(logEntryIntf);
171
George Liu3e3cc352023-07-26 15:59:31 +0800172 entryDataMap entryData;
173 try
Tom Joseph6b7a1432017-05-19 10:43:36 +0530174 {
George Liu3e3cc352023-07-26 15:59:31 +0800175 auto reply = bus.call(methodCall);
176 reply.read(entryData);
177 }
178 catch (const std::exception& e)
179 {
George Liu7acfcf02024-07-17 20:14:56 +0800180 lg2::error("Error in reading logging property entries: {ERROR}",
181 "ERROR", e);
Tom Joseph6b7a1432017-05-19 10:43:36 +0530182 elog<InternalFailure>();
183 }
184
Tom Joseph6b7a1432017-05-19 10:43:36 +0530185 // Read Id from the log entry.
186 static constexpr auto propId = "Id";
187 auto iterId = entryData.find(propId);
188 if (iterId == entryData.end())
189 {
George Liu7acfcf02024-07-17 20:14:56 +0800190 lg2::error("Error in reading Id of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530191 elog<InternalFailure>();
192 }
193
Tom Joseph6b7a1432017-05-19 10:43:36 +0530194 // Read Timestamp from the log entry.
195 static constexpr auto propTimeStamp = "Timestamp";
196 auto iterTimeStamp = entryData.find(propTimeStamp);
197 if (iterTimeStamp == entryData.end())
198 {
George Liu7acfcf02024-07-17 20:14:56 +0800199 lg2::error("Error in reading Timestamp of logging entry");
Tom Joseph6b7a1432017-05-19 10:43:36 +0530200 elog<InternalFailure>();
201 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530202 std::chrono::milliseconds chronoTimeStamp(
Vernon Maueryf442e112019-04-09 11:44:36 -0700203 std::get<uint64_t>(iterTimeStamp->second));
Tom Joseph6b7a1432017-05-19 10:43:36 +0530204
Lei YU690f4d52021-04-28 19:05:24 +0800205 bool isFromSELLogger = false;
Lei YU690f4d52021-04-28 19:05:24 +0800206
207 // The recordID are with the same offset between different types,
208 // so we are safe to set the recordID here
209 record.event.eventRecord.recordID =
210 static_cast<uint16_t>(std::get<uint32_t>(iterId->second));
211
212 iterId = entryData.find(propAdditionalData);
213 if (iterId != entryData.end())
214 {
215 // Check if it's a SEL from phosphor-sel-logger which shall contain
216 // the record ID, etc
Patrick Williams39deff22024-12-11 21:12:59 -0500217 const auto& addData = std::get<AdditionalData>(iterId->second);
Patrick Williamsbe5a2e02024-11-22 11:23:17 -0500218 auto recordTypeIter = addData.find(strRecordType);
219 if (recordTypeIter != addData.end())
Lei YU690f4d52021-04-28 19:05:24 +0800220 {
221 // It is a SEL from phosphor-sel-logger
222 isFromSELLogger = true;
223 }
224 else
225 {
226 // Not a SEL from phosphor-sel-logger, it shall have a valid
227 // invSensor
228 if (iter == invSensors.end())
229 {
George Liu7acfcf02024-07-17 20:14:56 +0800230 lg2::error("System event sensor not found");
Lei YU690f4d52021-04-28 19:05:24 +0800231 elog<InternalFailure>();
232 }
233 }
234 }
235
236 if (isFromSELLogger)
Tom Joseph6b7a1432017-05-19 10:43:36 +0530237 {
Lei YUaf378fa2020-12-02 16:28:57 +0800238 // It is expected to be a custom SEL entry
Potin Lai0c6561d2025-01-16 00:08:52 +0800239 const auto& addData = std::get<AdditionalData>(iterId->second);
240 auto recordType =
241 static_cast<uint8_t>(convert(addData.find(strRecordType)->second));
Lei YU5015e952020-12-03 14:01:31 +0800242 auto isOEM = isRecordOEM(recordType);
243 if (isOEM)
244 {
Potin Lai0c6561d2025-01-16 00:08:52 +0800245 constructOEMSEL(recordType, chronoTimeStamp, addData, record);
Lei YU5015e952020-12-03 14:01:31 +0800246 }
247 else
248 {
Potin Lai0c6561d2025-01-16 00:08:52 +0800249 constructSEL(recordType, chronoTimeStamp, addData, entryData,
250 record);
Lei YU5015e952020-12-03 14:01:31 +0800251 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530252 }
253 else
254 {
Lei YUaf378fa2020-12-02 16:28:57 +0800255 record.event.eventRecord.timeStamp = static_cast<uint32_t>(
256 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp)
257 .count());
258
Lei YUaf378fa2020-12-02 16:28:57 +0800259 record.event.eventRecord.recordType = systemEventRecord;
260 record.event.eventRecord.generatorID = generatorID;
261 record.event.eventRecord.eventMsgRevision = eventMsgRevision;
262
263 record.event.eventRecord.sensorType = iter->second.sensorType;
264 record.event.eventRecord.sensorNum = iter->second.sensorID;
265 record.event.eventRecord.eventData1 = iter->second.eventOffset;
266
267 // Read Resolved from the log entry.
Lei YUaf378fa2020-12-02 16:28:57 +0800268 auto iterResolved = entryData.find(propResolved);
269 if (iterResolved == entryData.end())
270 {
George Liu7acfcf02024-07-17 20:14:56 +0800271 lg2::error("Error in reading Resolved field of logging entry");
Lei YUaf378fa2020-12-02 16:28:57 +0800272 elog<InternalFailure>();
273 }
274
Lei YUaf378fa2020-12-02 16:28:57 +0800275 // Evaluate if the event is assertion or deassertion event
276 if (std::get<bool>(iterResolved->second))
277 {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400278 record.event.eventRecord.eventType =
279 deassertEvent | iter->second.eventReadingType;
Lei YUaf378fa2020-12-02 16:28:57 +0800280 }
281 else
282 {
283 record.event.eventRecord.eventType = iter->second.eventReadingType;
284 }
Tom Joseph6b7a1432017-05-19 10:43:36 +0530285 }
286
287 return record;
288}
289
290} // namespace internal
291
Tom Joseph6edc8a02017-06-30 18:52:56 +0530292GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath)
293{
Patrick Williams5d82f472022-07-22 19:26:53 -0500294 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph6edc8a02017-06-30 18:52:56 +0530295
Vernon Mauerya7f81cc2019-11-06 12:51:43 -0800296 static constexpr auto assocIntf =
297 "xyz.openbmc_project.Association.Definitions";
298 static constexpr auto assocProp = "Associations";
Tom Joseph6edc8a02017-06-30 18:52:56 +0530299
George Liu42f64ef2024-02-05 15:03:18 +0800300 std::vector<ipmi::Association> assocs;
George Liu3e3cc352023-07-26 15:59:31 +0800301 try
302 {
George Liu42f64ef2024-02-05 15:03:18 +0800303 auto service = ipmi::getService(bus, assocIntf, objPath);
Patrick Williams1318a5e2024-08-16 15:19:54 -0400304 auto propValue =
305 ipmi::getDbusProperty(bus, service, objPath, assocIntf, assocProp);
George Liu42f64ef2024-02-05 15:03:18 +0800306 assocs = std::get<std::vector<ipmi::Association>>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800307 }
308 catch (const std::exception& e)
309 {
George Liu7acfcf02024-07-17 20:14:56 +0800310 lg2::error("Error in reading Associations interface: {ERROR}", "ERROR",
311 e);
George Liu3e3cc352023-07-26 15:59:31 +0800312 elog<InternalFailure>();
313 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530314
Tom Joseph6edc8a02017-06-30 18:52:56 +0530315 /*
316 * Check if the log entry has any callout associations, if there is a
317 * callout association try to match the inventory path to the corresponding
318 * IPMI sensor.
319 */
320 for (const auto& item : assocs)
321 {
322 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0)
323 {
Patrick Venture0b02be92018-08-31 11:55:55 -0700324 auto iter = invSensors.find(std::get<2>(item));
325 if (iter == invSensors.end())
326 {
327 iter = invSensors.find(BOARD_SENSOR);
328 if (iter == invSensors.end())
329 {
George Liu7acfcf02024-07-17 20:14:56 +0800330 lg2::error("Motherboard sensor not found");
Patrick Venture0b02be92018-08-31 11:55:55 -0700331 elog<InternalFailure>();
332 }
333 }
Tom Joseph6edc8a02017-06-30 18:52:56 +0530334
Patrick Venture0b02be92018-08-31 11:55:55 -0700335 return internal::prepareSELEntry(objPath, iter);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530336 }
337 }
338
339 // If there are no callout associations link the log entry to system event
340 // sensor
341 auto iter = invSensors.find(SYSTEM_SENSOR);
Tom Joseph6edc8a02017-06-30 18:52:56 +0530342 return internal::prepareSELEntry(objPath, iter);
343}
344
Tom Joseph399fd922017-06-30 18:40:30 +0530345std::chrono::seconds getEntryTimeStamp(const std::string& objPath)
346{
Patrick Williams5d82f472022-07-22 19:26:53 -0500347 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph399fd922017-06-30 18:40:30 +0530348
George Liu42f64ef2024-02-05 15:03:18 +0800349 static constexpr auto propTimeStamp = "Timestamp";
Tom Joseph399fd922017-06-30 18:40:30 +0530350
George Liu42f64ef2024-02-05 15:03:18 +0800351 uint64_t timeStamp;
George Liu3e3cc352023-07-26 15:59:31 +0800352 try
Tom Joseph399fd922017-06-30 18:40:30 +0530353 {
George Liu42f64ef2024-02-05 15:03:18 +0800354 auto service = ipmi::getService(bus, logEntryIntf, objPath);
355 auto propValue = ipmi::getDbusProperty(bus, service, objPath,
356 logEntryIntf, propTimeStamp);
357 timeStamp = std::get<uint64_t>(propValue);
George Liu3e3cc352023-07-26 15:59:31 +0800358 }
359 catch (const std::exception& e)
360 {
George Liu7acfcf02024-07-17 20:14:56 +0800361 lg2::error("Error in reading Timestamp from Entry interface: {ERROR}",
362 "ERROR", e);
Tom Joseph399fd922017-06-30 18:40:30 +0530363 elog<InternalFailure>();
364 }
365
George Liu42f64ef2024-02-05 15:03:18 +0800366 std::chrono::milliseconds chronoTimeStamp(timeStamp);
Tom Joseph399fd922017-06-30 18:40:30 +0530367
368 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp);
369}
370
Tom Joseph232f5292017-07-07 20:14:02 +0530371void readLoggingObjectPaths(ObjectPaths& paths)
372{
Patrick Williams5d82f472022-07-22 19:26:53 -0500373 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
Tom Joseph232f5292017-07-07 20:14:02 +0530374 auto depth = 0;
375 paths.clear();
376
Patrick Venture0b02be92018-08-31 11:55:55 -0700377 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
378 mapperIntf, "GetSubTreePaths");
Tom Joseph232f5292017-07-07 20:14:02 +0530379 mapperCall.append(logBasePath);
380 mapperCall.append(depth);
381 mapperCall.append(ObjectPaths({logEntryIntf}));
382
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300383 try
Tom Joseph232f5292017-07-07 20:14:02 +0530384 {
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300385 auto reply = bus.call(mapperCall);
Tom Joseph232f5292017-07-07 20:14:02 +0530386 reply.read(paths);
Tom Joseph232f5292017-07-07 20:14:02 +0530387 }
Patrick Williams5d82f472022-07-22 19:26:53 -0500388 catch (const sdbusplus::exception_t& e)
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300389 {
390 if (strcmp(e.name(),
391 "xyz.openbmc_project.Common.Error.ResourceNotFound"))
392 {
393 throw;
394 }
395 }
396
397 std::sort(paths.begin(), paths.end(),
398 [](const std::string& a, const std::string& b) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400399 namespace fs = std::filesystem;
400 fs::path pathA(a);
401 fs::path pathB(b);
402 auto idA = std::stoul(pathA.filename().string());
403 auto idB = std::stoul(pathB.filename().string());
Konstantin Aladyshev49434b62022-01-10 16:58:47 +0300404
Patrick Williams1318a5e2024-08-16 15:19:54 -0400405 return idA < idB;
406 });
Tom Joseph232f5292017-07-07 20:14:02 +0530407}
408
Tom Joseph6b7a1432017-05-19 10:43:36 +0530409} // namespace sel
410
411} // namespace ipmi