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