| Patrick Venture | 46470a3 | 2018-09-07 19:26:25 -0700 | [diff] [blame] | 1 | #include "config.h" | 
|  | 2 |  | 
|  | 3 | #include "selutility.hpp" | 
|  | 4 |  | 
| Lei YU | 5015e95 | 2020-12-03 14:01:31 +0800 | [diff] [blame^] | 5 | #include <charconv> | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 6 | #include <chrono> | 
| Vernon Mauery | bdda800 | 2019-02-26 10:18:51 -0800 | [diff] [blame] | 7 | #include <filesystem> | 
| Vernon Mauery | e08fbff | 2019-04-03 09:19:34 -0700 | [diff] [blame] | 8 | #include <ipmid/api.hpp> | 
| Vernon Mauery | 3325024 | 2019-03-12 16:49:26 -0700 | [diff] [blame] | 9 | #include <ipmid/types.hpp> | 
| Vernon Mauery | 6a98fe7 | 2019-03-11 15:57:48 -0700 | [diff] [blame] | 10 | #include <ipmid/utils.hpp> | 
| Patrick Venture | 3a5071a | 2018-09-12 13:27:42 -0700 | [diff] [blame] | 11 | #include <phosphor-logging/elog-errors.hpp> | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 12 | #include <vector> | 
| Patrick Venture | 3a5071a | 2018-09-12 13:27:42 -0700 | [diff] [blame] | 13 | #include <xyz/openbmc_project/Common/error.hpp> | 
| Patrick Venture | 46470a3 | 2018-09-07 19:26:25 -0700 | [diff] [blame] | 14 |  | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 15 | extern const ipmi::sensor::InvObjectIDMap invSensors; | 
|  | 16 | using namespace phosphor::logging; | 
|  | 17 | using InternalFailure = | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 18 | sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 19 |  | 
| Lei YU | 5015e95 | 2020-12-03 14:01:31 +0800 | [diff] [blame^] | 20 | namespace | 
|  | 21 | { | 
|  | 22 |  | 
|  | 23 | constexpr auto systemEventRecord = 0x02; | 
|  | 24 |  | 
|  | 25 | } // namespace | 
|  | 26 |  | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 27 | namespace ipmi | 
|  | 28 | { | 
|  | 29 |  | 
|  | 30 | namespace sel | 
|  | 31 | { | 
|  | 32 |  | 
|  | 33 | namespace internal | 
|  | 34 | { | 
|  | 35 |  | 
| Lei YU | 5015e95 | 2020-12-03 14:01:31 +0800 | [diff] [blame^] | 36 | inline bool isRecordOEM(uint8_t recordType) | 
|  | 37 | { | 
|  | 38 | return recordType != systemEventRecord; | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 | /** Parse the entry with format like key=val */ | 
|  | 42 | std::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 |  | 
|  | 52 | std::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 |  | 
|  | 64 | uint8_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 | 
|  | 72 | std::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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 84 | GetSELEntryResponse | 
|  | 85 | prepareSELEntry(const std::string& objPath, | 
|  | 86 | ipmi::sensor::InvObjectIDMap::const_iterator iter) | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 87 | { | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 88 | GetSELEntryResponse record{}; | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 89 |  | 
|  | 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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 94 | auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(), | 
|  | 95 | propIntf, "GetAll"); | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 96 | 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 Joseph | 306878b | 2017-07-10 19:30:54 +0530 | [diff] [blame] | 105 | std::map<PropertyName, PropertyType> entryData; | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 106 | 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 Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 117 | // 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 Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 125 | std::chrono::milliseconds chronoTimeStamp( | 
| Vernon Mauery | f442e11 | 2019-04-09 11:44:36 -0700 | [diff] [blame] | 126 | std::get<uint64_t>(iterTimeStamp->second)); | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 127 |  | 
| Lei YU | af378fa | 2020-12-02 16:28:57 +0800 | [diff] [blame] | 128 | if (iter == invSensors.end()) | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 129 | { | 
| Lei YU | af378fa | 2020-12-02 16:28:57 +0800 | [diff] [blame] | 130 | // It is expected to be a custom SEL entry | 
| Lei YU | 5015e95 | 2020-12-03 14:01:31 +0800 | [diff] [blame^] | 131 | 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 Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 174 | } | 
|  | 175 | else | 
|  | 176 | { | 
| Lei YU | af378fa | 2020-12-02 16:28:57 +0800 | [diff] [blame] | 177 |  | 
|  | 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 YU | af378fa | 2020-12-02 16:28:57 +0800 | [diff] [blame] | 184 | 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 Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 216 | } | 
|  | 217 |  | 
|  | 218 | return record; | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | } // namespace internal | 
|  | 222 |  | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 223 | GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath) | 
|  | 224 | { | 
|  | 225 | sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; | 
|  | 226 |  | 
| Vernon Mauery | a7f81cc | 2019-11-06 12:51:43 -0800 | [diff] [blame] | 227 | static constexpr auto assocIntf = | 
|  | 228 | "xyz.openbmc_project.Association.Definitions"; | 
|  | 229 | static constexpr auto assocProp = "Associations"; | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 230 |  | 
|  | 231 | auto service = ipmi::getService(bus, assocIntf, objPath); | 
|  | 232 |  | 
|  | 233 | // Read the Associations interface. | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 234 | auto methodCall = | 
|  | 235 | bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 236 | 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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 246 | using AssociationList = | 
|  | 247 | std::vector<std::tuple<std::string, std::string, std::string>>; | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 248 |  | 
| Vernon Mauery | 16b8693 | 2019-05-01 08:36:11 -0700 | [diff] [blame] | 249 | std::variant<AssociationList> list; | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 250 | reply.read(list); | 
|  | 251 |  | 
| Vernon Mauery | f442e11 | 2019-04-09 11:44:36 -0700 | [diff] [blame] | 252 | auto& assocs = std::get<AssociationList>(list); | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 253 |  | 
|  | 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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 263 | 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 Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 273 |  | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 274 | return internal::prepareSELEntry(objPath, iter); | 
| Tom Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 275 | } | 
|  | 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 Joseph | 6edc8a0 | 2017-06-30 18:52:56 +0530 | [diff] [blame] | 281 | return internal::prepareSELEntry(objPath, iter); | 
|  | 282 | } | 
|  | 283 |  | 
| Tom Joseph | 399fd92 | 2017-06-30 18:40:30 +0530 | [diff] [blame] | 284 | std::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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 293 | auto methodCall = | 
|  | 294 | bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); | 
| Tom Joseph | 399fd92 | 2017-06-30 18:40:30 +0530 | [diff] [blame] | 295 | 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 Mauery | 16b8693 | 2019-05-01 08:36:11 -0700 | [diff] [blame] | 305 | std::variant<uint64_t> timeStamp; | 
| Tom Joseph | 399fd92 | 2017-06-30 18:40:30 +0530 | [diff] [blame] | 306 | reply.read(timeStamp); | 
|  | 307 |  | 
| Vernon Mauery | f442e11 | 2019-04-09 11:44:36 -0700 | [diff] [blame] | 308 | std::chrono::milliseconds chronoTimeStamp(std::get<uint64_t>(timeStamp)); | 
| Tom Joseph | 399fd92 | 2017-06-30 18:40:30 +0530 | [diff] [blame] | 309 |  | 
|  | 310 | return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp); | 
|  | 311 | } | 
|  | 312 |  | 
| Tom Joseph | 232f529 | 2017-07-07 20:14:02 +0530 | [diff] [blame] | 313 | void readLoggingObjectPaths(ObjectPaths& paths) | 
|  | 314 | { | 
|  | 315 | sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; | 
|  | 316 | auto depth = 0; | 
|  | 317 | paths.clear(); | 
|  | 318 |  | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 319 | auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, | 
|  | 320 | mapperIntf, "GetSubTreePaths"); | 
| Tom Joseph | 232f529 | 2017-07-07 20:14:02 +0530 | [diff] [blame] | 321 | 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 Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 334 | 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 Joseph | 232f529 | 2017-07-07 20:14:02 +0530 | [diff] [blame] | 341 |  | 
| Patrick Venture | 0b02be9 | 2018-08-31 11:55:55 -0700 | [diff] [blame] | 342 | return idA < idB; | 
|  | 343 | }); | 
| Tom Joseph | 232f529 | 2017-07-07 20:14:02 +0530 | [diff] [blame] | 344 | } | 
|  | 345 | } | 
|  | 346 |  | 
| Tom Joseph | 6b7a143 | 2017-05-19 10:43:36 +0530 | [diff] [blame] | 347 | } // namespace sel | 
|  | 348 |  | 
|  | 349 | } // namespace ipmi |