1 #include <chrono> 2 #include <vector> 3 #include <experimental/filesystem> 4 #include <phosphor-logging/elog-errors.hpp> 5 #include "host-ipmid/ipmid-api.h" 6 #include "xyz/openbmc_project/Common/error.hpp" 7 #include "config.h" 8 #include "selutility.hpp" 9 #include "types.hpp" 10 #include "utils.hpp" 11 12 extern const ipmi::sensor::InvObjectIDMap invSensors; 13 using namespace phosphor::logging; 14 using InternalFailure = 15 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 16 17 namespace ipmi 18 { 19 20 namespace sel 21 { 22 23 namespace internal 24 { 25 26 GetSELEntryResponse prepareSELEntry( 27 const std::string& objPath, 28 ipmi::sensor::InvObjectIDMap::const_iterator iter) 29 { 30 GetSELEntryResponse record {}; 31 32 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 33 auto service = ipmi::getService(bus, logEntryIntf, objPath); 34 35 // Read all the log entry properties. 36 auto methodCall = bus.new_method_call(service.c_str(), 37 objPath.c_str(), 38 propIntf, 39 "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> 75 (iterTimeStamp->second)); 76 record.timeStamp = static_cast<uint32_t>(std::chrono::duration_cast< 77 std::chrono::seconds>(chronoTimeStamp).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 = bus.new_method_call(service.c_str(), 128 objPath.c_str(), 129 propIntf, 130 "Get"); 131 methodCall.append(assocIntf); 132 methodCall.append(assocProp); 133 134 auto reply = bus.call(methodCall); 135 if (reply.is_method_error()) 136 { 137 log<level::ERR>("Error in reading Associations interface"); 138 elog<InternalFailure>(); 139 } 140 141 using AssociationList = std::vector<std::tuple< 142 std::string, std::string, std::string>>; 143 144 sdbusplus::message::variant<AssociationList> list; 145 reply.read(list); 146 147 auto& assocs = sdbusplus::message::variant_ns::get<AssociationList> 148 (list); 149 150 /* 151 * Check if the log entry has any callout associations, if there is a 152 * callout association try to match the inventory path to the corresponding 153 * IPMI sensor. 154 */ 155 for (const auto& item : assocs) 156 { 157 if (std::get<0>(item).compare(CALLOUT_FWD_ASSOCIATION) == 0) 158 { 159 auto iter = invSensors.find(std::get<2>(item)); 160 if (iter == invSensors.end()) 161 { 162 iter = invSensors.find(BOARD_SENSOR); 163 if (iter == invSensors.end()) 164 { 165 log<level::ERR>("Motherboard sensor not found"); 166 elog<InternalFailure>(); 167 } 168 } 169 170 return internal::prepareSELEntry(objPath, iter); 171 } 172 } 173 174 // If there are no callout associations link the log entry to system event 175 // sensor 176 auto iter = invSensors.find(SYSTEM_SENSOR); 177 if (iter == invSensors.end()) 178 { 179 log<level::ERR>("System event sensor not found"); 180 elog<InternalFailure>(); 181 } 182 183 return internal::prepareSELEntry(objPath, iter); 184 } 185 186 std::chrono::seconds getEntryTimeStamp(const std::string& objPath) 187 { 188 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 189 190 auto service = ipmi::getService(bus, logEntryIntf, objPath); 191 192 using namespace std::string_literals; 193 static const auto propTimeStamp = "Timestamp"s; 194 195 auto methodCall = bus.new_method_call(service.c_str(), 196 objPath.c_str(), 197 propIntf, 198 "Get"); 199 methodCall.append(logEntryIntf); 200 methodCall.append(propTimeStamp); 201 202 auto reply = bus.call(methodCall); 203 if (reply.is_method_error()) 204 { 205 log<level::ERR>("Error in reading Timestamp from Entry interface"); 206 elog<InternalFailure>(); 207 } 208 209 sdbusplus::message::variant<uint64_t> timeStamp; 210 reply.read(timeStamp); 211 212 std::chrono::milliseconds chronoTimeStamp( 213 sdbusplus::message::variant_ns::get<uint64_t>(timeStamp)); 214 215 return std::chrono::duration_cast<std::chrono::seconds>(chronoTimeStamp); 216 } 217 218 void readLoggingObjectPaths(ObjectPaths& paths) 219 { 220 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 221 auto depth = 0; 222 paths.clear(); 223 224 auto mapperCall = bus.new_method_call(mapperBusName, 225 mapperObjPath, 226 mapperIntf, 227 "GetSubTreePaths"); 228 mapperCall.append(logBasePath); 229 mapperCall.append(depth); 230 mapperCall.append(ObjectPaths({logEntryIntf})); 231 232 auto reply = bus.call(mapperCall); 233 if (reply.is_method_error()) 234 { 235 log<level::INFO>("Error in reading logging entry object paths"); 236 } 237 else 238 { 239 reply.read(paths); 240 241 std::sort(paths.begin(), paths.end(), [](const std::string& a, 242 const std::string& b) 243 { 244 namespace fs = std::experimental::filesystem; 245 fs::path pathA(a); 246 fs::path pathB(b); 247 auto idA = std::stoul(pathA.filename().string()); 248 auto idB = std::stoul(pathB.filename().string()); 249 250 return idA < idB; 251 }); 252 } 253 } 254 255 } // namespace sel 256 257 } // namespace ipmi 258