1 /** 2 * Copyright © 2019 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "manager.hpp" 17 18 #include "additional_data.hpp" 19 #include "json_utils.hpp" 20 #include "pel.hpp" 21 22 #include <unistd.h> 23 24 #include <filesystem> 25 #include <fstream> 26 #include <xyz/openbmc_project/Common/error.hpp> 27 28 namespace openpower 29 { 30 namespace pels 31 { 32 33 using namespace phosphor::logging; 34 namespace fs = std::filesystem; 35 namespace rg = openpower::pels::message; 36 37 namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error; 38 39 namespace additional_data 40 { 41 constexpr auto rawPEL = "RAWPEL"; 42 constexpr auto esel = "ESEL"; 43 } // namespace additional_data 44 45 void Manager::create(const std::string& message, uint32_t obmcLogID, 46 uint64_t timestamp, Entry::Level severity, 47 const std::vector<std::string>& additionalData, 48 const std::vector<std::string>& associations) 49 { 50 AdditionalData ad{additionalData}; 51 52 // If a PEL was passed in via a filename or in an ESEL, 53 // use that. Otherwise, create one. 54 auto rawPelPath = ad.getValue(additional_data::rawPEL); 55 if (rawPelPath) 56 { 57 addRawPEL(*rawPelPath, obmcLogID); 58 } 59 else 60 { 61 auto esel = ad.getValue(additional_data::esel); 62 if (esel) 63 { 64 addESELPEL(*esel, obmcLogID); 65 } 66 else 67 { 68 createPEL(message, obmcLogID, timestamp, severity, additionalData, 69 associations); 70 } 71 } 72 } 73 74 void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID) 75 { 76 if (fs::exists(rawPelPath)) 77 { 78 std::ifstream file(rawPelPath, std::ios::in | std::ios::binary); 79 80 auto data = std::vector<uint8_t>(std::istreambuf_iterator<char>(file), 81 std::istreambuf_iterator<char>()); 82 if (file.fail()) 83 { 84 log<level::ERR>("Filesystem error reading a raw PEL", 85 entry("PELFILE=%s", rawPelPath.c_str()), 86 entry("OBMCLOGID=%d", obmcLogID)); 87 // TODO, Decide what to do here. Maybe nothing. 88 return; 89 } 90 91 file.close(); 92 93 addPEL(data, obmcLogID); 94 } 95 else 96 { 97 log<level::ERR>("Raw PEL file from BMC event log does not exist", 98 entry("PELFILE=%s", (rawPelPath).c_str()), 99 entry("OBMCLOGID=%d", obmcLogID)); 100 } 101 } 102 103 void Manager::addPEL(std::vector<uint8_t>& pelData, uint32_t obmcLogID) 104 { 105 106 auto pel = std::make_unique<openpower::pels::PEL>(pelData, obmcLogID); 107 if (pel->valid()) 108 { 109 // PELs created by others still need these fields set by us. 110 pel->assignID(); 111 pel->setCommitTime(); 112 113 try 114 { 115 log<level::DEBUG>("Adding external PEL to repo", 116 entry("PEL_ID=0x%X", pel->id())); 117 118 _repo.add(pel); 119 } 120 catch (std::exception& e) 121 { 122 // Probably a full or r/o filesystem, not much we can do. 123 log<level::ERR>("Unable to add PEL to Repository", 124 entry("PEL_ID=0x%X", pel->id())); 125 } 126 } 127 else 128 { 129 log<level::ERR>("Invalid PEL received from the host", 130 entry("OBMCLOGID=%d", obmcLogID)); 131 132 AdditionalData ad; 133 ad.add("PLID", getNumberString("0x%08X", pel->plid())); 134 ad.add("OBMC_LOG_ID", std::to_string(obmcLogID)); 135 ad.add("PEL_SIZE", std::to_string(pelData.size())); 136 137 std::string asciiString; 138 auto src = pel->primarySRC(); 139 if (src) 140 { 141 asciiString = (*src)->asciiString(); 142 } 143 144 ad.add("SRC", asciiString); 145 146 _eventLogger.log("org.open_power.Logging.Error.BadHostPEL", 147 Entry::Level::Error, ad); 148 } 149 } 150 151 void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID) 152 { 153 std::vector<uint8_t> data; 154 155 log<level::DEBUG>("Adding PEL from ESEL", 156 entry("OBMC_LOG_ID=%d", obmcLogID)); 157 158 try 159 { 160 data = std::move(eselToRawData(esel)); 161 } 162 catch (std::exception& e) 163 { 164 // Try to add it below anyway, so it follows the usual bad data path. 165 log<level::ERR>("Problems converting ESEL string to a byte vector"); 166 } 167 168 addPEL(data, obmcLogID); 169 } 170 171 std::vector<uint8_t> Manager::eselToRawData(const std::string& esel) 172 { 173 std::vector<uint8_t> data; 174 std::string byteString; 175 176 // As the eSEL string looks like: "50 48 00 ab ..." there are 3 177 // characters per raw byte, and since the actual PEL data starts 178 // at the 16th byte, the code will grab the PEL data starting at 179 // offset 48 in the string. 180 static constexpr size_t pelStart = 16 * 3; 181 182 if (esel.size() <= pelStart) 183 { 184 log<level::ERR>("ESEL data too short", 185 entry("ESEL_SIZE=%d", esel.size())); 186 187 throw std::length_error("ESEL data too short"); 188 } 189 190 for (size_t i = pelStart; i < esel.size(); i += 3) 191 { 192 if (i + 1 < esel.size()) 193 { 194 byteString = esel.substr(i, 2); 195 data.push_back(std::stoi(byteString, nullptr, 16)); 196 } 197 else 198 { 199 log<level::ERR>("ESEL data too short", 200 entry("ESEL_SIZE=%d", esel.size())); 201 throw std::length_error("ESEL data too short"); 202 } 203 } 204 205 return data; 206 } 207 208 void Manager::erase(uint32_t obmcLogID) 209 { 210 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 211 212 _repo.remove(id); 213 } 214 215 bool Manager::isDeleteProhibited(uint32_t obmcLogID) 216 { 217 return false; 218 } 219 220 void Manager::createPEL(const std::string& message, uint32_t obmcLogID, 221 uint64_t timestamp, 222 phosphor::logging::Entry::Level severity, 223 const std::vector<std::string>& additionalData, 224 const std::vector<std::string>& associations) 225 { 226 auto entry = _registry.lookup(message, rg::LookupType::name); 227 std::string msg; 228 229 if (entry) 230 { 231 AdditionalData ad{additionalData}; 232 233 auto pel = std::make_unique<openpower::pels::PEL>( 234 *entry, obmcLogID, timestamp, severity, ad, *_dataIface); 235 236 _repo.add(pel); 237 238 auto src = pel->primarySRC(); 239 if (src) 240 { 241 using namespace std::literals::string_literals; 242 auto id = getNumberString("0x%08X", pel->id()); 243 msg = "Created PEL "s + id + " with SRC "s + (*src)->asciiString(); 244 while (msg.back() == ' ') 245 { 246 msg.pop_back(); 247 } 248 log<level::INFO>(msg.c_str()); 249 } 250 } 251 else 252 { 253 // TODO ibm-openbmc/dev/1151: Create a new PEL for this case. 254 // For now, just trace it. 255 msg = "Event not found in PEL message registry: " + message; 256 log<level::INFO>(msg.c_str()); 257 } 258 } 259 260 sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID) 261 { 262 Repository::LogID id{Repository::LogID::Pel(pelID)}; 263 std::optional<int> fd; 264 265 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID)); 266 267 try 268 { 269 fd = _repo.getPELFD(id); 270 } 271 catch (std::exception& e) 272 { 273 throw common_error::InternalFailure(); 274 } 275 276 if (!fd) 277 { 278 throw common_error::InvalidArgument(); 279 } 280 281 scheduleFDClose(*fd); 282 283 return *fd; 284 } 285 286 void Manager::scheduleFDClose(int fd) 287 { 288 _fdCloserEventSource = std::make_unique<sdeventplus::source::Defer>( 289 _logManager.getBus().get_event(), 290 std::bind(std::mem_fn(&Manager::closeFD), this, fd, 291 std::placeholders::_1)); 292 } 293 294 void Manager::closeFD(int fd, sdeventplus::source::EventBase& source) 295 { 296 close(fd); 297 _fdCloserEventSource.reset(); 298 } 299 300 std::vector<uint8_t> Manager::getPELFromOBMCID(uint32_t obmcLogID) 301 { 302 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)}; 303 std::optional<std::vector<uint8_t>> data; 304 305 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID)); 306 307 try 308 { 309 data = _repo.getPELData(id); 310 } 311 catch (std::exception& e) 312 { 313 throw common_error::InternalFailure(); 314 } 315 316 if (!data) 317 { 318 throw common_error::InvalidArgument(); 319 } 320 321 return *data; 322 } 323 324 void Manager::hostAck(uint32_t pelID) 325 { 326 Repository::LogID id{Repository::LogID::Pel(pelID)}; 327 328 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID)); 329 330 if (!_repo.hasPEL(id)) 331 { 332 throw common_error::InvalidArgument(); 333 } 334 335 if (_hostNotifier) 336 { 337 _hostNotifier->ackPEL(pelID); 338 } 339 } 340 341 void Manager::hostReject(uint32_t pelID, RejectionReason reason) 342 { 343 Repository::LogID id{Repository::LogID::Pel(pelID)}; 344 345 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID), 346 entry("REASON=%d", static_cast<int>(reason))); 347 348 if (!_repo.hasPEL(id)) 349 { 350 throw common_error::InvalidArgument(); 351 } 352 353 if (reason == RejectionReason::BadPEL) 354 { 355 AdditionalData data; 356 data.add("BAD_ID", getNumberString("0x%08X", pelID)); 357 _eventLogger.log("org.open_power.Logging.Error.SentBadPELToHost", 358 Entry::Level::Informational, data); 359 if (_hostNotifier) 360 { 361 _hostNotifier->setBadPEL(pelID); 362 } 363 } 364 else if ((reason == RejectionReason::HostFull) && _hostNotifier) 365 { 366 _hostNotifier->setHostFull(pelID); 367 } 368 } 369 370 } // namespace pels 371 } // namespace openpower 372