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