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>( 62 sdbusplus::message::variant_ns::get<uint32_t>(iterId->second)); 63 64 // Read Timestamp from the log entry. 65 static constexpr auto propTimeStamp = "Timestamp"; 66 auto iterTimeStamp = entryData.find(propTimeStamp); 67 if (iterTimeStamp == entryData.end()) 68 { 69 log<level::ERR>("Error in reading Timestamp of logging entry"); 70 elog<InternalFailure>(); 71 } 72 73 std::chrono::milliseconds chronoTimeStamp( 74 sdbusplus::message::variant_ns::get<uint64_t>(iterTimeStamp->second)); 75 record.timeStamp = static_cast<uint32_t>( 76 std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp) 77 .count()); 78 79 static constexpr auto systemEventRecord = 0x02; 80 static constexpr auto generatorID = 0x2000; 81 static constexpr auto eventMsgRevision = 0x04; 82 83 record.recordType = systemEventRecord; 84 record.generatorID = generatorID; 85 record.eventMsgRevision = eventMsgRevision; 86 87 record.sensorType = iter->second.sensorType; 88 record.sensorNum = iter->second.sensorID; 89 record.eventData1 = iter->second.eventOffset; 90 91 // Read Resolved from the log entry. 92 static constexpr auto propResolved = "Resolved"; 93 auto iterResolved = entryData.find(propResolved); 94 if (iterResolved == entryData.end()) 95 { 96 log<level::ERR>("Error in reading Resolved field of logging entry"); 97 elog<InternalFailure>(); 98 } 99 100 static constexpr auto deassertEvent = 0x80; 101 102 // Evaluate if the event is assertion or deassertion event 103 if (sdbusplus::message::variant_ns::get<bool>(iterResolved->second)) 104 { 105 record.eventType = deassertEvent | iter->second.eventReadingType; 106 } 107 else 108 { 109 record.eventType = iter->second.eventReadingType; 110 } 111 112 return record; 113 } 114 115 } // namespace internal 116 117 GetSELEntryResponse convertLogEntrytoSEL(const std::string& objPath) 118 { 119 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 120 121 static constexpr auto assocIntf = "org.openbmc.Associations"; 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 sdbusplus::message::variant<AssociationList> list; 143 reply.read(list); 144 145 auto& assocs = sdbusplus::message::variant_ns::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 sdbusplus::message::variant<uint64_t> timeStamp; 205 reply.read(timeStamp); 206 207 std::chrono::milliseconds chronoTimeStamp( 208 sdbusplus::message::variant_ns::get<uint64_t>(timeStamp)); 209 210 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp); 211 } 212 213 void readLoggingObjectPaths(ObjectPaths& paths) 214 { 215 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 216 auto depth = 0; 217 paths.clear(); 218 219 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 220 mapperIntf, "GetSubTreePaths"); 221 mapperCall.append(logBasePath); 222 mapperCall.append(depth); 223 mapperCall.append(ObjectPaths({logEntryIntf})); 224 225 auto reply = bus.call(mapperCall); 226 if (reply.is_method_error()) 227 { 228 log<level::INFO>("Error in reading logging entry object paths"); 229 } 230 else 231 { 232 reply.read(paths); 233 234 std::sort(paths.begin(), paths.end(), 235 [](const std::string& a, const std::string& b) { 236 namespace fs = std::filesystem; 237 fs::path pathA(a); 238 fs::path pathB(b); 239 auto idA = std::stoul(pathA.filename().string()); 240 auto idB = std::stoul(pathB.filename().string()); 241 242 return idA < idB; 243 }); 244 } 245 } 246 247 } // namespace sel 248 249 } // namespace ipmi 250