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