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 = "org.openbmc.Associations"; 121 static constexpr auto assocProp = "associations"; 122 123 auto service = ipmi::getService(bus, assocIntf, objPath); 124 125 // Read the Associations interface. 126 auto methodCall = 127 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); 128 methodCall.append(assocIntf); 129 methodCall.append(assocProp); 130 131 auto reply = bus.call(methodCall); 132 if (reply.is_method_error()) 133 { 134 log<level::ERR>("Error in reading Associations interface"); 135 elog<InternalFailure>(); 136 } 137 138 using AssociationList = 139 std::vector<std::tuple<std::string, std::string, std::string>>; 140 141 sdbusplus::message::variant<AssociationList> list; 142 reply.read(list); 143 144 auto& assocs = std::get<AssociationList>(list); 145 146 /* 147 * Check if the log entry has any callout associations, if there is a 148 * callout association try to match the inventory path to the corresponding 149 * IPMI sensor. 150 */ 151 for (const auto& item : assocs) 152 { 153 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0) 154 { 155 auto iter = invSensors.find(std::get<2>(item)); 156 if (iter == invSensors.end()) 157 { 158 iter = invSensors.find(BOARD_SENSOR); 159 if (iter == invSensors.end()) 160 { 161 log<level::ERR>("Motherboard sensor not found"); 162 elog<InternalFailure>(); 163 } 164 } 165 166 return internal::prepareSELEntry(objPath, iter); 167 } 168 } 169 170 // If there are no callout associations link the log entry to system event 171 // sensor 172 auto iter = invSensors.find(SYSTEM_SENSOR); 173 if (iter == invSensors.end()) 174 { 175 log<level::ERR>("System event sensor not found"); 176 elog<InternalFailure>(); 177 } 178 179 return internal::prepareSELEntry(objPath, iter); 180 } 181 182 std::chrono::seconds getEntryTimeStamp(const std::string& objPath) 183 { 184 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 185 186 auto service = ipmi::getService(bus, logEntryIntf, objPath); 187 188 using namespace std::string_literals; 189 static const auto propTimeStamp = "Timestamp"s; 190 191 auto methodCall = 192 bus.new_method_call(service.c_str(), objPath.c_str(), propIntf, "Get"); 193 methodCall.append(logEntryIntf); 194 methodCall.append(propTimeStamp); 195 196 auto reply = bus.call(methodCall); 197 if (reply.is_method_error()) 198 { 199 log<level::ERR>("Error in reading Timestamp from Entry interface"); 200 elog<InternalFailure>(); 201 } 202 203 sdbusplus::message::variant<uint64_t> timeStamp; 204 reply.read(timeStamp); 205 206 std::chrono::milliseconds chronoTimeStamp(std::get<uint64_t>(timeStamp)); 207 208 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp); 209 } 210 211 void readLoggingObjectPaths(ObjectPaths& paths) 212 { 213 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 214 auto depth = 0; 215 paths.clear(); 216 217 auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath, 218 mapperIntf, "GetSubTreePaths"); 219 mapperCall.append(logBasePath); 220 mapperCall.append(depth); 221 mapperCall.append(ObjectPaths({logEntryIntf})); 222 223 auto reply = bus.call(mapperCall); 224 if (reply.is_method_error()) 225 { 226 log<level::INFO>("Error in reading logging entry object paths"); 227 } 228 else 229 { 230 reply.read(paths); 231 232 std::sort(paths.begin(), paths.end(), 233 [](const std::string& a, const std::string& b) { 234 namespace fs = std::filesystem; 235 fs::path pathA(a); 236 fs::path pathB(b); 237 auto idA = std::stoul(pathA.filename().string()); 238 auto idB = std::stoul(pathB.filename().string()); 239 240 return idA < idB; 241 }); 242 } 243 } 244 245 } // namespace sel 246 247 } // namespace ipmi 248