blob: b93e58cdff428b091a55a20f0990e964f6b7177b [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Matt Spinler89fa0822019-07-17 13:54:30 -05004#include "extensions/openpower-pels/manager.hpp"
5#include "log_manager.hpp"
Matt Spinlere6b48f12020-04-02 09:51:39 -05006#include "mocks.hpp"
Matt Spinler89fa0822019-07-17 13:54:30 -05007#include "pel_utils.hpp"
8
Matt Spinlere6b48f12020-04-02 09:51:39 -05009#include <sdbusplus/test/sdbus_mock.hpp>
Matt Spinlera34ab722019-12-16 10:39:32 -060010#include <xyz/openbmc_project/Common/error.hpp>
Matt Spinler89fa0822019-07-17 13:54:30 -050011
Patrick Williams2544b412022-10-04 08:41:06 -050012#include <fstream>
13#include <regex>
14
Matt Spinler89fa0822019-07-17 13:54:30 -050015#include <gtest/gtest.h>
16
17using namespace openpower::pels;
18namespace fs = std::filesystem;
19
Matt Spinlere6b48f12020-04-02 09:51:39 -050020using ::testing::NiceMock;
Matt Spinler3dd17e92020-08-05 15:04:27 -050021using ::testing::Return;
harsh-agarwal1d763db32024-09-03 09:18:50 -050022using json = nlohmann::json;
Matt Spinlere6b48f12020-04-02 09:51:39 -050023
Matt Spinler4bec95d2025-11-17 13:19:04 -060024using Level = phosphor::logging::Entry::Level;
25
Matt Spinler05c2c6c2019-12-18 14:02:09 -060026class TestLogger
27{
28 public:
Matt Spinler4bec95d2025-11-17 13:19:04 -060029 void log(const std::string& name, Level level,
Matt Spinler05c2c6c2019-12-18 14:02:09 -060030 const EventLogger::ADMap& additionalData)
31 {
32 errName = name;
33 errLevel = level;
34 ad = additionalData;
35 }
36
37 std::string errName;
Matt Spinler4bec95d2025-11-17 13:19:04 -060038 Level errLevel;
Matt Spinler05c2c6c2019-12-18 14:02:09 -060039 EventLogger::ADMap ad;
40};
41
Matt Spinler89fa0822019-07-17 13:54:30 -050042class ManagerTest : public CleanPELFiles
43{
Matt Spinler6b1a5c82020-01-07 08:48:53 -060044 public:
Matt Spinlere6b48f12020-04-02 09:51:39 -050045 ManagerTest() :
46 bus(sdbusplus::get_mocked_new(&sdbusInterface)),
47 logManager(bus, "logging_path")
Matt Spinler6b1a5c82020-01-07 08:48:53 -060048 {
49 sd_event_default(&sdEvent);
Matt Spinler6b1a5c82020-01-07 08:48:53 -060050 }
51
Matt Spinler32e36b82023-04-25 10:57:15 -050052 fs::path makeTempDir()
53 {
54 char path[] = "/tmp/tempnameXXXXXX";
55 std::filesystem::path dir = mkdtemp(path);
56 dirsToRemove.push_back(dir);
57 return dir;
58 }
59
Matt Spinler6b1a5c82020-01-07 08:48:53 -060060 ~ManagerTest()
61 {
Matt Spinler32e36b82023-04-25 10:57:15 -050062 for (const auto& d : dirsToRemove)
63 {
64 std::filesystem::remove_all(d);
65 }
Matt Spinler6b1a5c82020-01-07 08:48:53 -060066 sd_event_unref(sdEvent);
67 }
68
Matt Spinlere6b48f12020-04-02 09:51:39 -050069 NiceMock<sdbusplus::SdBusMock> sdbusInterface;
Patrick Williams45e83522022-07-22 19:26:52 -050070 sdbusplus::bus_t bus;
Matt Spinler6b1a5c82020-01-07 08:48:53 -060071 phosphor::logging::internal::Manager logManager;
72 sd_event* sdEvent;
Matt Spinler05c2c6c2019-12-18 14:02:09 -060073 TestLogger logger;
Matt Spinler32e36b82023-04-25 10:57:15 -050074 std::vector<std::filesystem::path> dirsToRemove;
Matt Spinler89fa0822019-07-17 13:54:30 -050075};
76
Matt Spinler67456c22019-10-21 12:22:49 -050077std::optional<fs::path> findAnyPELInRepo()
78{
79 // PELs are named <timestamp>_<ID>
80 std::regex expr{"\\d+_\\d+"};
81
82 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
83 {
84 if (std::regex_search(f.path().string(), expr))
85 {
86 return f.path();
87 }
88 }
89 return std::nullopt;
90}
91
Matt Spinler7e727a32020-07-07 15:00:17 -050092size_t countPELsInRepo()
93{
94 size_t count = 0;
95 std::regex expr{"\\d+_\\d+"};
96
97 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
98 {
99 if (std::regex_search(f.path().string(), expr))
100 {
101 count++;
102 }
103 }
104 return count;
105}
106
Matt Spinlerff9cec22020-07-15 13:06:35 -0500107void deletePELFile(uint32_t id)
108{
109 char search[20];
110
111 sprintf(search, "\\d+_%.8X", id);
112 std::regex expr{search};
113
114 for (auto& f : fs::directory_iterator(getPELRepoPath() / "logs"))
115 {
116 if (std::regex_search(f.path().string(), expr))
117 {
118 fs::remove(f.path());
119 break;
120 }
121 }
122}
123
Matt Spinler89fa0822019-07-17 13:54:30 -0500124// Test that using the RAWPEL=<file> with the Manager::create() call gets
125// a PEL saved in the repository.
126TEST_F(ManagerTest, TestCreateWithPEL)
127{
Matt Spinlerc8705e22019-09-11 12:36:07 -0500128 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500129 std::make_unique<MockDataInterface>();
Matt Spinler89fa0822019-07-17 13:54:30 -0500130
Matt Spinlerd96fa602022-12-15 11:11:26 -0600131 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
132
Matt Spinler4bec95d2025-11-17 13:19:04 -0600133 Manager manager{logManager, std::move(dataIface),
134 std::bind_front(&TestLogger::log, &logger),
135 std::move(journal)};
Matt Spinler89fa0822019-07-17 13:54:30 -0500136
137 // Create a PEL, write it to a file, and pass that filename into
138 // the create function.
Matt Spinler42828bd2019-10-11 10:39:30 -0500139 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler89fa0822019-07-17 13:54:30 -0500140
141 fs::path pelFilename = makeTempDir() / "rawpel";
142 std::ofstream pelFile{pelFilename};
Matt Spinler42828bd2019-10-11 10:39:30 -0500143 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
Matt Spinler89fa0822019-07-17 13:54:30 -0500144 pelFile.close();
145
Patrick Williamse5940632024-11-22 20:47:58 -0500146 std::map<std::string, std::string> additionalData{
147 {"RAWPEL", pelFilename.string()}};
Matt Spinler89fa0822019-07-17 13:54:30 -0500148 std::vector<std::string> associations;
149
Matt Spinler4bec95d2025-11-17 13:19:04 -0600150 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinler89fa0822019-07-17 13:54:30 -0500151 associations);
152
Matt Spinler67456c22019-10-21 12:22:49 -0500153 // Find the file in the PEL repository directory
154 auto pelPathInRepo = findAnyPELInRepo();
Matt Spinler89fa0822019-07-17 13:54:30 -0500155
Matt Spinler67456c22019-10-21 12:22:49 -0500156 EXPECT_TRUE(pelPathInRepo);
Matt Spinler89fa0822019-07-17 13:54:30 -0500157
Matt Spinler475e5742019-07-18 16:09:49 -0500158 // Now remove it based on its OpenBMC event log ID
159 manager.erase(42);
160
Matt Spinler67456c22019-10-21 12:22:49 -0500161 pelPathInRepo = findAnyPELInRepo();
Matt Spinler475e5742019-07-18 16:09:49 -0500162
Matt Spinler67456c22019-10-21 12:22:49 -0500163 EXPECT_FALSE(pelPathInRepo);
Matt Spinler89fa0822019-07-17 13:54:30 -0500164}
Matt Spinler67456c22019-10-21 12:22:49 -0500165
Matt Spinlere95fd012020-01-07 12:53:16 -0600166TEST_F(ManagerTest, TestCreateWithInvalidPEL)
167{
168 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500169 std::make_unique<MockDataInterface>();
Matt Spinlere95fd012020-01-07 12:53:16 -0600170
Matt Spinlerd96fa602022-12-15 11:11:26 -0600171 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
172
Matt Spinler4bec95d2025-11-17 13:19:04 -0600173 Manager manager{logManager, std::move(dataIface),
174 std::bind_front(&TestLogger::log, &logger),
175 std::move(journal)};
Matt Spinlere95fd012020-01-07 12:53:16 -0600176
177 // Create a PEL, write it to a file, and pass that filename into
178 // the create function.
179 auto data = pelDataFactory(TestPELType::pelSimple);
180
181 // Truncate it to make it invalid.
182 data.resize(200);
183
184 fs::path pelFilename = makeTempDir() / "rawpel";
185 std::ofstream pelFile{pelFilename};
186 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
187 pelFile.close();
188
Patrick Williamse5940632024-11-22 20:47:58 -0500189 std::map<std::string, std::string> additionalData{
190 {"RAWPEL", pelFilename.string()}};
Matt Spinlere95fd012020-01-07 12:53:16 -0600191 std::vector<std::string> associations;
192
Matt Spinler4bec95d2025-11-17 13:19:04 -0600193 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinlere95fd012020-01-07 12:53:16 -0600194 associations);
195
196 // Run the event loop to log the bad PEL event
197 sdeventplus::Event e{sdEvent};
198 e.run(std::chrono::milliseconds(1));
199
200 PEL invalidPEL{data};
201 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
Matt Spinler4bec95d2025-11-17 13:19:04 -0600202 EXPECT_EQ(logger.errLevel, Level::Error);
Matt Spinlere95fd012020-01-07 12:53:16 -0600203 EXPECT_EQ(std::stoi(logger.ad["PLID"], nullptr, 16), invalidPEL.plid());
204 EXPECT_EQ(logger.ad["OBMC_LOG_ID"], "42");
205 EXPECT_EQ(logger.ad["SRC"], (*invalidPEL.primarySRC())->asciiString());
206 EXPECT_EQ(logger.ad["PEL_SIZE"], std::to_string(data.size()));
207
Matt Spinlerfe721892020-04-02 10:28:08 -0500208 // Check that the bad PEL data was saved to a file.
209 auto badPELData = readPELFile(getPELRepoPath() / "badPEL");
210 EXPECT_EQ(*badPELData, data);
Matt Spinlere95fd012020-01-07 12:53:16 -0600211}
212
Matt Spinler67456c22019-10-21 12:22:49 -0500213// Test that the message registry can be used to build a PEL.
214TEST_F(ManagerTest, TestCreateWithMessageRegistry)
215{
216 const auto registry = R"(
217{
218 "PELs":
219 [
220 {
221 "Name": "xyz.openbmc_project.Error.Test",
222 "Subsystem": "power_supply",
223 "ActionFlags": ["service_action", "report"],
224 "SRC":
225 {
226 "ReasonCode": "0x2030"
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800227 },
Vijay Lobo593a4c62021-06-16 14:25:26 -0500228 "Callouts": [
229 {
230 "CalloutList": [
Matt Spinler2edce4e2024-01-17 11:13:51 -0600231 {"Priority": "high", "Procedure": "BMC0001"},
Vijay Lobo593a4c62021-06-16 14:25:26 -0500232 {"Priority": "medium", "SymbolicFRU": "service_docs"}
233 ]
234 }
235 ],
Harisuddin Mohamed Isa0f717e12020-01-15 20:05:33 +0800236 "Documentation":
237 {
238 "Description": "A PGOOD Fault",
239 "Message": "PS had a PGOOD Fault"
Matt Spinler67456c22019-10-21 12:22:49 -0500240 }
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500241 },
242 {
243 "Name": "xyz.openbmc_project.Logging.Error.Default",
244 "Subsystem": "bmc_firmware",
245 "SRC":
246 {
247 "ReasonCode": "0x2031"
248 },
249 "Documentation":
250 {
251 "Description": "The entry used when no match found",
252 "Message": "This is a generic SRC"
253 }
Matt Spinler67456c22019-10-21 12:22:49 -0500254 }
255 ]
256}
257)";
258
Matt Spinler0d804ef2020-05-12 16:16:26 -0500259 auto path = getPELReadOnlyDataPath();
Matt Spinlerd4ffb652019-11-12 14:16:14 -0600260 fs::create_directories(path);
261 path /= "message_registry.json";
262
Matt Spinler67456c22019-10-21 12:22:49 -0500263 std::ofstream registryFile{path};
264 registryFile << registry;
265 registryFile.close();
266
Matt Spinler67456c22019-10-21 12:22:49 -0500267 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500268 std::make_unique<MockDataInterface>();
Matt Spinler67456c22019-10-21 12:22:49 -0500269
Matt Spinlerd96fa602022-12-15 11:11:26 -0600270 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
271
Matt Spinler4bec95d2025-11-17 13:19:04 -0600272 Manager manager{logManager, std::move(dataIface),
273 std::bind_front(&TestLogger::log, &logger),
274 std::move(journal)};
Matt Spinler67456c22019-10-21 12:22:49 -0500275
Patrick Williamse5940632024-11-22 20:47:58 -0500276 std::map<std::string, std::string> additionalData{{"FOO", "BAR"}};
Matt Spinler67456c22019-10-21 12:22:49 -0500277 std::vector<std::string> associations;
278
279 // Create the event log to create the PEL from.
Matt Spinler4bec95d2025-11-17 13:19:04 -0600280 manager.create("xyz.openbmc_project.Error.Test", 33, 0, Level::Error,
281 additionalData, associations);
Matt Spinler67456c22019-10-21 12:22:49 -0500282
283 // Ensure a PEL was created in the repository
284 auto pelFile = findAnyPELInRepo();
285 ASSERT_TRUE(pelFile);
286
287 auto data = readPELFile(*pelFile);
288 PEL pel(*data);
289
290 // Spot check it. Other testcases cover the details.
291 EXPECT_TRUE(pel.valid());
292 EXPECT_EQ(pel.obmcLogID(), 33);
293 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
294 "BD612030 ");
Vijay Lobod354a392021-06-01 16:21:02 -0500295 // Check if the eventId creation is good
296 EXPECT_EQ(manager.getEventId(pel),
297 "BD612030 00000055 00000010 00000000 00000000 00000000 00000000 "
298 "00000000 00000000");
Vijay Lobo593a4c62021-06-16 14:25:26 -0500299 // Check if resolution property creation is good
300 EXPECT_EQ(manager.getResolution(pel),
Matt Spinlerea2873d2021-08-18 10:35:40 -0500301 "1. Priority: High, Procedure: BMC0001\n2. Priority: Medium, PN: "
Vijay Lobo593a4c62021-06-16 14:25:26 -0500302 "SVCDOCS\n");
Matt Spinler67456c22019-10-21 12:22:49 -0500303
304 // Remove it
305 manager.erase(33);
306 pelFile = findAnyPELInRepo();
307 EXPECT_FALSE(pelFile);
308
309 // Create an event log that can't be found in the registry.
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500310 // In this case, xyz.openbmc_project.Logging.Error.Default will
311 // be used as the key instead to find a registry match.
Matt Spinler4bec95d2025-11-17 13:19:04 -0600312 manager.create("xyz.openbmc_project.Error.Foo", 42, 0, Level::Error,
313 additionalData, associations);
Matt Spinler67456c22019-10-21 12:22:49 -0500314
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500315 // Ensure a PEL was still created in the repository
Matt Spinler67456c22019-10-21 12:22:49 -0500316 pelFile = findAnyPELInRepo();
Matt Spinler30ddc9f2020-07-16 15:39:59 -0500317 ASSERT_TRUE(pelFile);
318
319 data = readPELFile(*pelFile);
320 PEL newPEL(*data);
321
322 EXPECT_TRUE(newPEL.valid());
323 EXPECT_EQ(newPEL.obmcLogID(), 42);
324 EXPECT_EQ(newPEL.primarySRC().value()->asciiString(),
325 "BD8D2031 ");
326
327 // Check for both the original AdditionalData item as well as
328 // the ERROR_NAME item that should contain the error message
329 // property that wasn't found.
330 std::string errorName;
331 std::string adItem;
332
333 for (const auto& section : newPEL.optionalSections())
334 {
335 if (SectionID::userData == static_cast<SectionID>(section->header().id))
336 {
337 if (UserDataFormat::json ==
338 static_cast<UserDataFormat>(section->header().subType))
339 {
340 auto ud = static_cast<UserData*>(section.get());
341
342 // Check that there was a UserData section added that
343 // contains debug details about the device.
344 const auto& d = ud->data();
345 std::string jsonString{d.begin(), d.end()};
346 auto json = nlohmann::json::parse(jsonString);
347
348 if (json.contains("ERROR_NAME"))
349 {
350 errorName = json["ERROR_NAME"].get<std::string>();
351 }
352
353 if (json.contains("FOO"))
354 {
355 adItem = json["FOO"].get<std::string>();
356 }
357 }
358 }
359 if (!errorName.empty())
360 {
361 break;
362 }
363 }
364
365 EXPECT_EQ(errorName, "xyz.openbmc_project.Error.Foo");
366 EXPECT_EQ(adItem, "BAR");
Matt Spinler67456c22019-10-21 12:22:49 -0500367}
Matt Spinlera34ab722019-12-16 10:39:32 -0600368
369TEST_F(ManagerTest, TestDBusMethods)
370{
Matt Spinlera34ab722019-12-16 10:39:32 -0600371 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500372 std::make_unique<MockDataInterface>();
Matt Spinlera34ab722019-12-16 10:39:32 -0600373
Matt Spinlerd96fa602022-12-15 11:11:26 -0600374 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
375
Matt Spinler4bec95d2025-11-17 13:19:04 -0600376 Manager manager{logManager, std::move(dataIface),
377 std::bind_front(&TestLogger::log, &logger),
378 std::move(journal)};
Matt Spinlera34ab722019-12-16 10:39:32 -0600379
380 // Create a PEL, write it to a file, and pass that filename into
381 // the create function so there's one in the repo.
382 auto data = pelDataFactory(TestPELType::pelSimple);
383
384 fs::path pelFilename = makeTempDir() / "rawpel";
385 std::ofstream pelFile{pelFilename};
386 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
387 pelFile.close();
388
Patrick Williamse5940632024-11-22 20:47:58 -0500389 std::map<std::string, std::string> additionalData{
390 {"RAWPEL", pelFilename.string()}};
Matt Spinlera34ab722019-12-16 10:39:32 -0600391 std::vector<std::string> associations;
392
Matt Spinler4bec95d2025-11-17 13:19:04 -0600393 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinlera34ab722019-12-16 10:39:32 -0600394 associations);
395
396 // getPELFromOBMCID
397 auto newData = manager.getPELFromOBMCID(42);
398 EXPECT_EQ(newData.size(), data.size());
399
400 // Read the PEL to get the ID for later
401 PEL pel{newData};
402 auto id = pel.id();
403
404 EXPECT_THROW(
405 manager.getPELFromOBMCID(id + 1),
406 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
407
408 // getPEL
409 auto unixfd = manager.getPEL(id);
410
411 // Get the size
412 struct stat s;
413 int r = fstat(unixfd, &s);
414 ASSERT_EQ(r, 0);
415 auto size = s.st_size;
416
417 // Open the FD and check the contents
418 FILE* fp = fdopen(unixfd, "r");
419 ASSERT_NE(fp, nullptr);
420
421 std::vector<uint8_t> fdData;
422 fdData.resize(size);
423 r = fread(fdData.data(), 1, size, fp);
424 EXPECT_EQ(r, size);
425
426 EXPECT_EQ(newData, fdData);
427
428 fclose(fp);
429
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600430 // Run the event loop to close the FD
431 sdeventplus::Event e{sdEvent};
432 e.run(std::chrono::milliseconds(1));
433
Matt Spinlera34ab722019-12-16 10:39:32 -0600434 EXPECT_THROW(
435 manager.getPEL(id + 1),
436 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
437
438 // hostAck
439 manager.hostAck(id);
440
441 EXPECT_THROW(
442 manager.hostAck(id + 1),
443 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
444
445 // hostReject
446 manager.hostReject(id, Manager::RejectionReason::BadPEL);
Matt Spinler05c2c6c2019-12-18 14:02:09 -0600447
448 // Run the event loop to log the bad PEL event
449 e.run(std::chrono::milliseconds(1));
450
451 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.SentBadPELToHost");
452 EXPECT_EQ(id, std::stoi(logger.ad["BAD_ID"], nullptr, 16));
453
Matt Spinlera34ab722019-12-16 10:39:32 -0600454 manager.hostReject(id, Manager::RejectionReason::HostFull);
455
456 EXPECT_THROW(
457 manager.hostReject(id + 1, Manager::RejectionReason::BadPEL),
458 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
459
Ramesh Iyyarf4203c42021-06-24 06:09:23 -0500460 // GetPELIdFromBMCLogId
461 EXPECT_EQ(pel.id(), manager.getPELIdFromBMCLogId(pel.obmcLogID()));
462 EXPECT_THROW(
463 manager.getPELIdFromBMCLogId(pel.obmcLogID() + 1),
464 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
Ramesh Iyyar530efbf2021-06-24 06:22:22 -0500465
466 // GetBMCLogIdFromPELId
467 EXPECT_EQ(pel.obmcLogID(), manager.getBMCLogIdFromPELId(pel.id()));
468 EXPECT_THROW(
469 manager.getBMCLogIdFromPELId(pel.id() + 1),
470 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
Matt Spinlera34ab722019-12-16 10:39:32 -0600471}
Matt Spinler19e72902020-01-24 11:05:20 -0600472
473// An ESEL from the wild
474const std::string esel{
475 "00 00 df 00 00 00 00 20 00 04 12 01 6f aa 00 00 "
Matt Spinler0bf04b52023-04-28 10:30:26 -0500476 "50 48 00 30 01 00 33 00 20 23 05 11 10 20 20 00 00 00 00 07 5c d5 50 db "
Matt Spinler19e72902020-01-24 11:05:20 -0600477 "42 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 4e 90 00 00 4e "
478 "55 48 00 18 01 00 09 00 8a 03 40 00 00 00 00 00 ff ff 00 00 00 00 00 00 "
479 "50 53 00 50 01 01 00 00 02 00 00 09 33 2d 00 48 00 00 00 e0 00 00 10 00 "
480 "00 00 00 00 00 20 00 00 00 0c 00 02 00 00 00 fa 00 00 0c e4 00 00 00 12 "
481 "42 43 38 41 33 33 32 44 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "
482 "20 20 20 20 20 20 20 20 55 44 00 1c 01 06 01 00 02 54 41 4b 00 00 00 06 "
483 "00 00 00 55 00 01 f9 20 00 00 00 00 55 44 00 24 01 06 01 00 01 54 41 4b "
484 "00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 23 01 00 02 00 05 00 00 "
485 "55 44 00 0c 01 0b 01 00 0f 01 00 00 55 44 00 10 01 04 01 00 0f 9f de 6a "
486 "00 01 00 00 55 44 00 7c 00 0c 01 00 00 13 0c 02 00 fa 0c e4 16 00 01 2c "
487 "0c 1c 16 00 00 fa 0a f0 14 00 00 fa 0b b8 14 00 00 be 09 60 12 00 01 2c "
488 "0d 7a 12 00 00 fa 0c 4e 10 00 00 fa 0c e4 10 00 00 be 0a 8c 16 00 01 2c "
489 "0c 1c 16 00 01 09 09 f6 16 00 00 fa 09 f6 14 00 00 fa 0b b8 14 00 00 fa "
490 "0a f0 14 00 00 be 08 ca 12 00 01 2c 0c e4 12 00 00 fa 0b 54 10 00 00 fa "
491 "0c 2d 10 00 00 be 08 ca 55 44 00 58 01 03 01 00 00 00 00 00 00 05 31 64 "
492 "00 00 00 00 00 05 0d d4 00 00 00 00 40 5f 06 e0 00 00 00 00 40 5d d2 00 "
493 "00 00 00 00 40 57 d3 d0 00 00 00 00 40 58 f6 a0 00 00 00 00 40 54 c9 34 "
494 "00 00 00 00 40 55 9a 10 00 00 00 00 40 4c 0a 80 00 00 00 00 00 00 27 14 "
495 "55 44 01 84 01 01 01 00 48 6f 73 74 62 6f 6f 74 20 42 75 69 6c 64 20 49 "
496 "44 3a 20 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 34 64 66 2d 70 30 61 38 "
497 "37 64 63 34 2f 68 62 69 63 6f 72 65 2e 62 69 6e 00 49 42 4d 2d 77 69 74 "
498 "68 65 72 73 70 6f 6f 6e 2d 4f 50 39 2d 76 32 2e 34 2d 39 2e 32 33 34 0a "
499 "09 6f 70 2d 62 75 69 6c 64 2d 38 32 66 34 63 66 30 0a 09 62 75 69 6c 64 "
500 "72 6f 6f 74 2d 32 30 31 39 2e 30 35 2e 32 2d 31 30 2d 67 38 39 35 39 31 "
501 "31 34 0a 09 73 6b 69 62 6f 6f 74 2d 76 36 2e 35 2d 31 38 2d 67 34 37 30 "
502 "66 66 62 35 66 32 39 64 37 0a 09 68 6f 73 74 62 6f 6f 74 2d 66 65 63 37 "
503 "34 64 66 2d 70 30 61 38 37 64 63 34 0a 09 6f 63 63 2d 65 34 35 39 37 61 "
504 "62 0a 09 6c 69 6e 75 78 2d 35 2e 32 2e 31 37 2d 6f 70 65 6e 70 6f 77 65 "
505 "72 31 2d 70 64 64 63 63 30 33 33 0a 09 70 65 74 69 74 62 6f 6f 74 2d 76 "
506 "31 2e 31 30 2e 34 0a 09 6d 61 63 68 69 6e 65 2d 78 6d 6c 2d 63 36 32 32 "
507 "63 62 35 2d 70 37 65 63 61 62 33 64 0a 09 68 6f 73 74 62 6f 6f 74 2d 62 "
508 "69 6e 61 72 69 65 73 2d 36 36 65 39 61 36 30 0a 09 63 61 70 70 2d 75 63 "
509 "6f 64 65 2d 70 39 2d 64 64 32 2d 76 34 0a 09 73 62 65 2d 36 30 33 33 30 "
510 "65 30 0a 09 68 63 6f 64 65 2d 68 77 30 39 32 31 31 39 61 2e 6f 70 6d 73 "
511 "74 0a 00 00 55 44 00 70 01 04 01 00 0f 9f de 6a 00 05 00 00 07 5f 1d f4 "
512 "30 32 43 59 34 37 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
513 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
514 "0b ac 54 02 59 41 31 39 33 34 36 39 37 30 35 38 00 00 00 00 00 00 05 22 "
515 "a1 58 01 8a 00 58 40 20 17 18 4d 2c 00 00 00 fc 01 a1 00 00 55 44 00 14 "
516 "01 08 01 00 00 00 00 01 00 00 00 5a 00 00 00 05 55 44 03 fc 01 15 31 00 "
517 "01 28 00 42 46 41 50 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 f4 "
518 "00 00 00 00 00 00 03 f4 00 00 00 0b 00 00 00 00 00 00 00 3d 2c 9b c2 84 "
519 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 09 "
520 "00 00 00 00 00 11 bd 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
521 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 00 00 01 2c "
522 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c 00 00 00 64 00 00 00 3d "
523 "2c 9b d1 11 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
524 "00 00 00 0a 00 00 00 00 00 13 b5 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
525 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 "
526 "00 00 00 be 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a 8c 00 00 00 64 "
527 "00 00 00 3d 2c 9b df 98 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
528 "00 00 00 00 00 00 00 0b 00 00 00 00 00 15 ae 20 00 00 00 00 00 01 f8 80 "
529 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 "
530 "00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c e4 "
531 "00 00 00 64 00 00 00 3d 2c 9b ea b7 00 00 01 e4 00 48 43 4f fb ed 70 b1 "
532 "00 00 02 01 00 00 00 00 00 00 00 0c 00 00 00 00 00 17 a6 a0 00 00 00 00 "
533 "00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 "
534 "00 00 00 12 00 00 00 00 00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 "
535 "00 00 0c 4e 00 00 00 64 00 00 00 3d 2c 9b f6 27 00 00 01 e4 00 48 43 4f "
536 "fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0d 00 00 00 00 00 19 9f 20 "
537 "00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 "
538 "00 00 00 00 00 00 00 12 00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 "
539 "00 00 00 00 00 00 0d 7a 00 00 00 64 00 00 00 3d 2c 9c 05 75 00 00 01 e4 "
540 "00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0e 00 00 00 00 "
541 "00 1b 97 a0 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 00 00 00 00 "
542 "00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 be 00 00 00 00 "
543 "00 00 07 d0 00 00 00 00 00 00 09 60 00 00 00 64 00 00 00 3d 2c 9c 11 29 "
544 "00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 00 00 00 0f "
545 "00 00 00 00 00 1d 90 20 00 00 00 00 00 01 f8 80 00 00 00 00 00 00 00 01 "
546 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00 fa "
547 "00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0b b8 00 00 00 64 00 00 00 3d "
548 "2c 9c 1c 45 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 00 00 00 00 "
549 "00 00 00 10 00 00 00 00 00 1f 88 a0 00 00 00 00 00 01 f8 80 00 00 00 00 "
550 "00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 00 "
551 "00 00 00 fa 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0a f0 00 00 00 64 "
552 "00 00 00 3d 2c 9c 2b 14 00 00 01 e4 00 48 43 4f fb ed 70 b1 00 00 02 01 "
553 "00 00 00 00 00 00 00 11 00 00 00 00 00 21 81 20 00 00 00 00 00 01 f8 80 "
554 "00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 "
555 "00 00 00 00 00 00 01 2c 00 00 00 00 00 00 07 d0 00 00 00 00 00 00 0c 1c "
556 "00 00 00 64 00 00 00 3d 2d 6d 8f 9e 00 00 01 e4 00 00 43 4f 52 d7 9c 36 "
557 "00 00 04 73 00 00 00 1c 00 00 00 3d 2d 6d 99 ac 00 00 01 e4 00 10 43 4f "
558 "3f f2 02 3d 00 00 05 58 00 00 00 00 02 00 00 01 00 00 00 00 00 00 00 40 "
559 "00 00 00 2c 55 44 00 30 01 15 31 00 01 28 00 42 46 41 50 49 5f 44 42 47 "
560 "00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 28 00 00 00 00 "
561 "00 00 00 00 55 44 01 74 01 15 31 00 01 28 00 42 46 41 50 49 5f 49 00 00 "
562 "00 00 00 00 00 00 00 00 00 00 01 6c 00 00 00 00 00 00 01 6c 00 00 00 0b "
563 "00 00 00 00 00 00 00 3c 0d 52 18 5e 00 00 01 e4 00 08 43 4f 46 79 94 13 "
564 "00 00 0a 5b 00 00 00 00 00 00 2c 00 00 00 00 24 00 00 00 3c 0d 6b 26 6c "
565 "00 00 01 e4 00 00 43 4f 4e 9b 18 74 00 00 01 03 00 00 00 1c 00 00 00 3c "
566 "12 b9 2d 13 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
567 "00 00 00 3c 13 02 73 53 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 "
568 "00 00 00 1c 00 00 00 3c 13 04 7c 94 00 00 01 e4 00 00 43 4f ea 31 ed d4 "
569 "00 00 05 c4 00 00 00 1c 00 00 00 3c 13 06 ad e1 00 00 01 e4 00 00 43 4f "
570 "ea 31 ed d4 00 00 05 c4 00 00 00 1c 00 00 00 3c 13 07 3f 77 00 00 01 e4 "
571 "00 00 43 4f 5e 4a 55 32 00 00 10 f2 00 00 00 1c 00 00 00 3c 13 07 4e e4 "
572 "00 00 01 e4 00 00 43 4f 5e 4a 55 32 00 00 0d 68 00 00 00 1c 00 00 00 3c "
573 "13 36 79 18 00 00 01 e4 00 00 43 4f ea 31 ed d4 00 00 05 c4 00 00 00 1c "
574 "00 00 00 3d 2c 9c 36 70 00 00 01 e4 00 00 43 4f 23 45 90 97 00 00 02 47 "
575 "00 00 00 1c 00 00 00 3d 2d 6d a3 ed 00 00 01 e4 00 08 43 4f 74 3a 5b 1a "
576 "00 00 04 cc 00 00 00 00 02 00 00 01 00 00 00 24 55 44 00 30 01 15 31 00 "
577 "01 28 00 42 53 43 41 4e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 "
578 "00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 00"};
579
580TEST_F(ManagerTest, TestESELToRawData)
581{
582 auto data = Manager::eselToRawData(esel);
583
584 EXPECT_EQ(data.size(), 2464);
585
586 PEL pel{data};
587 EXPECT_TRUE(pel.valid());
588}
589
590TEST_F(ManagerTest, TestCreateWithESEL)
591{
592 std::unique_ptr<DataInterfaceBase> dataIface =
Matt Spinlere6b48f12020-04-02 09:51:39 -0500593 std::make_unique<MockDataInterface>();
Matt Spinler19e72902020-01-24 11:05:20 -0600594
Matt Spinlerd96fa602022-12-15 11:11:26 -0600595 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
596
Matt Spinler4bec95d2025-11-17 13:19:04 -0600597 Manager manager{logManager, std::move(dataIface),
598 std::bind_front(&TestLogger::log, &logger),
599 std::move(journal)};
Matt Spinler19e72902020-01-24 11:05:20 -0600600
601 {
Patrick Williamse5940632024-11-22 20:47:58 -0500602 std::map<std::string, std::string> additionalData{{"ESEL", esel}};
Matt Spinler19e72902020-01-24 11:05:20 -0600603 std::vector<std::string> associations;
604
Matt Spinler4bec95d2025-11-17 13:19:04 -0600605 manager.create("error message", 37, 0, Level::Error, additionalData,
Matt Spinler19e72902020-01-24 11:05:20 -0600606 associations);
607
608 auto data = manager.getPELFromOBMCID(37);
609 PEL pel{data};
610 EXPECT_TRUE(pel.valid());
611 }
612
613 // Now an invalid one
614 {
Patrick Williamse5940632024-11-22 20:47:58 -0500615 std::string adItem = esel;
Matt Spinler19e72902020-01-24 11:05:20 -0600616
617 // Crop it
618 adItem.resize(adItem.size() - 300);
619
Patrick Williamse5940632024-11-22 20:47:58 -0500620 std::map<std::string, std::string> additionalData{{"ESEL", adItem}};
Matt Spinler19e72902020-01-24 11:05:20 -0600621 std::vector<std::string> associations;
622
Matt Spinler4bec95d2025-11-17 13:19:04 -0600623 manager.create("error message", 38, 0, Level::Error, additionalData,
Matt Spinler19e72902020-01-24 11:05:20 -0600624 associations);
625
626 EXPECT_THROW(
627 manager.getPELFromOBMCID(38),
628 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
629
630 // Run the event loop to log the bad PEL event
631 sdeventplus::Event e{sdEvent};
632 e.run(std::chrono::milliseconds(1));
633
634 EXPECT_EQ(logger.errName, "org.open_power.Logging.Error.BadHostPEL");
Matt Spinler4bec95d2025-11-17 13:19:04 -0600635 EXPECT_EQ(logger.errLevel, Level::Error);
Matt Spinler19e72902020-01-24 11:05:20 -0600636 }
637}
Matt Spinler7e727a32020-07-07 15:00:17 -0500638
639// Test that PELs will be pruned when necessary
640TEST_F(ManagerTest, TestPruning)
641{
642 sdeventplus::Event e{sdEvent};
643
644 std::unique_ptr<DataInterfaceBase> dataIface =
645 std::make_unique<MockDataInterface>();
646
Matt Spinlerd96fa602022-12-15 11:11:26 -0600647 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
648
Matt Spinler4bec95d2025-11-17 13:19:04 -0600649 Manager manager{logManager, std::move(dataIface),
650 std::bind_front(&TestLogger::log, &logger),
651 std::move(journal)};
Matt Spinler7e727a32020-07-07 15:00:17 -0500652
Patrick Williamse5940632024-11-22 20:47:58 -0500653 // Create 25 1000B (4096B on disk each, which is what is used for
654 // pruning) BMC non-informational PELs in the 100KB repository. After
655 // the 24th one, the repo will be 96% full and a prune should be
656 // triggered to remove all but 7 to get under 30% full. Then when the
657 // 25th is added there will be 8 left.
Matt Spinler7e727a32020-07-07 15:00:17 -0500658
659 auto dir = makeTempDir();
660 for (int i = 1; i <= 25; i++)
661 {
662 auto data = pelFactory(42, 'O', 0x40, 0x8800, 1000);
663
664 fs::path pelFilename = dir / "rawpel";
665 std::ofstream pelFile{pelFilename};
666 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
667 pelFile.close();
668
Patrick Williamse5940632024-11-22 20:47:58 -0500669 std::map<std::string, std::string> additionalData{
670 {"RAWPEL", pelFilename.string()}};
Matt Spinler7e727a32020-07-07 15:00:17 -0500671 std::vector<std::string> associations;
672
Matt Spinler4bec95d2025-11-17 13:19:04 -0600673 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinler7e727a32020-07-07 15:00:17 -0500674 associations);
675
676 // Simulate the code getting back to the event loop
677 // after each create.
678 e.run(std::chrono::milliseconds(1));
679
680 if (i < 24)
681 {
682 EXPECT_EQ(countPELsInRepo(), i);
683 }
684 else if (i == 24)
685 {
686 // Prune occured
687 EXPECT_EQ(countPELsInRepo(), 7);
688 }
689 else // i == 25
690 {
691 EXPECT_EQ(countPELsInRepo(), 8);
692 }
693 }
694
695 try
696 {
697 // Make sure the 8 newest ones are still found.
698 for (uint32_t i = 0; i < 8; i++)
699 {
700 manager.getPEL(0x50000012 + i);
701 }
702 }
Patrick Williams66491c62021-10-06 12:23:37 -0500703 catch (
704 const sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument&
Matt Spinlerbe952d22022-07-01 11:30:11 -0500705 ex)
Matt Spinler7e727a32020-07-07 15:00:17 -0500706 {
707 ADD_FAILURE() << "PELs should have all been found";
708 }
Matt Spinler7e727a32020-07-07 15:00:17 -0500709}
Matt Spinlerff9cec22020-07-15 13:06:35 -0500710
711// Test that manually deleting a PEL file will be recognized by the code.
712TEST_F(ManagerTest, TestPELManualDelete)
713{
714 sdeventplus::Event e{sdEvent};
715
716 std::unique_ptr<DataInterfaceBase> dataIface =
717 std::make_unique<MockDataInterface>();
718
Matt Spinlerd96fa602022-12-15 11:11:26 -0600719 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
720
Matt Spinler4bec95d2025-11-17 13:19:04 -0600721 Manager manager{logManager, std::move(dataIface),
722 std::bind_front(&TestLogger::log, &logger),
723 std::move(journal)};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500724
725 auto data = pelDataFactory(TestPELType::pelSimple);
726 auto dir = makeTempDir();
727 fs::path pelFilename = dir / "rawpel";
728
Patrick Williamse5940632024-11-22 20:47:58 -0500729 std::map<std::string, std::string> additionalData{
730 {"RAWPEL", pelFilename.string()}};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500731 std::vector<std::string> associations;
732
733 // Add 20 PELs, they will get incrementing IDs like
734 // 0x50000001, 0x50000002, etc.
735 for (int i = 1; i <= 20; i++)
736 {
737 std::ofstream pelFile{pelFilename};
738 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
739 pelFile.close();
740
Matt Spinler4bec95d2025-11-17 13:19:04 -0600741 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinlerff9cec22020-07-15 13:06:35 -0500742 associations);
743
744 // Sanity check this ID is really there so we can test
745 // it was deleted later. This will throw an exception if
746 // not present.
747 manager.getPEL(0x50000000 + i);
748
749 // Run an event loop pass where the internal FD is deleted
750 // after the getPEL function call.
751 e.run(std::chrono::milliseconds(1));
752 }
753
754 EXPECT_EQ(countPELsInRepo(), 20);
755
756 deletePELFile(0x50000001);
757
758 // Run a single event loop pass so the inotify event can run
759 e.run(std::chrono::milliseconds(1));
760
761 EXPECT_EQ(countPELsInRepo(), 19);
762
763 EXPECT_THROW(
764 manager.getPEL(0x50000001),
765 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
766
767 // Delete a few more, they should all get handled in the same
768 // event loop pass
769 std::vector<uint32_t> toDelete{0x50000002, 0x50000003, 0x50000004,
770 0x50000005, 0x50000006};
771 std::for_each(toDelete.begin(), toDelete.end(),
772 [](auto i) { deletePELFile(i); });
773
774 e.run(std::chrono::milliseconds(1));
775
776 EXPECT_EQ(countPELsInRepo(), 14);
777
778 std::for_each(toDelete.begin(), toDelete.end(), [&manager](const auto i) {
779 EXPECT_THROW(
780 manager.getPEL(i),
781 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
782 });
Matt Spinlerff9cec22020-07-15 13:06:35 -0500783}
784
785// Test that deleting all PELs at once is handled OK.
786TEST_F(ManagerTest, TestPELManualDeleteAll)
787{
788 sdeventplus::Event e{sdEvent};
789
790 std::unique_ptr<DataInterfaceBase> dataIface =
791 std::make_unique<MockDataInterface>();
792
Matt Spinlerd96fa602022-12-15 11:11:26 -0600793 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
794
Matt Spinler4bec95d2025-11-17 13:19:04 -0600795 Manager manager{logManager, std::move(dataIface),
796 std::bind_front(&TestLogger::log, &logger),
797 std::move(journal)};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500798
799 auto data = pelDataFactory(TestPELType::pelSimple);
800 auto dir = makeTempDir();
801 fs::path pelFilename = dir / "rawpel";
802
Patrick Williamse5940632024-11-22 20:47:58 -0500803 std::map<std::string, std::string> additionalData{
804 {"RAWPEL", pelFilename.string()}};
Matt Spinlerff9cec22020-07-15 13:06:35 -0500805 std::vector<std::string> associations;
806
807 // Add 200 PELs, they will get incrementing IDs like
808 // 0x50000001, 0x50000002, etc.
809 for (int i = 1; i <= 200; i++)
810 {
811 std::ofstream pelFile{pelFilename};
812 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
813 pelFile.close();
814
Matt Spinler4bec95d2025-11-17 13:19:04 -0600815 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinlerff9cec22020-07-15 13:06:35 -0500816 associations);
817
818 // Sanity check this ID is really there so we can test
819 // it was deleted later. This will throw an exception if
820 // not present.
821 manager.getPEL(0x50000000 + i);
822
823 // Run an event loop pass where the internal FD is deleted
824 // after the getPEL function call.
825 e.run(std::chrono::milliseconds(1));
826 }
827
828 // Delete them all at once
829 auto logPath = getPELRepoPath() / "logs";
Sumit Kumar1d8835b2021-06-07 09:35:30 -0500830 std::string cmd = "rm " + logPath.string() + "/*_*";
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500831
832 {
833 auto rc = system(cmd.c_str());
834 EXPECT_EQ(rc, 0);
835 }
Matt Spinlerff9cec22020-07-15 13:06:35 -0500836
837 EXPECT_EQ(countPELsInRepo(), 0);
838
839 // It will take 5 event loop passes to process them all
840 for (int i = 0; i < 5; i++)
841 {
842 e.run(std::chrono::milliseconds(1));
843 }
844
845 for (int i = 1; i <= 200; i++)
846 {
847 EXPECT_THROW(
848 manager.getPEL(0x50000000 + i),
849 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument);
850 }
Matt Spinlerff9cec22020-07-15 13:06:35 -0500851}
Matt Spinler3dd17e92020-08-05 15:04:27 -0500852
853// Test that fault LEDs are turned on when PELs are created
854TEST_F(ManagerTest, TestServiceIndicators)
855{
856 std::unique_ptr<DataInterfaceBase> dataIface =
857 std::make_unique<MockDataInterface>();
858
859 MockDataInterface* mockIface =
860 reinterpret_cast<MockDataInterface*>(dataIface.get());
861
Matt Spinlerd96fa602022-12-15 11:11:26 -0600862 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
863
Matt Spinler4bec95d2025-11-17 13:19:04 -0600864 Manager manager{logManager, std::move(dataIface),
865 std::bind_front(&TestLogger::log, &logger),
866 std::move(journal)};
Matt Spinler3dd17e92020-08-05 15:04:27 -0500867
868 // Add a PEL with a callout as if hostboot added it
869 {
870 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42", 0, true))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600871 .WillOnce(
872 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500873
Matt Spinler993168d2021-04-07 16:05:03 -0500874 EXPECT_CALL(*mockIface,
875 setFunctional("/system/chassis/processor", false))
Matt Spinler3dd17e92020-08-05 15:04:27 -0500876 .Times(1);
877
878 // This hostboot PEL has a single hardware callout in it.
879 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500);
880
881 fs::path pelFilename = makeTempDir() / "rawpel";
882 std::ofstream pelFile{pelFilename};
883 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
884 pelFile.close();
885
Patrick Williamse5940632024-11-22 20:47:58 -0500886 std::map<std::string, std::string> additionalData{
887 {"RAWPEL", pelFilename.string()}};
Matt Spinler3dd17e92020-08-05 15:04:27 -0500888 std::vector<std::string> associations;
889
Matt Spinler4bec95d2025-11-17 13:19:04 -0600890 manager.create("error message", 42, 0, Level::Error, additionalData,
Matt Spinler3dd17e92020-08-05 15:04:27 -0500891 associations);
Matt Spinler3dd17e92020-08-05 15:04:27 -0500892 }
893
894 // Add a BMC PEL with a callout that uses the message registry
895 {
896 std::vector<std::string> names{"systemA"};
897 EXPECT_CALL(*mockIface, getSystemNames)
898 .Times(1)
Matt Spinler1ab66962020-10-29 13:21:44 -0500899 .WillOnce(Return(names));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500900
901 EXPECT_CALL(*mockIface, expandLocationCode("P42-C23", 0))
902 .WillOnce(Return("U42-P42-C23"));
903
904 // First call to this is when building the Callout section
905 EXPECT_CALL(*mockIface, getInventoryFromLocCode("P42-C23", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600906 .WillOnce(
907 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500908
909 // Second call to this is finding the associated LED group
910 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U42-P42-C23", 0, true))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600911 .WillOnce(
912 Return(std::vector<std::string>{"/system/chassis/processor"}));
Matt Spinler3dd17e92020-08-05 15:04:27 -0500913
Matt Spinler993168d2021-04-07 16:05:03 -0500914 EXPECT_CALL(*mockIface,
915 setFunctional("/system/chassis/processor", false))
Matt Spinler3dd17e92020-08-05 15:04:27 -0500916 .Times(1);
917
918 const auto registry = R"(
919 {
920 "PELs":
921 [
922 {
923 "Name": "xyz.openbmc_project.Error.Test",
924 "Subsystem": "power_supply",
925 "ActionFlags": ["service_action", "report"],
926 "SRC":
927 {
928 "ReasonCode": "0x2030"
929 },
930 "Callouts": [
931 {
932 "CalloutList": [
933 {"Priority": "high", "LocCode": "P42-C23"}
934 ]
935 }
936 ],
937 "Documentation":
938 {
939 "Description": "Test Error",
940 "Message": "Test Error"
941 }
942 }
943 ]
944 })";
945
946 auto path = getPELReadOnlyDataPath();
947 fs::create_directories(path);
948 path /= "message_registry.json";
949
950 std::ofstream registryFile{path};
951 registryFile << registry;
952 registryFile.close();
953
Patrick Williamse5940632024-11-22 20:47:58 -0500954 std::map<std::string, std::string> additionalData;
Matt Spinler3dd17e92020-08-05 15:04:27 -0500955 std::vector<std::string> associations;
956
Matt Spinler4bec95d2025-11-17 13:19:04 -0600957 manager.create("xyz.openbmc_project.Error.Test", 42, 0, Level::Error,
958 additionalData, associations);
Matt Spinler3dd17e92020-08-05 15:04:27 -0500959 }
960}
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500961
962// Test for duplicate PELs moved to archive folder
963TEST_F(ManagerTest, TestDuplicatePEL)
964{
965 sdeventplus::Event e{sdEvent};
966 size_t count = 0;
967
968 std::unique_ptr<DataInterfaceBase> dataIface =
969 std::make_unique<MockDataInterface>();
970
Matt Spinlerd96fa602022-12-15 11:11:26 -0600971 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
972
Matt Spinler4bec95d2025-11-17 13:19:04 -0600973 Manager manager{logManager, std::move(dataIface),
974 std::bind_front(&TestLogger::log, &logger),
975 std::move(journal)};
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500976
977 for (int i = 0; i < 2; i++)
978 {
979 // This hostboot PEL has a single hardware callout in it.
980 auto data = pelFactory(1, 'B', 0x20, 0xA400, 500);
981
982 fs::path pelFilename = makeTempDir() / "rawpel";
983 std::ofstream pelFile{pelFilename};
984 pelFile.write(reinterpret_cast<const char*>(data.data()), data.size());
985 pelFile.close();
986
Patrick Williamse5940632024-11-22 20:47:58 -0500987 std::map<std::string, std::string> additionalData{
988 {"RAWPEL", pelFilename.string()}};
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500989 std::vector<std::string> associations;
990
Matt Spinler4bec95d2025-11-17 13:19:04 -0600991 manager.create("error message", 42, 0, Level::Error, additionalData,
Sumit Kumar2ccdcef2021-07-31 10:04:58 -0500992 associations);
993
994 e.run(std::chrono::milliseconds(1));
995 }
996
997 for (auto& f :
998 fs::directory_iterator(getPELRepoPath() / "logs" / "archive"))
999 {
1000 if (fs::is_regular_file(f.path()))
1001 {
1002 count++;
1003 }
1004 }
1005
1006 // Get count of PELs in the repository & in archive directtory
1007 EXPECT_EQ(countPELsInRepo(), 1);
1008 EXPECT_EQ(count, 1);
1009}
Sumit Kumar3e274432021-09-14 06:37:56 -05001010
Patrick Williamse5940632024-11-22 20:47:58 -05001011// Test termination bit set for pel with critical system termination
1012// severity
Sumit Kumar3e274432021-09-14 06:37:56 -05001013TEST_F(ManagerTest, TestTerminateBitWithPELSevCriticalSysTerminate)
1014{
1015 const auto registry = R"(
1016{
1017 "PELs":
1018 [
1019 {
1020 "Name": "xyz.openbmc_project.Error.Test",
1021 "Subsystem": "power_supply",
1022 "Severity": "critical_system_term",
1023 "ActionFlags": ["service_action", "report"],
1024 "SRC":
1025 {
1026 "ReasonCode": "0x2030"
1027 },
1028 "Documentation":
1029 {
1030 "Description": "A PGOOD Fault",
1031 "Message": "PS had a PGOOD Fault"
1032 }
1033 }
1034 ]
1035}
1036)";
1037
1038 auto path = getPELReadOnlyDataPath();
1039 fs::create_directories(path);
1040 path /= "message_registry.json";
1041
1042 std::ofstream registryFile{path};
1043 registryFile << registry;
1044 registryFile.close();
1045
1046 std::unique_ptr<DataInterfaceBase> dataIface =
1047 std::make_unique<MockDataInterface>();
1048
Matt Spinlerd96fa602022-12-15 11:11:26 -06001049 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
1050
Matt Spinler4bec95d2025-11-17 13:19:04 -06001051 Manager manager{logManager, std::move(dataIface),
1052 std::bind_front(&TestLogger::log, &logger),
1053 std::move(journal)};
Sumit Kumar3e274432021-09-14 06:37:56 -05001054
Patrick Williamse5940632024-11-22 20:47:58 -05001055 std::map<std::string, std::string> additionalData{{"FOO", "BAR"}};
Sumit Kumar3e274432021-09-14 06:37:56 -05001056 std::vector<std::string> associations;
1057
1058 // Create the event log to create the PEL from.
Matt Spinler4bec95d2025-11-17 13:19:04 -06001059 manager.create("xyz.openbmc_project.Error.Test", 33, 0, Level::Error,
1060 additionalData, associations);
Sumit Kumar3e274432021-09-14 06:37:56 -05001061
1062 // Ensure a PEL was created in the repository
1063 auto pelData = findAnyPELInRepo();
1064 ASSERT_TRUE(pelData);
1065
1066 auto getPELData = readPELFile(*pelData);
1067 PEL pel(*getPELData);
1068
1069 // Spot check it. Other testcases cover the details.
1070 EXPECT_TRUE(pel.valid());
1071
1072 // Check for terminate bit set
1073 auto& hexwords = pel.primarySRC().value()->hexwordData();
1074 EXPECT_EQ(hexwords[3] & 0x20000000, 0x20000000);
1075}
Matt Spinler0003af12022-06-08 10:46:17 -05001076
1077TEST_F(ManagerTest, TestSanitizeFieldforDBus)
1078{
1079 std::string base{"(test0!}\n\t ~"};
1080 auto string = base;
1081 string += char{' ' - 1};
1082 string += char{'~' + 1};
1083 string += char{0};
1084 string += char{static_cast<char>(0xFF)};
1085
1086 // convert the last four chars to spaces
1087 EXPECT_EQ(Manager::sanitizeFieldForDBus(string), base + " ");
1088}
Matt Spinler0dd22c82023-05-04 15:28:12 -05001089
1090TEST_F(ManagerTest, TestFruPlug)
1091{
1092 const auto registry = R"(
1093{
1094 "PELs":
1095 [{
1096 "Name": "xyz.openbmc_project.Fan.Error.Fault",
1097 "Subsystem": "power_fans",
1098 "ComponentID": "0x2800",
1099 "SRC":
1100 {
1101 "Type": "11",
1102 "ReasonCode": "0x76F0",
1103 "Words6To9": {},
1104 "DeconfigFlag": true
1105 },
1106 "Callouts": [{
1107 "CalloutList": [
1108 {"Priority": "low", "LocCode": "P0"},
1109 {"Priority": "high", "LocCode": "A3"}
1110 ]
1111 }],
1112 "Documentation": {
1113 "Description": "A Fan Fault",
1114 "Message": "Fan had a Fault"
1115 }
1116 }]
1117}
1118)";
1119
1120 auto path = getPELReadOnlyDataPath();
1121 fs::create_directories(path);
1122 path /= "message_registry.json";
1123
1124 std::ofstream registryFile{path};
1125 registryFile << registry;
1126 registryFile.close();
1127
1128 std::unique_ptr<DataInterfaceBase> dataIface =
1129 std::make_unique<MockDataInterface>();
1130
1131 MockDataInterface* mockIface =
1132 reinterpret_cast<MockDataInterface*>(dataIface.get());
1133
1134 // Set up the mock calls used when building callouts
1135 EXPECT_CALL(*mockIface, getInventoryFromLocCode("P0", 0, false))
1136 .WillRepeatedly(Return(std::vector<std::string>{"motherboard"}));
1137 EXPECT_CALL(*mockIface, expandLocationCode("P0", 0))
1138 .WillRepeatedly(Return("U1234-P0"));
1139 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U1234-P0", 0, true))
1140 .WillRepeatedly(Return(std::vector<std::string>{"motherboard"}));
1141
1142 EXPECT_CALL(*mockIface, getInventoryFromLocCode("A3", 0, false))
1143 .WillRepeatedly(Return(std::vector<std::string>{"fan"}));
1144 EXPECT_CALL(*mockIface, expandLocationCode("A3", 0))
1145 .WillRepeatedly(Return("U1234-A3"));
1146 EXPECT_CALL(*mockIface, getInventoryFromLocCode("U1234-A3", 0, true))
1147 .WillRepeatedly(Return(std::vector<std::string>{"fan"}));
1148
1149 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
1150
Matt Spinler4bec95d2025-11-17 13:19:04 -06001151 Manager manager{logManager, std::move(dataIface),
1152 std::bind_front(&TestLogger::log, &logger),
1153 std::move(journal)};
Matt Spinler0dd22c82023-05-04 15:28:12 -05001154
Patrick Williamse5940632024-11-22 20:47:58 -05001155 std::map<std::string, std::string> additionalData;
Matt Spinler0dd22c82023-05-04 15:28:12 -05001156 std::vector<std::string> associations;
1157
1158 auto checkDeconfigured = [](bool deconfigured) {
1159 auto pelFile = findAnyPELInRepo();
1160 ASSERT_TRUE(pelFile);
1161
1162 auto data = readPELFile(*pelFile);
1163 PEL pel(*data);
1164 ASSERT_TRUE(pel.valid());
1165
1166 EXPECT_EQ(pel.primarySRC().value()->getErrorStatusFlag(
1167 SRC::ErrorStatusFlags::deconfigured),
1168 deconfigured);
1169 };
1170
Matt Spinler4bec95d2025-11-17 13:19:04 -06001171 manager.create("xyz.openbmc_project.Fan.Error.Fault", 42, 0, Level::Error,
1172 additionalData, associations);
Matt Spinler0dd22c82023-05-04 15:28:12 -05001173 checkDeconfigured(true);
1174
1175 // Replace A3 so PEL deconfigured flag should be set to false
1176 mockIface->fruPresent("U1234-A3");
1177 checkDeconfigured(false);
1178
1179 manager.erase(42);
1180
1181 // Create it again and replace a FRU not in the callout list.
1182 // Deconfig flag should stay on.
Matt Spinler4bec95d2025-11-17 13:19:04 -06001183 manager.create("xyz.openbmc_project.Fan.Error.Fault", 43, 0, Level::Error,
1184 additionalData, associations);
Matt Spinler0dd22c82023-05-04 15:28:12 -05001185 checkDeconfigured(true);
1186 mockIface->fruPresent("U1234-A4");
1187 checkDeconfigured(true);
1188}
harsh-agarwal1d763db32024-09-03 09:18:50 -05001189
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001190std::pair<int, std::filesystem::path> createHWIsolatedCalloutFile()
harsh-agarwal1d763db32024-09-03 09:18:50 -05001191{
1192 json jsonCalloutDataList(nlohmann::json::value_t::array);
1193 json jsonDimmCallout;
1194
1195 jsonDimmCallout["LocationCode"] = "Ufcs-DIMM0";
1196 jsonDimmCallout["EntityPath"] = {35, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0,
1197 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1198 jsonDimmCallout["GuardType"] = "GARD_Predictive";
1199 jsonDimmCallout["Deconfigured"] = false;
1200 jsonDimmCallout["Guarded"] = true;
1201 jsonDimmCallout["Priority"] = "M";
1202 jsonCalloutDataList.emplace_back(std::move(jsonDimmCallout));
1203
1204 std::string calloutData(jsonCalloutDataList.dump());
1205 std::string calloutFile("/tmp/phalPELCalloutsJson.XXXXXX");
1206 int fileFD = -1;
1207
1208 fileFD = mkostemp(calloutFile.data(), O_RDWR);
1209 if (fileFD == -1)
1210 {
1211 perror("Failed to create PELCallouts file");
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001212 return {-1, {}};
harsh-agarwal1d763db32024-09-03 09:18:50 -05001213 }
1214
1215 ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
1216 if (rc == -1)
1217 {
1218 perror("Failed to write PELCallouts file");
1219 close(fileFD);
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001220 return {-1, {}};
harsh-agarwal1d763db32024-09-03 09:18:50 -05001221 }
1222
1223 // Ensure we seek to the beginning of the file
1224 rc = lseek(fileFD, 0, SEEK_SET);
1225 if (rc == -1)
1226 {
1227 perror("Failed to set SEEK_SET for PELCallouts file");
1228 close(fileFD);
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001229 return {-1, {}};
harsh-agarwal1d763db32024-09-03 09:18:50 -05001230 }
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001231 return {fileFD, calloutFile};
harsh-agarwal1d763db32024-09-03 09:18:50 -05001232}
1233
1234void appendFFDCEntry(int fd, uint8_t subTypeJson, uint8_t version,
1235 phosphor::logging::FFDCEntries& ffdcEntries)
1236{
1237 phosphor::logging::FFDCEntry ffdcEntry =
1238 std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
1239 Create::FFDCFormat::JSON,
1240 subTypeJson, version, fd);
1241 ffdcEntries.push_back(ffdcEntry);
1242}
1243
1244TEST_F(ManagerTest, TestPELDeleteWithoutHWIsolation)
1245{
1246 const auto registry = R"(
1247 {
1248 "PELs":
1249 [{
1250 "Name": "xyz.openbmc_project.Error.Test",
1251 "SRC":
1252 {
1253 "ReasonCode": "0x2030"
1254 },
1255 "Documentation": {
1256 "Description": "Test Error",
1257 "Message": "Test Error"
1258 }
1259 }]
1260 }
1261 )";
1262
1263 auto path = getPELReadOnlyDataPath();
1264 fs::create_directories(path);
1265 path /= "message_registry.json";
1266
1267 std::ofstream registryFile{path};
1268 registryFile << registry;
1269 registryFile.close();
1270
1271 std::unique_ptr<DataInterfaceBase> dataIface =
1272 std::make_unique<MockDataInterface>();
1273
1274 MockDataInterface* mockIface =
1275 reinterpret_cast<MockDataInterface*>(dataIface.get());
1276
1277 EXPECT_CALL(*mockIface, getInventoryFromLocCode("Ufcs-DIMM0", 0, false))
1278 .WillOnce(Return(std::vector<std::string>{
1279 "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"}));
1280
1281 // Mock the scenario where the hardware isolation guard is flagged
1282 // but is not associated, resulting in an empty list being returned.
1283 EXPECT_CALL(
1284 *mockIface,
1285 getAssociatedPaths(
1286 ::testing::StrEq(
1287 "/xyz/openbmc_project/logging/entry/42/isolated_hw_entry"),
1288 ::testing::StrEq("/"), 0,
1289 ::testing::ElementsAre(
1290 "xyz.openbmc_project.HardwareIsolation.Entry")))
1291 .WillRepeatedly(Return(std::vector<std::string>{}));
1292
1293 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
Matt Spinler4bec95d2025-11-17 13:19:04 -06001294 Manager manager{logManager, std::move(dataIface),
1295 std::bind_front(&TestLogger::log, &logger),
1296 std::move(journal)};
Patrick Williamse5940632024-11-22 20:47:58 -05001297 std::map<std::string, std::string> additionalData;
harsh-agarwal1d763db32024-09-03 09:18:50 -05001298 std::vector<std::string> associations;
1299
1300 // Check when there's no PEL with given id.
1301 {
1302 EXPECT_FALSE(manager.isDeleteProhibited(42));
1303 }
1304 // creating without ffdcEntries
Matt Spinler4bec95d2025-11-17 13:19:04 -06001305 manager.create("xyz.openbmc_project.Error.Test", 42, 0, Level::Error,
1306 additionalData, associations);
harsh-agarwal1d763db32024-09-03 09:18:50 -05001307 auto pelFile = findAnyPELInRepo();
1308 auto data = readPELFile(*pelFile);
1309 PEL pel_unguarded(*data);
1310 {
1311 // Verify that the guard flag is false.
1312 EXPECT_FALSE(pel_unguarded.getGuardFlag());
Patrick Williamse5940632024-11-22 20:47:58 -05001313 // Check that `isDeleteProhibited` returns false when the guard flag
1314 // is false.
harsh-agarwal1d763db32024-09-03 09:18:50 -05001315 EXPECT_FALSE(manager.isDeleteProhibited(42));
1316 }
1317 manager.erase(42);
1318 EXPECT_FALSE(findAnyPELInRepo());
1319
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001320 auto [fd, calloutFile] = createHWIsolatedCalloutFile();
harsh-agarwal1d763db32024-09-03 09:18:50 -05001321 ASSERT_NE(fd, -1);
1322 uint8_t subTypeJson = 0xCA;
1323 uint8_t version = 0x01;
1324 phosphor::logging::FFDCEntries ffdcEntries;
1325 appendFFDCEntry(fd, subTypeJson, version, ffdcEntries);
Matt Spinler4bec95d2025-11-17 13:19:04 -06001326 manager.create("xyz.openbmc_project.Error.Test", 42, 0, Level::Error,
1327 additionalData, associations, ffdcEntries);
harsh-agarwal1d763db32024-09-03 09:18:50 -05001328 close(fd);
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001329 std::filesystem::remove(calloutFile);
harsh-agarwal1d763db32024-09-03 09:18:50 -05001330
1331 auto pelPathInRepo = findAnyPELInRepo();
1332 auto unguardedData = readPELFile(*pelPathInRepo);
1333 PEL pel(*unguardedData);
1334 {
1335 // Verify guard flag set to true
1336 EXPECT_TRUE(pel.getGuardFlag());
1337 // Check even if guard flag is true, if dbus call returns empty
1338 // array list then `isDeleteProhibited` returns false
1339 EXPECT_FALSE(manager.isDeleteProhibited(42));
1340 }
1341 manager.erase(42);
1342}
1343
1344TEST_F(ManagerTest, TestPELDeleteWithHWIsolation)
1345{
1346 const auto registry = R"(
1347 {
1348 "PELs":
1349 [{
1350 "Name": "xyz.openbmc_project.Error.Test",
1351 "Severity": "critical_system_term",
1352 "SRC":
1353 {
1354 "ReasonCode": "0x2030"
1355 },
1356 "Documentation": {
1357 "Description": "Test Error",
1358 "Message": "Test Error"
1359 }
1360 }]
1361 }
1362 )";
1363
1364 auto path = getPELReadOnlyDataPath();
1365 fs::create_directories(path);
1366 path /= "message_registry.json";
1367
1368 std::ofstream registryFile{path};
1369 registryFile << registry;
1370 registryFile.close();
1371
1372 std::unique_ptr<DataInterfaceBase> dataIface =
1373 std::make_unique<MockDataInterface>();
1374
1375 MockDataInterface* mockIface =
1376 reinterpret_cast<MockDataInterface*>(dataIface.get());
1377
1378 EXPECT_CALL(*mockIface, getInventoryFromLocCode("Ufcs-DIMM0", 0, false))
1379 .WillOnce(Return(std::vector<std::string>{
1380 "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"}));
1381
1382 EXPECT_CALL(
1383 *mockIface,
1384 getAssociatedPaths(
1385 ::testing::StrEq(
1386 "/xyz/openbmc_project/logging/entry/42/isolated_hw_entry"),
1387 ::testing::StrEq("/"), 0,
1388 ::testing::ElementsAre(
1389 "xyz.openbmc_project.HardwareIsolation.Entry")))
1390 .WillRepeatedly(Return(std::vector<std::string>{
1391 "/xyz/openbmc_project/hardware_isolation/entry/1"}));
1392
1393 std::unique_ptr<JournalBase> journal = std::make_unique<MockJournal>();
Matt Spinler4bec95d2025-11-17 13:19:04 -06001394 Manager manager{logManager, std::move(dataIface),
1395 std::bind_front(&TestLogger::log, &logger),
1396 std::move(journal)};
Patrick Williamse5940632024-11-22 20:47:58 -05001397 std::map<std::string, std::string> additionalData;
harsh-agarwal1d763db32024-09-03 09:18:50 -05001398 std::vector<std::string> associations;
1399
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001400 auto [fd, calloutFile] = createHWIsolatedCalloutFile();
harsh-agarwal1d763db32024-09-03 09:18:50 -05001401 ASSERT_NE(fd, -1);
1402 uint8_t subTypeJson = 0xCA;
1403 uint8_t version = 0x01;
1404 phosphor::logging::FFDCEntries ffdcEntries;
1405 appendFFDCEntry(fd, subTypeJson, version, ffdcEntries);
Matt Spinler4bec95d2025-11-17 13:19:04 -06001406 manager.create("xyz.openbmc_project.Error.Test", 42, 0, Level::Error,
1407 additionalData, associations, ffdcEntries);
harsh-agarwal1d763db32024-09-03 09:18:50 -05001408 close(fd);
Matt Spinlerb015dcb2025-03-19 14:22:07 -05001409 std::filesystem::remove(calloutFile);
harsh-agarwal1d763db32024-09-03 09:18:50 -05001410
1411 auto pelFile = findAnyPELInRepo();
1412 EXPECT_TRUE(pelFile);
1413 auto data = readPELFile(*pelFile);
1414 PEL pel(*data);
1415 EXPECT_TRUE(pel.valid());
Patrick Williamse5940632024-11-22 20:47:58 -05001416 // Test case where the guard flag is set to true and the hardware
1417 // isolation guard is associated, which should result in
1418 // `isDeleteProhibited` returning true as expected.
harsh-agarwal1d763db32024-09-03 09:18:50 -05001419 EXPECT_TRUE(pel.getGuardFlag());
1420 EXPECT_TRUE(manager.isDeleteProhibited(42));
1421 manager.erase(42);
1422}