1 #include <chrono> 2 #include <vector> 3 #if __has_include(<filesystem>) 4 #include <filesystem> 5 #elif __has_include(<experimental/filesystem>) 6 #include <experimental/filesystem> 7 namespace std 8 { 9 // splice experimental::filesystem into std 10 namespace filesystem = std::experimental::filesystem; 11 } // namespace std 12 #else 13 #error filesystem not available 14 #endif 15 #include "config.h" 16 17 #include "selutility.hpp" 18 #include "types.hpp" 19 #include "utils.hpp" 20 21 #include <phosphor-logging/elog-errors.hpp> 22 #include <xyz/openbmc_project/Common/error.hpp> 23 24 #include "host-ipmid/ipmid-api.h" 25 26 extern const ipmi::sensor::InvObjectIDMap invSensors; 27 using namespace phosphor::logging; 28 using InternalFailure = 29 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 30 31 namespace ipmi 32 { 33 34 namespace sel 35 { 36 37 namespace internal 38 { 39 40 GetSELEntryResponse 41 prepareSELEntry(const std::string& objPath, 42 ipmi::sensor::InvObjectIDMap::const_iterator iter) 43 { 44 GetSELEntryResponse record{}; 45 46 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 47 auto service = ipmi::getService(bus, logEntryIntf, objPath); 48 49 // Read all the log entry properties. 50 auto methodCall = bus.new_method_call(service.c_str(), objPath.c_str(), 51 propIntf, "GetAll"); 52 methodCall.append(logEntryIntf); 53 54 auto reply = bus.call(methodCall); 55 if (reply.is_method_error()) 56 { 57 log<level::ERR>("Error in reading logging property entries"); 58 elog<InternalFailure>(); 59 } 60 61 std::map<PropertyName, PropertyType> entryData; 62 reply.read(entryData); 63 64 // Read Id from the log entry. 65 static constexpr auto propId = "Id"; 66 auto iterId = entryData.find(propId); 67 if (iterId == entryData.end()) 68 { 69 log<level::ERR>("Error in reading Id of logging entry"); 70 elog<InternalFailure>(); 71 } 72 73 record.recordID = static_cast<uint16_t>( 74 sdbusplus::message::variant_ns::get<uint32_t>(iterId->second)); 75 76 // Read Timestamp from the log entry. 77 static constexpr auto propTimeStamp = "Timestamp"; 78 auto iterTimeStamp = entryData.find(propTimeStamp); 79 if (iterTimeStamp == entryData.end()) 80 { 81 log<level::ERR>("Error in reading Timestamp of logging entry"); 82 elog<InternalFailure>(); 83 } 84 85 std::chrono::milliseconds chronoTimeStamp( 86 sdbusplus::message::variant_ns::get<uint64_t>(iterTimeStamp->second)); 87 record.timeStamp = static_cast<uint32_t>( 88 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp) 89 .count()); 90 91 static constexpr auto systemEventRecord = 0x02; 92 static constexpr auto generatorID = 0x2000; 93 static constexpr auto eventMsgRevision = 0x04; 94 95 record.recordType = systemEventRecord; 96 record.generatorID = generatorID; 97 record.eventMsgRevision = eventMsgRevision; 98 99 record.sensorType = iter->second.sensorType; 100 record.sensorNum = iter->second.sensorID; 101 record.eventData1 = iter->second.eventOffset; 102 103 // Read Resolved from the log entry. 104 static constexpr auto propResolved = "Resolved"; 105 auto iterResolved = entryData.find(propResolved); 106 if (iterResolved == entryData.end()) 107 { 108 log<level::ERR>("Error in reading Resolved field of logging entry"); 109 elog<InternalFailure>(); 110 } 111 112 static constexpr auto deassertEvent = 0x80; 113 114 // Evaluate if the event is assertion or deassertion event 115 if (sdbusplus::message::variant_ns::get<bool>(iterResolved->second)) 116 { 117 record.eventType = deassertEvent | iter->second.eventReadingType; 118 } 119 else 120 { 121 record.eventType = iter->second.eventReadingType; 122 } 123 124 return record; 125 } 126 127 } // namespace internal 128 129 GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath) 130 { 131 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 132 133 static constexpr auto assocIntf = "org.openbmc.Associations"; 134 static constexpr auto assocProp = "associations"; 135 136 auto service = ipmi::getService(bus, assocIntf, objPath); 137 138 // Read the Associations interface. 139 auto methodCall = 140 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); 141 methodCall.append(assocIntf); 142 methodCall.append(assocProp); 143 144 auto reply = bus.call(methodCall); 145 if (reply.is_method_error()) 146 { 147 log<level::ERR>("Error in reading Associations interface"); 148 elog<InternalFailure>(); 149 } 150 151 using AssociationList = 152 std::vector<std::tuple<std::string, std::string, std::string>>; 153 154 sdbusplus::message::variant<AssociationList> list; 155 reply.read(list); 156 157 auto& assocs = sdbusplus::message::variant_ns::get<AssociationList>(list); 158 159 /* 160 * Check if the log entry has any callout associations, if there is a 161 * callout association try to match the inventory path to the corresponding 162 * IPMI sensor. 163 */ 164 for (const auto& item : assocs) 165 { 166 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0) 167 { 168 auto iter = invSensors.find(std::get<2>(item)); 169 if (iter == invSensors.end()) 170 { 171 iter = invSensors.find(BOARD_SENSOR); 172 if (iter == invSensors.end()) 173 { 174 log<level::ERR>("Motherboard sensor not found"); 175 elog<InternalFailure>(); 176 } 177 } 178 179 return internal::prepareSELEntry(objPath, iter); 180 } 181 } 182 183 // If there are no callout associations link the log entry to system event 184 // sensor 185 auto iter = invSensors.find(SYSTEM_SENSOR); 186 if (iter == invSensors.end()) 187 { 188 log<level::ERR>("System event sensor not found"); 189 elog<InternalFailure>(); 190 } 191 192 return internal::prepareSELEntry(objPath, iter); 193 } 194 195 std::chrono::seconds getEntryTimeStamp(const std::string& objPath) 196 { 197 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 198 199 auto service = ipmi::getService(bus, logEntryIntf, objPath); 200 201 using namespace std::string_literals; 202 static const auto propTimeStamp = "Timestamp"s; 203 204 auto methodCall = 205 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); 206 methodCall.append(logEntryIntf); 207 methodCall.append(propTimeStamp); 208 209 auto reply = bus.call(methodCall); 210 if (reply.is_method_error()) 211 { 212 log<level::ERR>("Error in reading Timestamp from Entry interface"); 213 elog<InternalFailure>(); 214 } 215 216 sdbusplus::message::variant<uint64_t> timeStamp; 217 reply.read(timeStamp); 218 219 std::chrono::milliseconds chronoTimeStamp( 220 sdbusplus::message::variant_ns::get<uint64_t>(timeStamp)); 221 222 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp); 223 } 224 225 void readLoggingObjectPaths(ObjectPaths& paths) 226 { 227 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 228 auto depth = 0; 229 paths.clear(); 230 231 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 232 mapperIntf, "GetSubTreePaths"); 233 mapperCall.append(logBasePath); 234 mapperCall.append(depth); 235 mapperCall.append(ObjectPaths({logEntryIntf})); 236 237 auto reply = bus.call(mapperCall); 238 if (reply.is_method_error()) 239 { 240 log<level::INFO>("Error in reading logging entry object paths"); 241 } 242 else 243 { 244 reply.read(paths); 245 246 std::sort(paths.begin(), paths.end(), 247 [](const std::string& a, const std::string& b) { 248 namespace fs = std::filesystem; 249 fs::path pathA(a); 250 fs::path pathB(b); 251 auto idA = std::stoul(pathA.filename().string()); 252 auto idB = std::stoul(pathB.filename().string()); 253 254 return idA < idB; 255 }); 256 } 257 } 258 259 } // namespace sel 260 261 } // namespace ipmi 262