| Alexander Hansen | 40fb549 | 2025-10-28 17:56:12 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: Apache-2.0 | 
|  | 2 | // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation | 
|  | 3 |  | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 4 | #include "elog_entry.hpp" | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 5 | #include "extensions/openpower-pels/generic.hpp" | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 6 | #include "extensions/openpower-pels/pel.hpp" | 
| Matt Spinler | aa65947 | 2019-10-23 09:26:48 -0500 | [diff] [blame] | 7 | #include "mocks.hpp" | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 8 | #include "pel_utils.hpp" | 
|  | 9 |  | 
|  | 10 | #include <filesystem> | 
|  | 11 | #include <fstream> | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 12 | #include <optional> | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 13 |  | 
|  | 14 | #include <gtest/gtest.h> | 
|  | 15 |  | 
|  | 16 | namespace fs = std::filesystem; | 
|  | 17 | using namespace openpower::pels; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 18 | using ::testing::_; | 
| William A. Kennington III | b41fa54 | 2021-05-29 14:45:16 -0700 | [diff] [blame] | 19 | using ::testing::DoAll; | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 20 | using ::testing::NiceMock; | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 21 | using ::testing::Return; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 22 | using ::testing::SetArgReferee; | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 23 |  | 
|  | 24 | class PELTest : public CleanLogID | 
| Patrick Williams | 2544b41 | 2022-10-04 08:41:06 -0500 | [diff] [blame] | 25 | {}; | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 26 |  | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 27 | fs::path makeTempDir() | 
|  | 28 | { | 
|  | 29 | char path[] = "/tmp/tempdirXXXXXX"; | 
|  | 30 | std::filesystem::path dir = mkdtemp(path); | 
|  | 31 | return dir; | 
|  | 32 | } | 
|  | 33 |  | 
|  | 34 | int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data) | 
|  | 35 | { | 
|  | 36 | static size_t count = 0; | 
|  | 37 | fs::path path = dir / (std::string{"file"} + std::to_string(count)); | 
|  | 38 | std::ofstream stream{path}; | 
|  | 39 | count++; | 
|  | 40 |  | 
|  | 41 | stream.write(reinterpret_cast<const char*>(data.data()), data.size()); | 
|  | 42 | stream.close(); | 
|  | 43 |  | 
|  | 44 | FILE* fp = fopen(path.c_str(), "r"); | 
|  | 45 | return fileno(fp); | 
|  | 46 | } | 
|  | 47 |  | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 48 | TEST_F(PELTest, FlattenTest) | 
|  | 49 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 50 | auto data = pelDataFactory(TestPELType::pelSimple); | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 51 | auto pel = std::make_unique<PEL>(data); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 52 |  | 
|  | 53 | // Check a few fields | 
|  | 54 | EXPECT_TRUE(pel->valid()); | 
|  | 55 | EXPECT_EQ(pel->id(), 0x80818283); | 
|  | 56 | EXPECT_EQ(pel->plid(), 0x50515253); | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 57 | EXPECT_EQ(pel->userHeader().subsystem(), 0x10); | 
|  | 58 | EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 59 |  | 
|  | 60 | // Test that data in == data out | 
|  | 61 | auto flattenedData = pel->data(); | 
| Matt Spinler | f1b46ff | 2020-01-22 14:10:04 -0600 | [diff] [blame] | 62 | EXPECT_EQ(data, flattenedData); | 
|  | 63 | EXPECT_EQ(flattenedData.size(), pel->size()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 64 | } | 
|  | 65 |  | 
|  | 66 | TEST_F(PELTest, CommitTimeTest) | 
|  | 67 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 68 | auto data = pelDataFactory(TestPELType::pelSimple); | 
|  | 69 | auto pel = std::make_unique<PEL>(data); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 70 |  | 
|  | 71 | auto origTime = pel->commitTime(); | 
|  | 72 | pel->setCommitTime(); | 
|  | 73 | auto newTime = pel->commitTime(); | 
|  | 74 |  | 
| Matt Spinler | f1b46ff | 2020-01-22 14:10:04 -0600 | [diff] [blame] | 75 | EXPECT_NE(origTime, newTime); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 76 |  | 
|  | 77 | // Make a new PEL and check new value is still there | 
|  | 78 | auto newData = pel->data(); | 
|  | 79 | auto newPel = std::make_unique<PEL>(newData); | 
| Matt Spinler | f1b46ff | 2020-01-22 14:10:04 -0600 | [diff] [blame] | 80 | EXPECT_EQ(newTime, newPel->commitTime()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 81 | } | 
|  | 82 |  | 
|  | 83 | TEST_F(PELTest, AssignIDTest) | 
|  | 84 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 85 | auto data = pelDataFactory(TestPELType::pelSimple); | 
|  | 86 | auto pel = std::make_unique<PEL>(data); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 87 |  | 
|  | 88 | auto origID = pel->id(); | 
|  | 89 | pel->assignID(); | 
|  | 90 | auto newID = pel->id(); | 
|  | 91 |  | 
| Matt Spinler | f1b46ff | 2020-01-22 14:10:04 -0600 | [diff] [blame] | 92 | EXPECT_NE(origID, newID); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 93 |  | 
|  | 94 | // Make a new PEL and check new value is still there | 
|  | 95 | auto newData = pel->data(); | 
|  | 96 | auto newPel = std::make_unique<PEL>(newData); | 
| Matt Spinler | f1b46ff | 2020-01-22 14:10:04 -0600 | [diff] [blame] | 97 | EXPECT_EQ(newID, newPel->id()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 98 | } | 
|  | 99 |  | 
|  | 100 | TEST_F(PELTest, WithLogIDTest) | 
|  | 101 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 102 | auto data = pelDataFactory(TestPELType::pelSimple); | 
|  | 103 | auto pel = std::make_unique<PEL>(data, 0x42); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 104 |  | 
|  | 105 | EXPECT_TRUE(pel->valid()); | 
|  | 106 | EXPECT_EQ(pel->obmcLogID(), 0x42); | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | TEST_F(PELTest, InvalidPELTest) | 
|  | 110 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 111 | auto data = pelDataFactory(TestPELType::pelSimple); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 112 |  | 
|  | 113 | // Too small | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 114 | data.resize(PrivateHeader::flattenedSize()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 115 |  | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 116 | auto pel = std::make_unique<PEL>(data); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 117 |  | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 118 | EXPECT_TRUE(pel->privateHeader().valid()); | 
|  | 119 | EXPECT_FALSE(pel->userHeader().valid()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 120 | EXPECT_FALSE(pel->valid()); | 
|  | 121 |  | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 122 | // Now corrupt the private header | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 123 | data = pelDataFactory(TestPELType::pelSimple); | 
|  | 124 | data.at(0) = 0; | 
|  | 125 | pel = std::make_unique<PEL>(data); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 126 |  | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 127 | EXPECT_FALSE(pel->privateHeader().valid()); | 
|  | 128 | EXPECT_TRUE(pel->userHeader().valid()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 129 | EXPECT_FALSE(pel->valid()); | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | TEST_F(PELTest, EmptyDataTest) | 
|  | 133 | { | 
|  | 134 | std::vector<uint8_t> data; | 
|  | 135 | auto pel = std::make_unique<PEL>(data); | 
|  | 136 |  | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 137 | EXPECT_FALSE(pel->privateHeader().valid()); | 
|  | 138 | EXPECT_FALSE(pel->userHeader().valid()); | 
| Matt Spinler | cb6b059 | 2019-07-16 15:58:51 -0500 | [diff] [blame] | 139 | EXPECT_FALSE(pel->valid()); | 
|  | 140 | } | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 141 |  | 
|  | 142 | TEST_F(PELTest, CreateFromRegistryTest) | 
|  | 143 | { | 
|  | 144 | message::Entry regEntry; | 
|  | 145 | uint64_t timestamp = 5; | 
|  | 146 |  | 
|  | 147 | regEntry.name = "test"; | 
|  | 148 | regEntry.subsystem = 5; | 
|  | 149 | regEntry.actionFlags = 0xC000; | 
| Matt Spinler | bd716f0 | 2019-10-15 10:54:11 -0500 | [diff] [blame] | 150 | regEntry.src.type = 0xBD; | 
|  | 151 | regEntry.src.reasonCode = 0x1234; | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 152 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 153 | std::map<std::string, std::string> data{{"KEY1", "VALUE1"}}; | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 154 | AdditionalData ad{data}; | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 155 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 156 | NiceMock<MockJournal> journal; | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 157 | PelFFDC ffdc; | 
| Matt Spinler | bd716f0 | 2019-10-15 10:54:11 -0500 | [diff] [blame] | 158 |  | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 159 | PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 160 | ad,       ffdc, dataIface, journal}; | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 161 |  | 
|  | 162 | EXPECT_TRUE(pel.valid()); | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 163 | EXPECT_EQ(pel.privateHeader().obmcLogID(), 42); | 
|  | 164 | EXPECT_EQ(pel.userHeader().severity(), 0x40); | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 165 |  | 
| Matt Spinler | bd716f0 | 2019-10-15 10:54:11 -0500 | [diff] [blame] | 166 | EXPECT_EQ(pel.primarySRC().value()->asciiString(), | 
|  | 167 | "BD051234                        "); | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 168 |  | 
|  | 169 | // Check that certain optional sections have been created | 
|  | 170 | size_t mtmsCount = 0; | 
|  | 171 | size_t euhCount = 0; | 
|  | 172 | size_t udCount = 0; | 
|  | 173 |  | 
|  | 174 | for (const auto& section : pel.optionalSections()) | 
|  | 175 | { | 
|  | 176 | if (section->header().id == | 
|  | 177 | static_cast<uint16_t>(SectionID::failingMTMS)) | 
|  | 178 | { | 
|  | 179 | mtmsCount++; | 
|  | 180 | } | 
|  | 181 | else if (section->header().id == | 
|  | 182 | static_cast<uint16_t>(SectionID::extendedUserHeader)) | 
|  | 183 | { | 
|  | 184 | euhCount++; | 
|  | 185 | } | 
|  | 186 | else if (section->header().id == | 
|  | 187 | static_cast<uint16_t>(SectionID::userData)) | 
|  | 188 | { | 
|  | 189 | udCount++; | 
|  | 190 | } | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | EXPECT_EQ(mtmsCount, 1); | 
|  | 194 | EXPECT_EQ(euhCount, 1); | 
|  | 195 | EXPECT_EQ(udCount, 2); // AD section and sysInfo section | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 196 | ASSERT_FALSE(pel.isHwCalloutPresent()); | 
| Matt Spinler | 1f93c59 | 2020-09-10 10:43:08 -0500 | [diff] [blame] | 197 |  | 
|  | 198 | { | 
|  | 199 | // The same thing, but without the action flags specified | 
|  | 200 | // in the registry, so the constructor should set them. | 
|  | 201 | regEntry.actionFlags = std::nullopt; | 
|  | 202 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 203 | PEL pel2{regEntry,  42, | 
|  | 204 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 205 | ad,        ffdc, | 
|  | 206 | dataIface, journal}; | 
| Matt Spinler | 1f93c59 | 2020-09-10 10:43:08 -0500 | [diff] [blame] | 207 |  | 
|  | 208 | EXPECT_EQ(pel2.userHeader().actionFlags(), 0xA800); | 
|  | 209 | } | 
| Matt Spinler | b832363 | 2019-09-20 15:11:04 -0500 | [diff] [blame] | 210 | } | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 211 |  | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 212 | // Test that when the AdditionalData size is over 16KB that | 
|  | 213 | // the PEL that's created is exactly 16KB since the UserData | 
|  | 214 | // section that contains all that data was pruned. | 
|  | 215 | TEST_F(PELTest, CreateTooBigADTest) | 
|  | 216 | { | 
|  | 217 | message::Entry regEntry; | 
|  | 218 | uint64_t timestamp = 5; | 
|  | 219 |  | 
|  | 220 | regEntry.name = "test"; | 
|  | 221 | regEntry.subsystem = 5; | 
|  | 222 | regEntry.actionFlags = 0xC000; | 
|  | 223 | regEntry.src.type = 0xBD; | 
|  | 224 | regEntry.src.reasonCode = 0x1234; | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 225 | PelFFDC ffdc; | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 226 |  | 
|  | 227 | // Over the 16KB max PEL size | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 228 | std::map<std::string, std::string> data{{"KEY1", std::string(17000, 'G')}}; | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 229 | AdditionalData ad{data}; | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 230 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 231 | NiceMock<MockJournal> journal; | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 232 |  | 
| Matt Spinler | 56ad2a0 | 2020-03-26 14:00:52 -0500 | [diff] [blame] | 233 | PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 234 | ad,       ffdc, dataIface, journal}; | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 235 |  | 
|  | 236 | EXPECT_TRUE(pel.valid()); | 
|  | 237 | EXPECT_EQ(pel.size(), 16384); | 
|  | 238 |  | 
|  | 239 | // Make sure that there are still 2 UD sections. | 
| Matt Spinler | be952d2 | 2022-07-01 11:30:11 -0500 | [diff] [blame] | 240 | const auto& optSections = pel.optionalSections(); | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 241 | auto udCount = std::count_if( | 
|  | 242 | optSections.begin(), optSections.end(), [](const auto& section) { | 
|  | 243 | return section->header().id == | 
|  | 244 | static_cast<uint16_t>(SectionID::userData); | 
|  | 245 | }); | 
| Matt Spinler | 9b7e94f | 2020-03-24 15:44:41 -0500 | [diff] [blame] | 246 |  | 
|  | 247 | EXPECT_EQ(udCount, 2); // AD section and sysInfo section | 
|  | 248 | } | 
|  | 249 |  | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 250 | // Test that we'll create Generic optional sections for sections that | 
|  | 251 | // there aren't explicit classes for. | 
|  | 252 | TEST_F(PELTest, GenericSectionTest) | 
|  | 253 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 254 | auto data = pelDataFactory(TestPELType::pelSimple); | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 255 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 256 | std::vector<uint8_t> section1{ | 
|  | 257 | 0x58, 0x58, // ID 'XX' | 
|  | 258 | 0x00, 0x18, // Size | 
|  | 259 | 0x01, 0x02, // version, subtype | 
|  | 260 | 0x03, 0x04, // comp ID | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 261 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 262 | // some data | 
|  | 263 | 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F, | 
|  | 264 | 0x09, 0x22, 0x3A, 0x00}; | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 265 |  | 
|  | 266 | std::vector<uint8_t> section2{ | 
|  | 267 | 0x59, 0x59, // ID 'YY' | 
|  | 268 | 0x00, 0x20, // Size | 
|  | 269 | 0x01, 0x02, // version, subtype | 
|  | 270 | 0x03, 0x04, // comp ID | 
|  | 271 |  | 
|  | 272 | // some data | 
|  | 273 | 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F, | 
|  | 274 | 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; | 
|  | 275 |  | 
|  | 276 | // Add the new sections at the end | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 277 | data.insert(data.end(), section1.begin(), section1.end()); | 
|  | 278 | data.insert(data.end(), section2.begin(), section2.end()); | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 279 |  | 
|  | 280 | // Increment the section count | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 281 | data.at(27) += 2; | 
|  | 282 | auto origData = data; | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 283 |  | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 284 | PEL pel{data}; | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 285 |  | 
|  | 286 | const auto& sections = pel.optionalSections(); | 
|  | 287 |  | 
|  | 288 | bool foundXX = false; | 
|  | 289 | bool foundYY = false; | 
|  | 290 |  | 
|  | 291 | // Check that we can find these 2 Generic sections | 
|  | 292 | for (const auto& section : sections) | 
|  | 293 | { | 
|  | 294 | if (section->header().id == 0x5858) | 
|  | 295 | { | 
|  | 296 | foundXX = true; | 
|  | 297 | EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); | 
|  | 298 | } | 
|  | 299 | else if (section->header().id == 0x5959) | 
|  | 300 | { | 
|  | 301 | foundYY = true; | 
|  | 302 | EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); | 
|  | 303 | } | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | EXPECT_TRUE(foundXX); | 
|  | 307 | EXPECT_TRUE(foundYY); | 
| Matt Spinler | 07eefc5 | 2019-09-26 11:18:26 -0500 | [diff] [blame] | 308 |  | 
|  | 309 | // Now flatten and check | 
|  | 310 | auto newData = pel.data(); | 
|  | 311 |  | 
|  | 312 | EXPECT_EQ(origData, newData); | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 313 | } | 
|  | 314 |  | 
|  | 315 | // Test that an invalid section will still get a Generic object | 
|  | 316 | TEST_F(PELTest, InvalidGenericTest) | 
|  | 317 | { | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 318 | auto data = pelDataFactory(TestPELType::pelSimple); | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 319 |  | 
|  | 320 | // Not a valid section | 
|  | 321 | std::vector<uint8_t> section1{0x01, 0x02, 0x03}; | 
|  | 322 |  | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 323 | data.insert(data.end(), section1.begin(), section1.end()); | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 324 |  | 
|  | 325 | // Increment the section count | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 326 | data.at(27) += 1; | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 327 |  | 
| Matt Spinler | 42828bd | 2019-10-11 10:39:30 -0500 | [diff] [blame] | 328 | PEL pel{data}; | 
| Matt Spinler | 131870c | 2019-09-25 13:29:04 -0500 | [diff] [blame] | 329 | EXPECT_FALSE(pel.valid()); | 
|  | 330 |  | 
|  | 331 | const auto& sections = pel.optionalSections(); | 
|  | 332 |  | 
|  | 333 | bool foundGeneric = false; | 
|  | 334 | for (const auto& section : sections) | 
|  | 335 | { | 
|  | 336 | if (dynamic_cast<Generic*>(section.get()) != nullptr) | 
|  | 337 | { | 
|  | 338 | foundGeneric = true; | 
|  | 339 | EXPECT_EQ(section->valid(), false); | 
|  | 340 | break; | 
|  | 341 | } | 
|  | 342 | } | 
|  | 343 |  | 
|  | 344 | EXPECT_TRUE(foundGeneric); | 
|  | 345 | } | 
| Matt Spinler | afa857c | 2019-10-24 13:03:46 -0500 | [diff] [blame] | 346 |  | 
|  | 347 | // Create a UserData section out of AdditionalData | 
|  | 348 | TEST_F(PELTest, MakeUDSectionTest) | 
|  | 349 | { | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 350 | std::map<std::string, std::string> ad{{"KEY1", "VALUE1"}, | 
|  | 351 | {"KEY2", "VALUE2"}, | 
|  | 352 | {"KEY3", "VALUE3"}, | 
|  | 353 | {"ESEL", "TEST"}}; | 
| Matt Spinler | afa857c | 2019-10-24 13:03:46 -0500 | [diff] [blame] | 354 | AdditionalData additionalData{ad}; | 
|  | 355 |  | 
|  | 356 | auto ud = util::makeADUserDataSection(additionalData); | 
|  | 357 |  | 
|  | 358 | EXPECT_TRUE(ud->valid()); | 
|  | 359 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 360 | EXPECT_EQ(ud->header().version, 0x01); | 
|  | 361 | EXPECT_EQ(ud->header().subType, 0x01); | 
|  | 362 | EXPECT_EQ(ud->header().componentID, 0x2000); | 
|  | 363 |  | 
|  | 364 | const auto& d = ud->data(); | 
|  | 365 |  | 
|  | 366 | std::string jsonString{d.begin(), d.end()}; | 
| Matt Spinler | 53407be | 2019-11-18 09:16:31 -0600 | [diff] [blame] | 367 |  | 
|  | 368 | std::string expectedJSON = | 
| Matt Spinler | afa857c | 2019-10-24 13:03:46 -0500 | [diff] [blame] | 369 | R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})"; | 
| Matt Spinler | 53407be | 2019-11-18 09:16:31 -0600 | [diff] [blame] | 370 |  | 
|  | 371 | // The actual data is null padded to a 4B boundary. | 
|  | 372 | std::vector<uint8_t> expectedData; | 
|  | 373 | expectedData.resize(52, '\0'); | 
|  | 374 | memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size()); | 
|  | 375 |  | 
|  | 376 | EXPECT_EQ(d, expectedData); | 
| Matt Spinler | afa857c | 2019-10-24 13:03:46 -0500 | [diff] [blame] | 377 |  | 
|  | 378 | // Ensure we can read this as JSON | 
|  | 379 | auto newJSON = nlohmann::json::parse(jsonString); | 
|  | 380 | EXPECT_EQ(newJSON["KEY1"], "VALUE1"); | 
|  | 381 | EXPECT_EQ(newJSON["KEY2"], "VALUE2"); | 
|  | 382 | EXPECT_EQ(newJSON["KEY3"], "VALUE3"); | 
| Matt Spinler | 97d19b4 | 2019-10-29 11:34:03 -0500 | [diff] [blame] | 383 | } | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 384 |  | 
|  | 385 | // Create the UserData section that contains system info | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 386 | TEST_F(PELTest, SysInfoSectionTest) | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 387 | { | 
|  | 388 | MockDataInterface dataIface; | 
|  | 389 |  | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 390 | EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234")); | 
| Matt Spinler | 4aa23a1 | 2020-02-03 15:05:09 -0600 | [diff] [blame] | 391 | EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready")); | 
|  | 392 | EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On")); | 
|  | 393 | EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off")); | 
| Sumit Kumar | 2c36fdd | 2021-09-21 03:12:11 -0500 | [diff] [blame] | 394 | EXPECT_CALL(dataIface, getBootState()) | 
|  | 395 | .WillOnce(Return("State.SystemInitComplete")); | 
| Ben Tyner | e32b7e7 | 2021-05-18 12:38:40 -0500 | [diff] [blame] | 396 | EXPECT_CALL(dataIface, getSystemIMKeyword()) | 
|  | 397 | .WillOnce(Return(std::vector<uint8_t>{0, 1, 0x55, 0xAA})); | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 398 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 399 | std::map<std::string, std::string> ad{{"_PID", std::to_string(getpid())}}; | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 400 | AdditionalData additionalData{ad}; | 
|  | 401 |  | 
|  | 402 | auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); | 
|  | 403 |  | 
|  | 404 | EXPECT_TRUE(ud->valid()); | 
|  | 405 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 406 | EXPECT_EQ(ud->header().version, 0x01); | 
|  | 407 | EXPECT_EQ(ud->header().subType, 0x01); | 
|  | 408 | EXPECT_EQ(ud->header().componentID, 0x2000); | 
|  | 409 |  | 
|  | 410 | // Pull out the JSON data and check it. | 
|  | 411 | const auto& d = ud->data(); | 
|  | 412 | std::string jsonString{d.begin(), d.end()}; | 
|  | 413 | auto json = nlohmann::json::parse(jsonString); | 
|  | 414 |  | 
| Patrick Williams | d9f0d64 | 2021-04-21 15:43:21 -0500 | [diff] [blame] | 415 | // Ensure the 'Process Name' entry contains the name of this test | 
|  | 416 | // executable. | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 417 | auto name = json["Process Name"].get<std::string>(); | 
| Patrick Williams | d9f0d64 | 2021-04-21 15:43:21 -0500 | [diff] [blame] | 418 | auto found = (name.find("pel_test") != std::string::npos) || | 
|  | 419 | (name.find("test-openpower-pels-pel") != std::string::npos); | 
|  | 420 | EXPECT_TRUE(found); | 
|  | 421 | // @TODO(stwcx): remove 'pel_test' when removing autotools. | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 422 |  | 
| Matt Spinler | c2b8a51 | 2021-05-21 12:44:42 -0600 | [diff] [blame] | 423 | auto version = json["FW Version ID"].get<std::string>(); | 
| Matt Spinler | 677381b | 2020-01-23 10:04:29 -0600 | [diff] [blame] | 424 | EXPECT_EQ(version, "ABCD1234"); | 
| Matt Spinler | 4aa23a1 | 2020-02-03 15:05:09 -0600 | [diff] [blame] | 425 |  | 
|  | 426 | auto state = json["BMCState"].get<std::string>(); | 
|  | 427 | EXPECT_EQ(state, "Ready"); | 
|  | 428 |  | 
|  | 429 | state = json["ChassisState"].get<std::string>(); | 
|  | 430 | EXPECT_EQ(state, "On"); | 
|  | 431 |  | 
|  | 432 | state = json["HostState"].get<std::string>(); | 
|  | 433 | EXPECT_EQ(state, "Off"); | 
| Ben Tyner | e32b7e7 | 2021-05-18 12:38:40 -0500 | [diff] [blame] | 434 |  | 
| Sumit Kumar | 2c36fdd | 2021-09-21 03:12:11 -0500 | [diff] [blame] | 435 | state = json["BootState"].get<std::string>(); | 
|  | 436 | EXPECT_EQ(state, "SystemInitComplete"); | 
|  | 437 |  | 
| Ben Tyner | e32b7e7 | 2021-05-18 12:38:40 -0500 | [diff] [blame] | 438 | auto keyword = json["System IM"].get<std::string>(); | 
|  | 439 | EXPECT_EQ(keyword, "000155AA"); | 
| Matt Spinler | 4dcd3f4 | 2020-01-22 14:55:07 -0600 | [diff] [blame] | 440 | } | 
| Matt Spinler | ce3f450 | 2020-01-22 15:44:35 -0600 | [diff] [blame] | 441 |  | 
|  | 442 | // Test that the sections that override | 
|  | 443 | //     virtual std::optional<std::string> Section::getJSON() const | 
|  | 444 | // return valid JSON. | 
|  | 445 | TEST_F(PELTest, SectionJSONTest) | 
|  | 446 | { | 
|  | 447 | auto data = pelDataFactory(TestPELType::pelSimple); | 
|  | 448 | PEL pel{data}; | 
|  | 449 |  | 
|  | 450 | // Check that all JSON returned from the sections is | 
|  | 451 | // parseable by nlohmann::json, which will throw an | 
|  | 452 | // exception and fail the test if there is a problem. | 
|  | 453 |  | 
|  | 454 | // The getJSON() response needs to be wrapped in a { } to make | 
|  | 455 | // actual valid JSON (PEL::toJSON() usually handles that). | 
|  | 456 |  | 
| Matt Spinler | b832aa5 | 2023-03-21 15:32:34 -0500 | [diff] [blame] | 457 | auto jsonString = pel.privateHeader().getJSON('O'); | 
| Matt Spinler | ce3f450 | 2020-01-22 15:44:35 -0600 | [diff] [blame] | 458 |  | 
|  | 459 | // PrivateHeader always prints JSON | 
|  | 460 | ASSERT_TRUE(jsonString); | 
|  | 461 | *jsonString = '{' + *jsonString + '}'; | 
|  | 462 | auto json = nlohmann::json::parse(*jsonString); | 
|  | 463 |  | 
| Matt Spinler | b832aa5 | 2023-03-21 15:32:34 -0500 | [diff] [blame] | 464 | jsonString = pel.userHeader().getJSON('O'); | 
| Matt Spinler | ce3f450 | 2020-01-22 15:44:35 -0600 | [diff] [blame] | 465 |  | 
|  | 466 | // UserHeader always prints JSON | 
|  | 467 | ASSERT_TRUE(jsonString); | 
|  | 468 | *jsonString = '{' + *jsonString + '}'; | 
|  | 469 | json = nlohmann::json::parse(*jsonString); | 
|  | 470 |  | 
|  | 471 | for (const auto& section : pel.optionalSections()) | 
|  | 472 | { | 
|  | 473 | // The optional sections may or may not have implemented getJSON(). | 
| Matt Spinler | b832aa5 | 2023-03-21 15:32:34 -0500 | [diff] [blame] | 474 | jsonString = section->getJSON('O'); | 
| Matt Spinler | ce3f450 | 2020-01-22 15:44:35 -0600 | [diff] [blame] | 475 | if (jsonString) | 
|  | 476 | { | 
|  | 477 | *jsonString = '{' + *jsonString + '}'; | 
|  | 478 | auto json = nlohmann::json::parse(*jsonString); | 
|  | 479 | } | 
|  | 480 | } | 
|  | 481 | } | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 482 |  | 
|  | 483 | PelFFDCfile getJSONFFDC(const fs::path& dir) | 
|  | 484 | { | 
|  | 485 | PelFFDCfile ffdc; | 
|  | 486 | ffdc.format = UserDataFormat::json; | 
|  | 487 | ffdc.subType = 5; | 
|  | 488 | ffdc.version = 42; | 
|  | 489 |  | 
|  | 490 | auto inputJSON = R"({ | 
|  | 491 | "key1": "value1", | 
|  | 492 | "key2": 42, | 
|  | 493 | "key3" : [1, 2, 3, 4, 5], | 
|  | 494 | "key4": {"key5": "value5"} | 
|  | 495 | })"_json; | 
|  | 496 |  | 
|  | 497 | // Write the JSON to a file and get its descriptor. | 
|  | 498 | auto s = inputJSON.dump(); | 
|  | 499 | std::vector<uint8_t> data{s.begin(), s.end()}; | 
|  | 500 | ffdc.fd = writeFileAndGetFD(dir, data); | 
|  | 501 |  | 
|  | 502 | return ffdc; | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | TEST_F(PELTest, MakeJSONFileUDSectionTest) | 
|  | 506 | { | 
|  | 507 | auto dir = makeTempDir(); | 
|  | 508 |  | 
|  | 509 | { | 
|  | 510 | auto ffdc = getJSONFFDC(dir); | 
|  | 511 |  | 
|  | 512 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 513 | close(ffdc.fd); | 
|  | 514 | ASSERT_TRUE(ud); | 
|  | 515 | ASSERT_TRUE(ud->valid()); | 
|  | 516 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 517 |  | 
|  | 518 | EXPECT_EQ(ud->header().version, | 
|  | 519 | static_cast<uint8_t>(UserDataFormatVersion::json)); | 
|  | 520 | EXPECT_EQ(ud->header().subType, | 
|  | 521 | static_cast<uint8_t>(UserDataFormat::json)); | 
|  | 522 | EXPECT_EQ(ud->header().componentID, | 
|  | 523 | static_cast<uint16_t>(ComponentID::phosphorLogging)); | 
|  | 524 |  | 
|  | 525 | // Pull the JSON back out of the the UserData section | 
|  | 526 | const auto& d = ud->data(); | 
|  | 527 | std::string js{d.begin(), d.end()}; | 
|  | 528 | auto json = nlohmann::json::parse(js); | 
|  | 529 |  | 
|  | 530 | EXPECT_EQ("value1", json["key1"].get<std::string>()); | 
|  | 531 | EXPECT_EQ(42, json["key2"].get<int>()); | 
|  | 532 |  | 
|  | 533 | std::vector<int> key3Values{1, 2, 3, 4, 5}; | 
|  | 534 | EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>()); | 
|  | 535 |  | 
|  | 536 | std::map<std::string, std::string> key4Values{{"key5", "value5"}}; | 
|  | 537 | auto actual = json["key4"].get<std::map<std::string, std::string>>(); | 
|  | 538 | EXPECT_EQ(key4Values, actual); | 
|  | 539 | } | 
|  | 540 |  | 
|  | 541 | { | 
|  | 542 | // A bad FD | 
|  | 543 | PelFFDCfile ffdc; | 
|  | 544 | ffdc.format = UserDataFormat::json; | 
|  | 545 | ffdc.subType = 5; | 
|  | 546 | ffdc.version = 42; | 
|  | 547 | ffdc.fd = 10000; | 
|  | 548 |  | 
|  | 549 | // The section shouldn't get made | 
|  | 550 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 551 | ASSERT_FALSE(ud); | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | fs::remove_all(dir); | 
|  | 555 | } | 
|  | 556 |  | 
|  | 557 | PelFFDCfile getCBORFFDC(const fs::path& dir) | 
|  | 558 | { | 
|  | 559 | PelFFDCfile ffdc; | 
|  | 560 | ffdc.format = UserDataFormat::cbor; | 
|  | 561 | ffdc.subType = 5; | 
|  | 562 | ffdc.version = 42; | 
|  | 563 |  | 
|  | 564 | auto inputJSON = R"({ | 
|  | 565 | "key1": "value1", | 
|  | 566 | "key2": 42, | 
|  | 567 | "key3" : [1, 2, 3, 4, 5], | 
|  | 568 | "key4": {"key5": "value5"} | 
|  | 569 | })"_json; | 
|  | 570 |  | 
|  | 571 | // Convert the JSON to CBOR and write it to a file | 
|  | 572 | auto data = nlohmann::json::to_cbor(inputJSON); | 
|  | 573 | ffdc.fd = writeFileAndGetFD(dir, data); | 
|  | 574 |  | 
|  | 575 | return ffdc; | 
|  | 576 | } | 
|  | 577 |  | 
|  | 578 | TEST_F(PELTest, MakeCBORFileUDSectionTest) | 
|  | 579 | { | 
|  | 580 | auto dir = makeTempDir(); | 
|  | 581 |  | 
|  | 582 | auto ffdc = getCBORFFDC(dir); | 
|  | 583 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 584 | close(ffdc.fd); | 
|  | 585 | ASSERT_TRUE(ud); | 
|  | 586 | ASSERT_TRUE(ud->valid()); | 
|  | 587 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 588 |  | 
|  | 589 | EXPECT_EQ(ud->header().version, | 
|  | 590 | static_cast<uint8_t>(UserDataFormatVersion::cbor)); | 
|  | 591 | EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor)); | 
|  | 592 | EXPECT_EQ(ud->header().componentID, | 
|  | 593 | static_cast<uint16_t>(ComponentID::phosphorLogging)); | 
|  | 594 |  | 
|  | 595 | // Pull the CBOR back out of the PEL section | 
|  | 596 | // The number of pad bytes to make the section be 4B aligned | 
|  | 597 | // was added at the end, read it and then remove it and the | 
|  | 598 | // padding before parsing it. | 
|  | 599 | auto data = ud->data(); | 
|  | 600 | Stream stream{data}; | 
|  | 601 | stream.offset(data.size() - 4); | 
|  | 602 | uint32_t pad; | 
|  | 603 | stream >> pad; | 
|  | 604 |  | 
|  | 605 | data.resize(data.size() - 4 - pad); | 
|  | 606 |  | 
|  | 607 | auto json = nlohmann::json::from_cbor(data); | 
|  | 608 |  | 
|  | 609 | EXPECT_EQ("value1", json["key1"].get<std::string>()); | 
|  | 610 | EXPECT_EQ(42, json["key2"].get<int>()); | 
|  | 611 |  | 
|  | 612 | std::vector<int> key3Values{1, 2, 3, 4, 5}; | 
|  | 613 | EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>()); | 
|  | 614 |  | 
|  | 615 | std::map<std::string, std::string> key4Values{{"key5", "value5"}}; | 
|  | 616 | auto actual = json["key4"].get<std::map<std::string, std::string>>(); | 
|  | 617 | EXPECT_EQ(key4Values, actual); | 
|  | 618 |  | 
|  | 619 | fs::remove_all(dir); | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | PelFFDCfile getTextFFDC(const fs::path& dir) | 
|  | 623 | { | 
|  | 624 | PelFFDCfile ffdc; | 
|  | 625 | ffdc.format = UserDataFormat::text; | 
|  | 626 | ffdc.subType = 5; | 
|  | 627 | ffdc.version = 42; | 
|  | 628 |  | 
|  | 629 | std::string text{"this is some text that will be used for FFDC"}; | 
|  | 630 | std::vector<uint8_t> data{text.begin(), text.end()}; | 
|  | 631 |  | 
|  | 632 | ffdc.fd = writeFileAndGetFD(dir, data); | 
|  | 633 |  | 
|  | 634 | return ffdc; | 
|  | 635 | } | 
|  | 636 |  | 
|  | 637 | TEST_F(PELTest, MakeTextFileUDSectionTest) | 
|  | 638 | { | 
|  | 639 | auto dir = makeTempDir(); | 
|  | 640 |  | 
|  | 641 | auto ffdc = getTextFFDC(dir); | 
|  | 642 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 643 | close(ffdc.fd); | 
|  | 644 | ASSERT_TRUE(ud); | 
|  | 645 | ASSERT_TRUE(ud->valid()); | 
|  | 646 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 647 |  | 
|  | 648 | EXPECT_EQ(ud->header().version, | 
|  | 649 | static_cast<uint8_t>(UserDataFormatVersion::text)); | 
|  | 650 | EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text)); | 
|  | 651 | EXPECT_EQ(ud->header().componentID, | 
|  | 652 | static_cast<uint16_t>(ComponentID::phosphorLogging)); | 
|  | 653 |  | 
|  | 654 | // Get the text back out | 
|  | 655 | std::string text{ud->data().begin(), ud->data().end()}; | 
|  | 656 | EXPECT_EQ(text, "this is some text that will be used for FFDC"); | 
|  | 657 |  | 
|  | 658 | fs::remove_all(dir); | 
|  | 659 | } | 
|  | 660 |  | 
|  | 661 | PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data) | 
|  | 662 | { | 
|  | 663 | PelFFDCfile ffdc; | 
|  | 664 | ffdc.format = UserDataFormat::custom; | 
|  | 665 | ffdc.subType = 5; | 
|  | 666 | ffdc.version = 42; | 
|  | 667 |  | 
|  | 668 | ffdc.fd = writeFileAndGetFD(dir, data); | 
|  | 669 |  | 
|  | 670 | return ffdc; | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | TEST_F(PELTest, MakeCustomFileUDSectionTest) | 
|  | 674 | { | 
|  | 675 | auto dir = makeTempDir(); | 
|  | 676 |  | 
|  | 677 | { | 
|  | 678 | std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8}; | 
|  | 679 |  | 
|  | 680 | auto ffdc = getCustomFFDC(dir, data); | 
|  | 681 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 682 | close(ffdc.fd); | 
|  | 683 | ASSERT_TRUE(ud); | 
|  | 684 | ASSERT_TRUE(ud->valid()); | 
|  | 685 | EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size | 
|  | 686 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 687 |  | 
|  | 688 | EXPECT_EQ(ud->header().version, 42); | 
|  | 689 | EXPECT_EQ(ud->header().subType, 5); | 
|  | 690 | EXPECT_EQ(ud->header().componentID, 0x2002); | 
|  | 691 |  | 
|  | 692 | // Get the data back out | 
|  | 693 | std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()}; | 
|  | 694 | EXPECT_EQ(data, newData); | 
|  | 695 | } | 
|  | 696 |  | 
|  | 697 | // Do the same thing again, but make it be non 4B aligned | 
|  | 698 | // so the data gets padded. | 
|  | 699 | { | 
|  | 700 | std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9}; | 
|  | 701 |  | 
|  | 702 | auto ffdc = getCustomFFDC(dir, data); | 
|  | 703 | auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); | 
|  | 704 | close(ffdc.fd); | 
|  | 705 | ASSERT_TRUE(ud); | 
|  | 706 | ASSERT_TRUE(ud->valid()); | 
|  | 707 | EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size | 
|  | 708 | EXPECT_EQ(ud->header().id, 0x5544); | 
|  | 709 |  | 
|  | 710 | EXPECT_EQ(ud->header().version, 42); | 
|  | 711 | EXPECT_EQ(ud->header().subType, 5); | 
|  | 712 | EXPECT_EQ(ud->header().componentID, 0x2002); | 
|  | 713 |  | 
|  | 714 | // Get the data back out | 
|  | 715 | std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()}; | 
|  | 716 |  | 
|  | 717 | // pad the original to 12B so we can compare | 
|  | 718 | data.push_back(0); | 
|  | 719 | data.push_back(0); | 
|  | 720 | data.push_back(0); | 
|  | 721 |  | 
|  | 722 | EXPECT_EQ(data, newData); | 
|  | 723 | } | 
|  | 724 |  | 
|  | 725 | fs::remove_all(dir); | 
|  | 726 | } | 
|  | 727 |  | 
|  | 728 | // Test Adding FFDC from files to a PEL | 
|  | 729 | TEST_F(PELTest, CreateWithFFDCTest) | 
|  | 730 | { | 
|  | 731 | auto dir = makeTempDir(); | 
|  | 732 | message::Entry regEntry; | 
|  | 733 | uint64_t timestamp = 5; | 
|  | 734 |  | 
|  | 735 | regEntry.name = "test"; | 
|  | 736 | regEntry.subsystem = 5; | 
|  | 737 | regEntry.actionFlags = 0xC000; | 
|  | 738 | regEntry.src.type = 0xBD; | 
|  | 739 | regEntry.src.reasonCode = 0x1234; | 
|  | 740 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 741 | std::map<std::string, std::string> additionalData{{"KEY1", "VALUE1"}}; | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 742 | AdditionalData ad{additionalData}; | 
|  | 743 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 744 | NiceMock<MockJournal> journal; | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 745 | PelFFDC ffdc; | 
|  | 746 |  | 
|  | 747 | std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8}; | 
|  | 748 |  | 
|  | 749 | // This will be trimmed when added | 
|  | 750 | std::vector<uint8_t> hugeCustomData(17000, 0x42); | 
|  | 751 |  | 
| Matt Spinler | f904caf | 2025-05-09 11:46:45 -0500 | [diff] [blame] | 752 | ffdc.emplace_back(getJSONFFDC(dir)); | 
|  | 753 | ffdc.emplace_back(getCBORFFDC(dir)); | 
|  | 754 | ffdc.emplace_back(getTextFFDC(dir)); | 
|  | 755 | ffdc.emplace_back(getCustomFFDC(dir, customData)); | 
|  | 756 | ffdc.emplace_back(getCustomFFDC(dir, hugeCustomData)); | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 757 |  | 
|  | 758 | PEL pel{regEntry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 759 | ad,       ffdc, dataIface, journal}; | 
| Matt Spinler | 5b289b2 | 2020-03-26 14:27:19 -0500 | [diff] [blame] | 760 |  | 
|  | 761 | EXPECT_TRUE(pel.valid()); | 
|  | 762 |  | 
|  | 763 | // Clipped to the max | 
|  | 764 | EXPECT_EQ(pel.size(), 16384); | 
|  | 765 |  | 
|  | 766 | // Check for the FFDC sections | 
|  | 767 | size_t udCount = 0; | 
|  | 768 | Section* ud = nullptr; | 
|  | 769 |  | 
|  | 770 | for (const auto& section : pel.optionalSections()) | 
|  | 771 | { | 
|  | 772 | if (section->header().id == static_cast<uint16_t>(SectionID::userData)) | 
|  | 773 | { | 
|  | 774 | udCount++; | 
|  | 775 | ud = section.get(); | 
|  | 776 | } | 
|  | 777 | } | 
|  | 778 |  | 
|  | 779 | EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections | 
|  | 780 |  | 
|  | 781 | // Check the last section was trimmed to | 
|  | 782 | // something a bit less that 17000. | 
|  | 783 | EXPECT_GT(ud->header().size, 14000); | 
|  | 784 | EXPECT_LT(ud->header().size, 16000); | 
|  | 785 |  | 
|  | 786 | fs::remove_all(dir); | 
|  | 787 | } | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 788 |  | 
|  | 789 | // Create a PEL with device callouts | 
|  | 790 | TEST_F(PELTest, CreateWithDevCalloutsTest) | 
|  | 791 | { | 
|  | 792 | message::Entry regEntry; | 
|  | 793 | uint64_t timestamp = 5; | 
|  | 794 |  | 
|  | 795 | regEntry.name = "test"; | 
|  | 796 | regEntry.subsystem = 5; | 
|  | 797 | regEntry.actionFlags = 0xC000; | 
|  | 798 | regEntry.src.type = 0xBD; | 
|  | 799 | regEntry.src.reasonCode = 0x1234; | 
|  | 800 |  | 
|  | 801 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 802 | NiceMock<MockJournal> journal; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 803 | PelFFDC ffdc; | 
|  | 804 |  | 
|  | 805 | const auto calloutJSON = R"( | 
|  | 806 | { | 
|  | 807 | "I2C": | 
|  | 808 | { | 
|  | 809 | "14": | 
|  | 810 | { | 
|  | 811 | "114": | 
|  | 812 | { | 
|  | 813 | "Callouts":[ | 
|  | 814 | { | 
|  | 815 | "Name": "/chassis/motherboard/cpu0", | 
|  | 816 | "LocationCode": "P1", | 
|  | 817 | "Priority": "H" | 
|  | 818 | } | 
|  | 819 | ], | 
|  | 820 | "Dest": "proc 0 target" | 
|  | 821 | } | 
|  | 822 | } | 
|  | 823 | } | 
|  | 824 | })"; | 
|  | 825 |  | 
|  | 826 | std::vector<std::string> names{"systemA"}; | 
|  | 827 | EXPECT_CALL(dataIface, getSystemNames) | 
|  | 828 | .Times(2) | 
| Matt Spinler | 1ab6696 | 2020-10-29 13:21:44 -0500 | [diff] [blame] | 829 | .WillRepeatedly(Return(names)); | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 830 |  | 
| Matt Spinler | 0d92b52 | 2021-06-16 13:28:17 -0600 | [diff] [blame] | 831 | EXPECT_CALL(dataIface, expandLocationCode("P1", 0)) | 
|  | 832 | .Times(1) | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 833 | .WillOnce(Return("UXXX-P1")); | 
|  | 834 |  | 
| Matt Spinler | 2f9225a | 2020-08-05 12:58:49 -0500 | [diff] [blame] | 835 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false)) | 
| Matt Spinler | bad056b | 2023-01-25 14:16:57 -0600 | [diff] [blame] | 836 | .WillOnce(Return(std::vector<std::string>{ | 
|  | 837 | "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"})); | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 838 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 839 | EXPECT_CALL(dataIface, | 
|  | 840 | getHWCalloutFields( | 
|  | 841 | "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", | 
|  | 842 | _, _, _)) | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 843 | .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), | 
|  | 844 | SetArgReferee<3>("123456789ABC"))); | 
|  | 845 |  | 
|  | 846 | auto dataPath = getPELReadOnlyDataPath(); | 
|  | 847 | std::ofstream file{dataPath / "systemA_dev_callouts.json"}; | 
|  | 848 | file << calloutJSON; | 
|  | 849 | file.close(); | 
|  | 850 |  | 
|  | 851 | { | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 852 | std::map<std::string, std::string> data{ | 
|  | 853 | {"CALLOUT_ERRNO", "5"}, | 
|  | 854 | {"CALLOUT_DEVICE_PATH", | 
|  | 855 | "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}}; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 856 |  | 
|  | 857 | AdditionalData ad{data}; | 
|  | 858 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 859 | PEL pel{regEntry,  42, | 
|  | 860 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 861 | ad,        ffdc, | 
|  | 862 | dataIface, journal}; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 863 |  | 
|  | 864 | ASSERT_TRUE(pel.primarySRC().value()->callouts()); | 
|  | 865 | auto& callouts = pel.primarySRC().value()->callouts()->callouts(); | 
|  | 866 | ASSERT_EQ(callouts.size(), 1); | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 867 | ASSERT_TRUE(pel.isHwCalloutPresent()); | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 868 |  | 
|  | 869 | EXPECT_EQ(callouts[0]->priority(), 'H'); | 
|  | 870 | EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1"); | 
|  | 871 |  | 
|  | 872 | auto& fru = callouts[0]->fruIdentity(); | 
|  | 873 | EXPECT_EQ(fru->getPN().value(), "1234567"); | 
|  | 874 | EXPECT_EQ(fru->getCCIN().value(), "CCCC"); | 
|  | 875 | EXPECT_EQ(fru->getSN().value(), "123456789ABC"); | 
|  | 876 |  | 
|  | 877 | const auto& section = pel.optionalSections().back(); | 
|  | 878 |  | 
|  | 879 | ASSERT_EQ(section->header().id, 0x5544); // UD | 
|  | 880 | auto ud = static_cast<UserData*>(section.get()); | 
|  | 881 |  | 
|  | 882 | // Check that there was a UserData section added that | 
|  | 883 | // contains debug details about the device. | 
|  | 884 | const auto& d = ud->data(); | 
|  | 885 | std::string jsonString{d.begin(), d.end()}; | 
|  | 886 | auto actualJSON = nlohmann::json::parse(jsonString); | 
|  | 887 |  | 
|  | 888 | auto expectedJSON = R"( | 
|  | 889 | { | 
|  | 890 | "PEL Internal Debug Data": { | 
|  | 891 | "SRC": [ | 
|  | 892 | "I2C: bus: 14 address: 114 dest: proc 0 target" | 
|  | 893 | ] | 
|  | 894 | } | 
|  | 895 | } | 
|  | 896 | )"_json; | 
|  | 897 |  | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 898 | EXPECT_TRUE( | 
|  | 899 | actualJSON.contains("/PEL Internal Debug Data/SRC"_json_pointer)); | 
|  | 900 | EXPECT_EQ(actualJSON["PEL Internal Debug Data"]["SRC"], | 
|  | 901 | expectedJSON["PEL Internal Debug Data"]["SRC"]); | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 902 | } | 
|  | 903 |  | 
|  | 904 | { | 
|  | 905 | // Device path not found (wrong i2c addr), so no callouts | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 906 | std::map<std::string, std::string> data{ | 
|  | 907 | {"CALLOUT_ERRNO", "5"}, | 
|  | 908 | {"CALLOUT_DEVICE_PATH", | 
|  | 909 | "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0099"}}; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 910 |  | 
|  | 911 | AdditionalData ad{data}; | 
|  | 912 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 913 | PEL pel{regEntry,  42, | 
|  | 914 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 915 | ad,        ffdc, | 
|  | 916 | dataIface, journal}; | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 917 |  | 
|  | 918 | // no callouts | 
|  | 919 | EXPECT_FALSE(pel.primarySRC().value()->callouts()); | 
|  | 920 |  | 
|  | 921 | // Now check that there was a UserData section | 
|  | 922 | // that contains the lookup error. | 
|  | 923 | const auto& section = pel.optionalSections().back(); | 
|  | 924 |  | 
|  | 925 | ASSERT_EQ(section->header().id, 0x5544); // UD | 
|  | 926 | auto ud = static_cast<UserData*>(section.get()); | 
|  | 927 |  | 
|  | 928 | const auto& d = ud->data(); | 
|  | 929 |  | 
|  | 930 | std::string jsonString{d.begin(), d.end()}; | 
|  | 931 |  | 
|  | 932 | auto actualJSON = nlohmann::json::parse(jsonString); | 
|  | 933 |  | 
|  | 934 | auto expectedJSON = | 
|  | 935 | "{\"PEL Internal Debug Data\":{\"SRC\":" | 
|  | 936 | "[\"Problem looking up I2C callouts on 14 153: " | 
|  | 937 | "[json.exception.out_of_range.403] key '153' not found\"]}}"_json; | 
|  | 938 |  | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 939 | EXPECT_TRUE( | 
|  | 940 | actualJSON.contains("/PEL Internal Debug Data/SRC"_json_pointer)); | 
|  | 941 | EXPECT_EQ(actualJSON["PEL Internal Debug Data"]["SRC"], | 
|  | 942 | expectedJSON["PEL Internal Debug Data"]["SRC"]); | 
| Matt Spinler | 0a90a85 | 2020-06-04 13:18:27 -0500 | [diff] [blame] | 943 | } | 
|  | 944 |  | 
|  | 945 | fs::remove_all(dataPath); | 
|  | 946 | } | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 947 |  | 
|  | 948 | // Test PELs when the callouts are passed in using a JSON file. | 
|  | 949 | TEST_F(PELTest, CreateWithJSONCalloutsTest) | 
|  | 950 | { | 
|  | 951 | PelFFDCfile ffdcFile; | 
|  | 952 | ffdcFile.format = UserDataFormat::json; | 
|  | 953 | ffdcFile.subType = 0xCA; // Callout JSON | 
|  | 954 | ffdcFile.version = 1; | 
|  | 955 |  | 
|  | 956 | // Write these callouts to a JSON file and pass it into | 
| Matt Spinler | 4efed0e | 2024-02-26 11:16:07 -0600 | [diff] [blame] | 957 | // the PEL as an FFDC file. Also has a duplicate that | 
|  | 958 | // will be removed. | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 959 | auto inputJSON = R"([ | 
|  | 960 | { | 
|  | 961 | "Priority": "H", | 
|  | 962 | "LocationCode": "P0-C1" | 
|  | 963 | }, | 
|  | 964 | { | 
|  | 965 | "Priority": "M", | 
|  | 966 | "Procedure": "PROCEDURE" | 
| Matt Spinler | 4efed0e | 2024-02-26 11:16:07 -0600 | [diff] [blame] | 967 | }, | 
|  | 968 | { | 
|  | 969 | "Priority": "L", | 
|  | 970 | "Procedure": "PROCEDURE" | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 971 | } | 
|  | 972 | ])"_json; | 
|  | 973 |  | 
|  | 974 | auto s = inputJSON.dump(); | 
|  | 975 | std::vector<uint8_t> data{s.begin(), s.end()}; | 
|  | 976 | auto dir = makeTempDir(); | 
|  | 977 | ffdcFile.fd = writeFileAndGetFD(dir, data); | 
|  | 978 |  | 
|  | 979 | PelFFDC ffdc; | 
|  | 980 | ffdc.push_back(std::move(ffdcFile)); | 
|  | 981 |  | 
|  | 982 | AdditionalData ad; | 
|  | 983 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 984 | NiceMock<MockJournal> journal; | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 985 |  | 
|  | 986 | EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0)) | 
|  | 987 | .Times(1) | 
|  | 988 | .WillOnce(Return("UXXX-P0-C1")); | 
|  | 989 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false)) | 
|  | 990 | .Times(1) | 
| Matt Spinler | bad056b | 2023-01-25 14:16:57 -0600 | [diff] [blame] | 991 | .WillOnce(Return( | 
|  | 992 | std::vector<std::string>{"/inv/system/chassis/motherboard/bmc"})); | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 993 | EXPECT_CALL(dataIface, getHWCalloutFields( | 
|  | 994 | "/inv/system/chassis/motherboard/bmc", _, _, _)) | 
|  | 995 | .Times(1) | 
|  | 996 | .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"), | 
|  | 997 | SetArgReferee<3>("123456789ABC"))); | 
|  | 998 |  | 
|  | 999 | message::Entry regEntry; | 
|  | 1000 | regEntry.name = "test"; | 
|  | 1001 | regEntry.subsystem = 5; | 
|  | 1002 | regEntry.actionFlags = 0xC000; | 
|  | 1003 | regEntry.src.type = 0xBD; | 
|  | 1004 | regEntry.src.reasonCode = 0x1234; | 
|  | 1005 |  | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1006 | PEL pel{regEntry, 42,   5,         phosphor::logging::Entry::Level::Error, | 
|  | 1007 | ad,       ffdc, dataIface, journal}; | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 1008 |  | 
|  | 1009 | ASSERT_TRUE(pel.valid()); | 
|  | 1010 | ASSERT_TRUE(pel.primarySRC().value()->callouts()); | 
|  | 1011 | const auto& callouts = pel.primarySRC().value()->callouts()->callouts(); | 
|  | 1012 | ASSERT_EQ(callouts.size(), 2); | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 1013 | ASSERT_TRUE(pel.isHwCalloutPresent()); | 
| Matt Spinler | e513dbc | 2020-08-27 11:14:17 -0500 | [diff] [blame] | 1014 |  | 
|  | 1015 | { | 
|  | 1016 | EXPECT_EQ(callouts[0]->priority(), 'H'); | 
|  | 1017 | EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1"); | 
|  | 1018 |  | 
|  | 1019 | auto& fru = callouts[0]->fruIdentity(); | 
|  | 1020 | EXPECT_EQ(fru->getPN().value(), "1234567"); | 
|  | 1021 | EXPECT_EQ(fru->getCCIN().value(), "CCCC"); | 
|  | 1022 | EXPECT_EQ(fru->getSN().value(), "123456789ABC"); | 
|  | 1023 | EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU); | 
|  | 1024 | } | 
|  | 1025 | { | 
|  | 1026 | EXPECT_EQ(callouts[1]->priority(), 'M'); | 
|  | 1027 | EXPECT_EQ(callouts[1]->locationCode(), ""); | 
|  | 1028 |  | 
|  | 1029 | auto& fru = callouts[1]->fruIdentity(); | 
|  | 1030 | EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU"); | 
|  | 1031 | EXPECT_EQ(fru->failingComponentType(), | 
|  | 1032 | src::FRUIdentity::maintenanceProc); | 
|  | 1033 | } | 
|  | 1034 | fs::remove_all(dir); | 
|  | 1035 | } | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 1036 |  | 
|  | 1037 | // Test PELs with symblic FRU callout. | 
|  | 1038 | TEST_F(PELTest, CreateWithJSONSymblicCalloutTest) | 
|  | 1039 | { | 
|  | 1040 | PelFFDCfile ffdcFile; | 
|  | 1041 | ffdcFile.format = UserDataFormat::json; | 
|  | 1042 | ffdcFile.subType = 0xCA; // Callout JSON | 
|  | 1043 | ffdcFile.version = 1; | 
|  | 1044 |  | 
|  | 1045 | // Write these callouts to a JSON file and pass it into | 
|  | 1046 | // the PEL as an FFDC file. | 
|  | 1047 | auto inputJSON = R"([ | 
|  | 1048 | { | 
|  | 1049 | "Priority": "M", | 
|  | 1050 | "Procedure": "SVCDOCS" | 
|  | 1051 | } | 
|  | 1052 | ])"_json; | 
|  | 1053 |  | 
|  | 1054 | auto s = inputJSON.dump(); | 
|  | 1055 | std::vector<uint8_t> data{s.begin(), s.end()}; | 
|  | 1056 | auto dir = makeTempDir(); | 
|  | 1057 | ffdcFile.fd = writeFileAndGetFD(dir, data); | 
|  | 1058 |  | 
|  | 1059 | PelFFDC ffdc; | 
|  | 1060 | ffdc.push_back(std::move(ffdcFile)); | 
|  | 1061 |  | 
|  | 1062 | AdditionalData ad; | 
|  | 1063 | NiceMock<MockDataInterface> dataIface; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1064 | NiceMock<MockJournal> journal; | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 1065 |  | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 1066 | message::Entry regEntry; | 
|  | 1067 | regEntry.name = "test"; | 
|  | 1068 | regEntry.subsystem = 5; | 
|  | 1069 | regEntry.actionFlags = 0xC000; | 
|  | 1070 | regEntry.src.type = 0xBD; | 
|  | 1071 | regEntry.src.reasonCode = 0x1234; | 
|  | 1072 |  | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1073 | PEL pel{regEntry, 42,   5,         phosphor::logging::Entry::Level::Error, | 
|  | 1074 | ad,       ffdc, dataIface, journal}; | 
| Andrew Geissler | f8e750d | 2022-01-14 14:56:13 -0600 | [diff] [blame] | 1075 |  | 
|  | 1076 | ASSERT_TRUE(pel.valid()); | 
|  | 1077 | ASSERT_TRUE(pel.primarySRC().value()->callouts()); | 
|  | 1078 | const auto& callouts = pel.primarySRC().value()->callouts()->callouts(); | 
|  | 1079 | ASSERT_EQ(callouts.size(), 1); | 
|  | 1080 | ASSERT_FALSE(pel.isHwCalloutPresent()); | 
|  | 1081 |  | 
|  | 1082 | { | 
|  | 1083 | EXPECT_EQ(callouts[0]->priority(), 'M'); | 
|  | 1084 | EXPECT_EQ(callouts[0]->locationCode(), ""); | 
|  | 1085 |  | 
|  | 1086 | auto& fru = callouts[0]->fruIdentity(); | 
|  | 1087 | EXPECT_EQ(fru->getMaintProc().value(), "SVCDOCS"); | 
|  | 1088 | } | 
|  | 1089 | fs::remove_all(dir); | 
|  | 1090 | } | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1091 |  | 
|  | 1092 | TEST_F(PELTest, FlattenLinesTest) | 
|  | 1093 | { | 
|  | 1094 | std::vector<std::string> msgs{"test1 test2", "test3 test4", "test5 test6"}; | 
|  | 1095 |  | 
|  | 1096 | auto buffer = util::flattenLines(msgs); | 
|  | 1097 |  | 
|  | 1098 | std::string string{"test1 test2\ntest3 test4\ntest5 test6\n"}; | 
|  | 1099 | std::vector<uint8_t> expected(string.begin(), string.end()); | 
|  | 1100 |  | 
|  | 1101 | EXPECT_EQ(buffer, expected); | 
|  | 1102 | } | 
|  | 1103 |  | 
|  | 1104 | void checkJournalSection(const std::unique_ptr<Section>& section, | 
|  | 1105 | const std::string& expected) | 
|  | 1106 | { | 
|  | 1107 | ASSERT_EQ(SectionID::userData, | 
|  | 1108 | static_cast<SectionID>(section->header().id)); | 
|  | 1109 | ASSERT_EQ(UserDataFormat::text, | 
|  | 1110 | static_cast<UserDataFormat>(section->header().subType)); | 
|  | 1111 | ASSERT_EQ(section->header().version, | 
|  | 1112 | static_cast<uint8_t>(UserDataFormatVersion::text)); | 
|  | 1113 |  | 
|  | 1114 | auto ud = static_cast<UserData*>(section.get()); | 
|  | 1115 |  | 
|  | 1116 | std::vector<uint8_t> expectedData(expected.begin(), expected.end()); | 
|  | 1117 |  | 
|  | 1118 | // PEL sections are 4B aligned so add padding before the compare | 
|  | 1119 | while (expectedData.size() % 4 != 0) | 
|  | 1120 | { | 
|  | 1121 | expectedData.push_back('\0'); | 
|  | 1122 | } | 
|  | 1123 |  | 
|  | 1124 | EXPECT_EQ(ud->data(), expectedData); | 
|  | 1125 | } | 
|  | 1126 |  | 
|  | 1127 | TEST_F(PELTest, CaptureJournalTest) | 
|  | 1128 | { | 
|  | 1129 | message::Entry regEntry; | 
|  | 1130 | uint64_t timestamp = 5; | 
|  | 1131 |  | 
|  | 1132 | regEntry.name = "test"; | 
|  | 1133 | regEntry.subsystem = 5; | 
|  | 1134 | regEntry.actionFlags = 0xC000; | 
|  | 1135 | regEntry.src.type = 0xBD; | 
|  | 1136 | regEntry.src.reasonCode = 0x1234; | 
|  | 1137 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 1138 | std::map<std::string, std::string> data{}; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1139 | AdditionalData ad{data}; | 
|  | 1140 | NiceMock<MockDataInterface> dataIface; | 
|  | 1141 | NiceMock<MockJournal> journal; | 
|  | 1142 | PelFFDC ffdc; | 
|  | 1143 |  | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1144 | size_t pelSectsWithOneUD{0}; | 
|  | 1145 |  | 
|  | 1146 | { | 
|  | 1147 | // Capture 5 lines from the journal into a single UD section | 
|  | 1148 | message::JournalCapture jc = size_t{5}; | 
|  | 1149 | regEntry.journalCapture = jc; | 
|  | 1150 |  | 
|  | 1151 | std::vector<std::string> msgs{"test1 test2", "test3 test4", | 
|  | 1152 | "test5 test6", "4", "5"}; | 
|  | 1153 |  | 
|  | 1154 | EXPECT_CALL(journal, getMessages("", 5)).WillOnce(Return(msgs)); | 
|  | 1155 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 1156 | PEL pel{regEntry,  42, | 
|  | 1157 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1158 | ad,        ffdc, | 
|  | 1159 | dataIface, journal}; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1160 |  | 
|  | 1161 | // Check the generated UserData section | 
|  | 1162 | std::string expected{"test1 test2\ntest3 test4\ntest5 test6\n4\n5\n"}; | 
|  | 1163 |  | 
|  | 1164 | checkJournalSection(pel.optionalSections().back(), expected); | 
|  | 1165 |  | 
|  | 1166 | // Save for upcoming testcases | 
|  | 1167 | pelSectsWithOneUD = pel.privateHeader().sectionCount(); | 
|  | 1168 | } | 
|  | 1169 |  | 
|  | 1170 | { | 
|  | 1171 | // Attempt to capture too many journal entries so the | 
|  | 1172 | // section gets dropped. | 
|  | 1173 | message::JournalCapture jc = size_t{1}; | 
|  | 1174 | regEntry.journalCapture = jc; | 
|  | 1175 |  | 
|  | 1176 | EXPECT_CALL(journal, sync()).Times(1); | 
|  | 1177 |  | 
|  | 1178 | // A 20000 byte line won't fit in a PEL | 
|  | 1179 | EXPECT_CALL(journal, getMessages("", 1)) | 
|  | 1180 | .WillOnce( | 
|  | 1181 | Return(std::vector<std::string>{std::string(20000, 'x')})); | 
|  | 1182 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 1183 | PEL pel{regEntry,  42, | 
|  | 1184 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1185 | ad,        ffdc, | 
|  | 1186 | dataIface, journal}; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1187 |  | 
|  | 1188 | // Check for 1 fewer sections than in the previous PEL | 
|  | 1189 | EXPECT_EQ(pel.privateHeader().sectionCount(), pelSectsWithOneUD - 1); | 
|  | 1190 | } | 
|  | 1191 |  | 
|  | 1192 | // Capture 3 different journal sections | 
|  | 1193 | { | 
|  | 1194 | message::AppCaptureList captureList{ | 
|  | 1195 | message::AppCapture{"app1", 3}, | 
|  | 1196 | message::AppCapture{"app2", 4}, | 
|  | 1197 | message::AppCapture{"app3", 1}, | 
|  | 1198 | }; | 
|  | 1199 | message::JournalCapture jc = captureList; | 
|  | 1200 | regEntry.journalCapture = jc; | 
|  | 1201 |  | 
|  | 1202 | std::vector<std::string> app1{"A B", "C D", "E F"}; | 
|  | 1203 | std::vector<std::string> app2{"1 2", "3 4", "5 6", "7 8"}; | 
|  | 1204 | std::vector<std::string> app3{"a b c"}; | 
|  | 1205 |  | 
|  | 1206 | std::string expected1{"A B\nC D\nE F\n"}; | 
|  | 1207 | std::string expected2{"1 2\n3 4\n5 6\n7 8\n"}; | 
|  | 1208 | std::string expected3{"a b c\n"}; | 
|  | 1209 |  | 
|  | 1210 | EXPECT_CALL(journal, sync()).Times(1); | 
|  | 1211 | EXPECT_CALL(journal, getMessages("app1", 3)).WillOnce(Return(app1)); | 
|  | 1212 | EXPECT_CALL(journal, getMessages("app2", 4)).WillOnce(Return(app2)); | 
|  | 1213 | EXPECT_CALL(journal, getMessages("app3", 1)).WillOnce(Return(app3)); | 
|  | 1214 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 1215 | PEL pel{regEntry,  42, | 
|  | 1216 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1217 | ad,        ffdc, | 
|  | 1218 | dataIface, journal}; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1219 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 1220 | // Two more sections than the 1 extra UD section in the first | 
|  | 1221 | // testcase | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1222 | ASSERT_EQ(pel.privateHeader().sectionCount(), pelSectsWithOneUD + 2); | 
|  | 1223 |  | 
|  | 1224 | const auto& optionalSections = pel.optionalSections(); | 
|  | 1225 | auto numOptSections = optionalSections.size(); | 
|  | 1226 |  | 
|  | 1227 | checkJournalSection(optionalSections[numOptSections - 3], expected1); | 
|  | 1228 | checkJournalSection(optionalSections[numOptSections - 2], expected2); | 
|  | 1229 | checkJournalSection(optionalSections[numOptSections - 1], expected3); | 
|  | 1230 | } | 
|  | 1231 |  | 
|  | 1232 | { | 
|  | 1233 | // One section gets saved, and one is too big and gets dropped | 
|  | 1234 | message::AppCaptureList captureList{ | 
|  | 1235 | message::AppCapture{"app4", 2}, | 
|  | 1236 | message::AppCapture{"app5", 1}, | 
|  | 1237 | }; | 
|  | 1238 | message::JournalCapture jc = captureList; | 
|  | 1239 | regEntry.journalCapture = jc; | 
|  | 1240 |  | 
|  | 1241 | std::vector<std::string> app4{"w x", "y z"}; | 
|  | 1242 | std::string expected4{"w x\ny z\n"}; | 
|  | 1243 |  | 
|  | 1244 | EXPECT_CALL(journal, sync()).Times(1); | 
|  | 1245 |  | 
|  | 1246 | EXPECT_CALL(journal, getMessages("app4", 2)).WillOnce(Return(app4)); | 
|  | 1247 |  | 
|  | 1248 | // A 20000 byte line won't fit in a PEL | 
|  | 1249 | EXPECT_CALL(journal, getMessages("app5", 1)) | 
|  | 1250 | .WillOnce( | 
|  | 1251 | Return(std::vector<std::string>{std::string(20000, 'x')})); | 
|  | 1252 |  | 
| Patrick Williams | 075c792 | 2024-08-16 15:19:49 -0400 | [diff] [blame] | 1253 | PEL pel{regEntry,  42, | 
|  | 1254 | timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1255 | ad,        ffdc, | 
|  | 1256 | dataIface, journal}; | 
| Matt Spinler | 9d92109 | 2022-12-15 11:54:49 -0600 | [diff] [blame] | 1257 |  | 
|  | 1258 | // The last section should have been dropped, so same as first TC | 
|  | 1259 | ASSERT_EQ(pel.privateHeader().sectionCount(), pelSectsWithOneUD); | 
|  | 1260 |  | 
|  | 1261 | checkJournalSection(pel.optionalSections().back(), expected4); | 
|  | 1262 | } | 
|  | 1263 | } | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1264 |  | 
|  | 1265 | // API to collect and parse the User Data section of the PEL. | 
|  | 1266 | nlohmann::json getDIMMInfo(const auto& pel) | 
|  | 1267 | { | 
|  | 1268 | nlohmann::json dimmInfo{}; | 
|  | 1269 | auto hasDIMMInfo = [&dimmInfo](const auto& optionalSection) { | 
|  | 1270 | if (optionalSection->header().id != | 
|  | 1271 | static_cast<uint16_t>(SectionID::userData)) | 
|  | 1272 | { | 
|  | 1273 | return false; | 
|  | 1274 | } | 
|  | 1275 | else | 
|  | 1276 | { | 
|  | 1277 | auto userData = static_cast<UserData*>(optionalSection.get()); | 
|  | 1278 |  | 
| Patrick Williams | e594063 | 2024-11-22 20:47:58 -0500 | [diff] [blame] | 1279 | // convert the userdata section to string and then parse in to | 
|  | 1280 | // json format | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1281 | std::string userDataString{userData->data().begin(), | 
|  | 1282 | userData->data().end()}; | 
|  | 1283 | nlohmann::json userDataJson = nlohmann::json::parse(userDataString); | 
|  | 1284 |  | 
|  | 1285 | if (userDataJson.contains("DIMMs Additional Info")) | 
|  | 1286 | { | 
|  | 1287 | dimmInfo = userDataJson.at("DIMMs Additional Info"); | 
|  | 1288 | } | 
|  | 1289 | else if ( | 
|  | 1290 | userDataJson.contains( | 
|  | 1291 | "/PEL Internal Debug Data/DIMMs Info Fetch Error"_json_pointer)) | 
|  | 1292 | { | 
|  | 1293 | dimmInfo = userDataJson.at( | 
|  | 1294 | "/PEL Internal Debug Data/DIMMs Info Fetch Error"_json_pointer); | 
|  | 1295 | } | 
|  | 1296 | else | 
|  | 1297 | { | 
|  | 1298 | return false; | 
|  | 1299 | } | 
|  | 1300 | return true; | 
|  | 1301 | } | 
|  | 1302 | }; | 
|  | 1303 | std::ranges::any_of(pel.optionalSections(), hasDIMMInfo); | 
|  | 1304 |  | 
|  | 1305 | return dimmInfo; | 
|  | 1306 | } | 
|  | 1307 |  | 
|  | 1308 | // Test whether the DIMM callouts manufacturing info is getting added to the | 
|  | 1309 | // SysInfo User Data section of the PEL | 
|  | 1310 | TEST_F(PELTest, TestDimmsCalloutInfo) | 
|  | 1311 | { | 
|  | 1312 | { | 
|  | 1313 | message::Entry entry; | 
|  | 1314 | uint64_t timestamp = 5; | 
|  | 1315 | AdditionalData ad; | 
|  | 1316 | NiceMock<MockDataInterface> dataIface; | 
|  | 1317 | NiceMock<MockJournal> journal; | 
|  | 1318 | PelFFDC ffdc; | 
|  | 1319 |  | 
|  | 1320 | // When callouts contain DIMM callouts. | 
|  | 1321 | entry.callouts = R"( | 
|  | 1322 | [ | 
|  | 1323 | { | 
|  | 1324 | "CalloutList": [ | 
|  | 1325 | { | 
|  | 1326 | "Priority": "high", | 
|  | 1327 | "LocCode": "P0-DIMM0" | 
|  | 1328 | }, | 
|  | 1329 | { | 
|  | 1330 | "Priority": "low", | 
|  | 1331 | "LocCode": "P0-DIMM1" | 
|  | 1332 | } | 
|  | 1333 | ] | 
|  | 1334 | } | 
|  | 1335 | ] | 
|  | 1336 | )"_json; | 
|  | 1337 |  | 
|  | 1338 | EXPECT_CALL(dataIface, expandLocationCode("P0-DIMM0", 0)) | 
|  | 1339 | .WillOnce(Return("U98D-P0-DIMM0")); | 
|  | 1340 | EXPECT_CALL(dataIface, expandLocationCode("P0-DIMM1", 0)) | 
|  | 1341 | .WillOnce(Return("U98D-P0-DIMM1")); | 
|  | 1342 |  | 
|  | 1343 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-DIMM0", 0, false)) | 
|  | 1344 | .WillOnce(Return(std::vector<std::string>{ | 
|  | 1345 | "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"})); | 
|  | 1346 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-DIMM1", 0, false)) | 
|  | 1347 | .WillOnce(Return(std::vector<std::string>{ | 
|  | 1348 | "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm1"})); | 
|  | 1349 |  | 
|  | 1350 | std::vector<uint8_t> diValue{128, 74}; | 
|  | 1351 | EXPECT_CALL(dataIface, getDIProperty("U98D-P0-DIMM0")) | 
|  | 1352 | .WillOnce(Return(diValue)); | 
|  | 1353 | EXPECT_CALL(dataIface, getDIProperty("U98D-P0-DIMM1")) | 
|  | 1354 | .WillOnce(Return(diValue)); | 
|  | 1355 |  | 
|  | 1356 | // Add some location code in expanded format to DIMM cache memory | 
|  | 1357 | dataIface.addDIMMLocCode("U98D-P0-DIMM0", true); | 
|  | 1358 | dataIface.addDIMMLocCode("U98D-P0-DIMM1", true); | 
|  | 1359 |  | 
|  | 1360 | PEL pel{entry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1361 | ad,    ffdc, dataIface, journal}; | 
|  | 1362 | nlohmann::json dimmInfoJson = getDIMMInfo(pel); | 
|  | 1363 |  | 
|  | 1364 | nlohmann::json expected_data = R"( | 
|  | 1365 | [ | 
|  | 1366 | { | 
|  | 1367 | "Location Code": "U98D-P0-DIMM0", | 
|  | 1368 | "DRAM Manufacturer ID": [ | 
|  | 1369 | "0x80", | 
|  | 1370 | "0x4a" | 
|  | 1371 | ] | 
|  | 1372 | }, | 
|  | 1373 | { | 
|  | 1374 | "Location Code": "U98D-P0-DIMM1", | 
|  | 1375 | "DRAM Manufacturer ID": [ | 
|  | 1376 | "0x80", | 
|  | 1377 | "0x4a" | 
|  | 1378 | ] | 
|  | 1379 | } | 
|  | 1380 | ] | 
|  | 1381 | )"_json; | 
|  | 1382 | EXPECT_EQ(expected_data, dimmInfoJson); | 
|  | 1383 | } | 
|  | 1384 | } | 
|  | 1385 |  | 
|  | 1386 | // When PEL has FRU callouts but PHAL is not enabled. | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1387 | TEST_F(PELTest, TestNoDimmsCallout) | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1388 | { | 
|  | 1389 | message::Entry entry; | 
|  | 1390 | uint64_t timestamp = 5; | 
|  | 1391 | AdditionalData ad; | 
|  | 1392 | NiceMock<MockDataInterface> dataIface; | 
|  | 1393 | NiceMock<MockJournal> journal; | 
|  | 1394 | PelFFDC ffdc; | 
|  | 1395 |  | 
|  | 1396 | entry.callouts = R"( | 
|  | 1397 | [ | 
|  | 1398 | { | 
|  | 1399 | "CalloutList": [ | 
|  | 1400 | { | 
|  | 1401 | "Priority": "high", | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1402 | "LocCode": "P0-PROC0" | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1403 | } | 
|  | 1404 | ] | 
|  | 1405 | } | 
|  | 1406 | ] | 
|  | 1407 | )"_json; | 
|  | 1408 |  | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1409 | EXPECT_CALL(dataIface, expandLocationCode("P0-PROC0", 0)) | 
|  | 1410 | .WillOnce(Return("U98D-P0-PROC0")); | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1411 |  | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1412 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-PROC0", 0, false)) | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1413 | .WillOnce(Return(std::vector<std::string>{ | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1414 | "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm0/cpu0"})); | 
|  | 1415 |  | 
|  | 1416 | // Add some location code in expanded format to DIMM cache memory | 
|  | 1417 | dataIface.addDIMMLocCode("U98D-P0-PROC0", false); | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1418 |  | 
|  | 1419 | PEL pel{entry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1420 | ad,    ffdc, dataIface, journal}; | 
|  | 1421 |  | 
|  | 1422 | nlohmann::json dimmInfoJson = getDIMMInfo(pel); | 
|  | 1423 |  | 
| Arya K Padman | ced8ed7 | 2024-09-02 05:18:07 -0500 | [diff] [blame] | 1424 | nlohmann::json expected_data{}; | 
| Arya K Padman | d8ae618 | 2024-07-19 06:25:10 -0500 | [diff] [blame] | 1425 |  | 
|  | 1426 | EXPECT_EQ(expected_data, dimmInfoJson); | 
|  | 1427 | } | 
|  | 1428 |  | 
|  | 1429 | // When the PEL doesn't contain any type of callouts | 
|  | 1430 | TEST_F(PELTest, TestDimmsCalloutInfoWithNoCallouts) | 
|  | 1431 | { | 
|  | 1432 | message::Entry entry; | 
|  | 1433 | uint64_t timestamp = 5; | 
|  | 1434 | AdditionalData ad; | 
|  | 1435 | NiceMock<MockDataInterface> dataIface; | 
|  | 1436 | NiceMock<MockJournal> journal; | 
|  | 1437 | PelFFDC ffdc; | 
|  | 1438 |  | 
|  | 1439 | PEL pel{entry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1440 | ad,    ffdc, dataIface, journal}; | 
|  | 1441 |  | 
|  | 1442 | nlohmann::json dimmInfoJson = getDIMMInfo(pel); | 
|  | 1443 |  | 
|  | 1444 | nlohmann::json expected_data{}; | 
|  | 1445 |  | 
|  | 1446 | EXPECT_EQ(expected_data, dimmInfoJson); | 
|  | 1447 | } | 
|  | 1448 |  | 
|  | 1449 | // When the PEL has DIMM callouts, but failed to fetch DI property value | 
|  | 1450 | TEST_F(PELTest, TestDimmsCalloutInfoDIFailure) | 
|  | 1451 | { | 
|  | 1452 | { | 
|  | 1453 | message::Entry entry; | 
|  | 1454 | uint64_t timestamp = 5; | 
|  | 1455 | AdditionalData ad; | 
|  | 1456 | NiceMock<MockDataInterface> dataIface; | 
|  | 1457 | NiceMock<MockJournal> journal; | 
|  | 1458 | PelFFDC ffdc; | 
|  | 1459 |  | 
|  | 1460 | entry.callouts = R"( | 
|  | 1461 | [ | 
|  | 1462 | { | 
|  | 1463 | "CalloutList": [ | 
|  | 1464 | { | 
|  | 1465 | "Priority": "high", | 
|  | 1466 | "LocCode": "P0-DIMM0" | 
|  | 1467 | } | 
|  | 1468 | ] | 
|  | 1469 | } | 
|  | 1470 | ] | 
|  | 1471 | )"_json; | 
|  | 1472 |  | 
|  | 1473 | EXPECT_CALL(dataIface, expandLocationCode("P0-DIMM0", 0)) | 
|  | 1474 | .WillOnce(Return("U98D-P0-DIMM0")); | 
|  | 1475 |  | 
|  | 1476 | EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-DIMM0", 0, false)) | 
|  | 1477 | .WillOnce(Return(std::vector<std::string>{ | 
|  | 1478 | "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0"})); | 
|  | 1479 |  | 
|  | 1480 | EXPECT_CALL(dataIface, getDIProperty("U98D-P0-DIMM0")) | 
|  | 1481 | .WillOnce(Return(std::nullopt)); | 
|  | 1482 |  | 
|  | 1483 | // Add some location code in expanded format to DIMM cache memory | 
|  | 1484 | dataIface.addDIMMLocCode("U98D-P0-DIMM0", true); | 
|  | 1485 |  | 
|  | 1486 | PEL pel{entry, 42,   timestamp, phosphor::logging::Entry::Level::Error, | 
|  | 1487 | ad,    ffdc, dataIface, journal}; | 
|  | 1488 |  | 
|  | 1489 | nlohmann::json dimmInfoJson = getDIMMInfo(pel); | 
|  | 1490 |  | 
|  | 1491 | nlohmann::json expected_data = R"( | 
|  | 1492 | [ | 
|  | 1493 | "Failed reading DI property from VINI Interface for the LocationCode:[U98D-P0-DIMM0]" | 
|  | 1494 | ] | 
|  | 1495 | )"_json; | 
|  | 1496 |  | 
|  | 1497 | EXPECT_EQ(expected_data, dimmInfoJson); | 
|  | 1498 | } | 
|  | 1499 | } |