blob: 2e6a07e2421ed2a3cd07f53c714f1fdd75b1b19d [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
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 */
Matt Spinler4e8078c2019-07-09 13:22:32 -050016#include "manager.hpp"
17
18#include "additional_data.hpp"
Matt Spinler05c2c6c2019-12-18 14:02:09 -060019#include "json_utils.hpp"
Matt Spinler89fa0822019-07-17 13:54:30 -050020#include "pel.hpp"
21
Matt Spinler6b1a5c82020-01-07 08:48:53 -060022#include <unistd.h>
23
Matt Spinler89fa0822019-07-17 13:54:30 -050024#include <filesystem>
25#include <fstream>
Matt Spinlera34ab722019-12-16 10:39:32 -060026#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler4e8078c2019-07-09 13:22:32 -050027
28namespace openpower
29{
30namespace pels
31{
32
33using namespace phosphor::logging;
Matt Spinler89fa0822019-07-17 13:54:30 -050034namespace fs = std::filesystem;
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +080035namespace rg = openpower::pels::message;
Matt Spinler4e8078c2019-07-09 13:22:32 -050036
Matt Spinlera34ab722019-12-16 10:39:32 -060037namespace common_error = sdbusplus::xyz::openbmc_project::Common::Error;
38
Matt Spinler4e8078c2019-07-09 13:22:32 -050039namespace additional_data
40{
41constexpr auto rawPEL = "RAWPEL";
Matt Spinler19e72902020-01-24 11:05:20 -060042constexpr auto esel = "ESEL";
43} // namespace additional_data
Matt Spinler4e8078c2019-07-09 13:22:32 -050044
45void 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
Matt Spinler19e72902020-01-24 11:05:20 -060052 // If a PEL was passed in via a filename or in an ESEL,
53 // use that. Otherwise, create one.
Matt Spinler4e8078c2019-07-09 13:22:32 -050054 auto rawPelPath = ad.getValue(additional_data::rawPEL);
55 if (rawPelPath)
56 {
57 addRawPEL(*rawPelPath, obmcLogID);
58 }
59 else
60 {
Matt Spinler19e72902020-01-24 11:05:20 -060061 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 }
Matt Spinler4e8078c2019-07-09 13:22:32 -050071 }
72}
73
74void Manager::addRawPEL(const std::string& rawPelPath, uint32_t obmcLogID)
75{
Matt Spinler89fa0822019-07-17 13:54:30 -050076 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
Matt Spinler19e72902020-01-24 11:05:20 -060093 addPEL(data, obmcLogID);
Matt Spinler89fa0822019-07-17 13:54:30 -050094 }
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 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500101}
102
Matt Spinler19e72902020-01-24 11:05:20 -0600103void 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 {
Matt Spinler5f5352e2020-03-05 16:23:27 -0600115 log<level::DEBUG>("Adding external PEL to repo",
116 entry("PEL_ID=0x%X", pel->id()));
117
Matt Spinler19e72902020-01-24 11:05:20 -0600118 _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
151void Manager::addESELPEL(const std::string& esel, uint32_t obmcLogID)
152{
153 std::vector<uint8_t> data;
154
Matt Spinler5f5352e2020-03-05 16:23:27 -0600155 log<level::DEBUG>("Adding PEL from ESEL",
156 entry("OBMC_LOG_ID=%d", obmcLogID));
157
Matt Spinler19e72902020-01-24 11:05:20 -0600158 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
171std::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
Matt Spinler4e8078c2019-07-09 13:22:32 -0500208void Manager::erase(uint32_t obmcLogID)
209{
Matt Spinler475e5742019-07-18 16:09:49 -0500210 Repository::LogID id{Repository::LogID::Obmc(obmcLogID)};
211
212 _repo.remove(id);
Matt Spinler4e8078c2019-07-09 13:22:32 -0500213}
214
215bool Manager::isDeleteProhibited(uint32_t obmcLogID)
216{
217 return false;
218}
219
220void 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{
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800226 auto entry = _registry.lookup(message, rg::LookupType::name);
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600227 std::string msg;
Matt Spinler67456c22019-10-21 12:22:49 -0500228
229 if (entry)
230 {
231 AdditionalData ad{additionalData};
232
Matt Spinlera34ab722019-12-16 10:39:32 -0600233 auto pel = std::make_unique<openpower::pels::PEL>(
234 *entry, obmcLogID, timestamp, severity, ad, *_dataIface);
Matt Spinler67456c22019-10-21 12:22:49 -0500235
236 _repo.add(pel);
Matt Spinler67456c22019-10-21 12:22:49 -0500237
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600238 auto src = pel->primarySRC();
239 if (src)
240 {
241 using namespace std::literals::string_literals;
Matt Spinler19e72902020-01-24 11:05:20 -0600242 auto id = getNumberString("0x%08X", pel->id());
Matt Spinler1d4c74a2019-12-16 14:40:21 -0600243 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 }
Matt Spinler4e8078c2019-07-09 13:22:32 -0500258}
259
Matt Spinlera34ab722019-12-16 10:39:32 -0600260sdbusplus::message::unix_fd Manager::getPEL(uint32_t pelID)
261{
262 Repository::LogID id{Repository::LogID::Pel(pelID)};
263 std::optional<int> fd;
264
Matt Spinler5f5352e2020-03-05 16:23:27 -0600265 log<level::DEBUG>("getPEL", entry("PEL_ID=0x%X", pelID));
266
Matt Spinlera34ab722019-12-16 10:39:32 -0600267 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
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600281 scheduleFDClose(*fd);
282
Matt Spinlera34ab722019-12-16 10:39:32 -0600283 return *fd;
284}
285
Matt Spinler6b1a5c82020-01-07 08:48:53 -0600286void 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
294void Manager::closeFD(int fd, sdeventplus::source::EventBase& source)
295{
296 close(fd);
297 _fdCloserEventSource.reset();
298}
299
Matt Spinlera34ab722019-12-16 10:39:32 -0600300std::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
Matt Spinler5f5352e2020-03-05 16:23:27 -0600305 log<level::DEBUG>("getPELFromOBMCID", entry("OBMC_LOG_ID=%d", obmcLogID));
306
Matt Spinlera34ab722019-12-16 10:39:32 -0600307 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
324void Manager::hostAck(uint32_t pelID)
325{
326 Repository::LogID id{Repository::LogID::Pel(pelID)};
327
Matt Spinler5f5352e2020-03-05 16:23:27 -0600328 log<level::DEBUG>("HostAck", entry("PEL_ID=0x%X", pelID));
329
Matt Spinlera34ab722019-12-16 10:39:32 -0600330 if (!_repo.hasPEL(id))
331 {
332 throw common_error::InvalidArgument();
333 }
334
335 if (_hostNotifier)
336 {
337 _hostNotifier->ackPEL(pelID);
338 }
339}
340
341void Manager::hostReject(uint32_t pelID, RejectionReason reason)
342{
343 Repository::LogID id{Repository::LogID::Pel(pelID)};
344
Matt Spinler5f5352e2020-03-05 16:23:27 -0600345 log<level::DEBUG>("HostReject", entry("PEL_ID=0x%X", pelID),
346 entry("REASON=%d", static_cast<int>(reason)));
347
Matt Spinlera34ab722019-12-16 10:39:32 -0600348 if (!_repo.hasPEL(id))
349 {
350 throw common_error::InvalidArgument();
351 }
352
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600353 if (reason == RejectionReason::BadPEL)
Matt Spinlera34ab722019-12-16 10:39:32 -0600354 {
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600355 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)
Matt Spinlera34ab722019-12-16 10:39:32 -0600360 {
361 _hostNotifier->setBadPEL(pelID);
362 }
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600363 }
364 else if ((reason == RejectionReason::HostFull) && _hostNotifier)
365 {
366 _hostNotifier->setHostFull(pelID);
Matt Spinlera34ab722019-12-16 10:39:32 -0600367 }
368}
369
Matt Spinler4e8078c2019-07-09 13:22:32 -0500370} // namespace pels
371} // namespace openpower