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