blob: d64307664df933cfde9473f4a99d1b45011608cd [file] [log] [blame]
Matt Spinler97f7abc2019-11-06 09:40:23 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Matt Spinlerb8323632019-09-20 15:11:04 -050016#include "elog_entry.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050017#include "extensions/openpower-pels/generic.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050018#include "extensions/openpower-pels/pel.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050019#include "mocks.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050020#include "pel_utils.hpp"
21
22#include <filesystem>
23#include <fstream>
24
25#include <gtest/gtest.h>
26
27namespace fs = std::filesystem;
28using namespace openpower::pels;
Matt Spinler0a90a852020-06-04 13:18:27 -050029using ::testing::_;
Matt Spinler56ad2a02020-03-26 14:00:52 -050030using ::testing::NiceMock;
Matt Spinler677381b2020-01-23 10:04:29 -060031using ::testing::Return;
Matt Spinler0a90a852020-06-04 13:18:27 -050032using ::testing::ReturnRef;
33using ::testing::SetArgReferee;
Matt Spinlercb6b0592019-07-16 15:58:51 -050034
35class PELTest : public CleanLogID
36{
37};
38
Matt Spinler5b289b22020-03-26 14:27:19 -050039fs::path makeTempDir()
40{
41 char path[] = "/tmp/tempdirXXXXXX";
42 std::filesystem::path dir = mkdtemp(path);
43 return dir;
44}
45
46int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data)
47{
48 static size_t count = 0;
49 fs::path path = dir / (std::string{"file"} + std::to_string(count));
50 std::ofstream stream{path};
51 count++;
52
53 stream.write(reinterpret_cast<const char*>(data.data()), data.size());
54 stream.close();
55
56 FILE* fp = fopen(path.c_str(), "r");
57 return fileno(fp);
58}
59
Matt Spinlercb6b0592019-07-16 15:58:51 -050060TEST_F(PELTest, FlattenTest)
61{
Matt Spinler42828bd2019-10-11 10:39:30 -050062 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler42828bd2019-10-11 10:39:30 -050063 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050064
65 // Check a few fields
66 EXPECT_TRUE(pel->valid());
67 EXPECT_EQ(pel->id(), 0x80818283);
68 EXPECT_EQ(pel->plid(), 0x50515253);
Matt Spinler97d19b42019-10-29 11:34:03 -050069 EXPECT_EQ(pel->userHeader().subsystem(), 0x10);
70 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0);
Matt Spinlercb6b0592019-07-16 15:58:51 -050071
72 // Test that data in == data out
73 auto flattenedData = pel->data();
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060074 EXPECT_EQ(data, flattenedData);
75 EXPECT_EQ(flattenedData.size(), pel->size());
Matt Spinlercb6b0592019-07-16 15:58:51 -050076}
77
78TEST_F(PELTest, CommitTimeTest)
79{
Matt Spinler42828bd2019-10-11 10:39:30 -050080 auto data = pelDataFactory(TestPELType::pelSimple);
81 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050082
83 auto origTime = pel->commitTime();
84 pel->setCommitTime();
85 auto newTime = pel->commitTime();
86
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060087 EXPECT_NE(origTime, newTime);
Matt Spinlercb6b0592019-07-16 15:58:51 -050088
89 // Make a new PEL and check new value is still there
90 auto newData = pel->data();
91 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060092 EXPECT_EQ(newTime, newPel->commitTime());
Matt Spinlercb6b0592019-07-16 15:58:51 -050093}
94
95TEST_F(PELTest, AssignIDTest)
96{
Matt Spinler42828bd2019-10-11 10:39:30 -050097 auto data = pelDataFactory(TestPELType::pelSimple);
98 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050099
100 auto origID = pel->id();
101 pel->assignID();
102 auto newID = pel->id();
103
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600104 EXPECT_NE(origID, newID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500105
106 // Make a new PEL and check new value is still there
107 auto newData = pel->data();
108 auto newPel = std::make_unique<PEL>(newData);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600109 EXPECT_EQ(newID, newPel->id());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500110}
111
112TEST_F(PELTest, WithLogIDTest)
113{
Matt Spinler42828bd2019-10-11 10:39:30 -0500114 auto data = pelDataFactory(TestPELType::pelSimple);
115 auto pel = std::make_unique<PEL>(data, 0x42);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500116
117 EXPECT_TRUE(pel->valid());
118 EXPECT_EQ(pel->obmcLogID(), 0x42);
119}
120
121TEST_F(PELTest, InvalidPELTest)
122{
Matt Spinler42828bd2019-10-11 10:39:30 -0500123 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500124
125 // Too small
Matt Spinler42828bd2019-10-11 10:39:30 -0500126 data.resize(PrivateHeader::flattenedSize());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500127
Matt Spinler42828bd2019-10-11 10:39:30 -0500128 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500129
Matt Spinler97d19b42019-10-29 11:34:03 -0500130 EXPECT_TRUE(pel->privateHeader().valid());
131 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500132 EXPECT_FALSE(pel->valid());
133
Matt Spinlercb6b0592019-07-16 15:58:51 -0500134 // Now corrupt the private header
Matt Spinler42828bd2019-10-11 10:39:30 -0500135 data = pelDataFactory(TestPELType::pelSimple);
136 data.at(0) = 0;
137 pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500138
Matt Spinler97d19b42019-10-29 11:34:03 -0500139 EXPECT_FALSE(pel->privateHeader().valid());
140 EXPECT_TRUE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500141 EXPECT_FALSE(pel->valid());
142}
143
144TEST_F(PELTest, EmptyDataTest)
145{
146 std::vector<uint8_t> data;
147 auto pel = std::make_unique<PEL>(data);
148
Matt Spinler97d19b42019-10-29 11:34:03 -0500149 EXPECT_FALSE(pel->privateHeader().valid());
150 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500151 EXPECT_FALSE(pel->valid());
152}
Matt Spinlerb8323632019-09-20 15:11:04 -0500153
154TEST_F(PELTest, CreateFromRegistryTest)
155{
156 message::Entry regEntry;
157 uint64_t timestamp = 5;
158
159 regEntry.name = "test";
160 regEntry.subsystem = 5;
161 regEntry.actionFlags = 0xC000;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500162 regEntry.src.type = 0xBD;
163 regEntry.src.reasonCode = 0x1234;
Matt Spinlerb8323632019-09-20 15:11:04 -0500164
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600165 std::vector<std::string> data{"KEY1=VALUE1"};
166 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500167 NiceMock<MockDataInterface> dataIface;
168 PelFFDC ffdc;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500169
Matt Spinler56ad2a02020-03-26 14:00:52 -0500170 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
171 ad, ffdc, dataIface};
Matt Spinlerb8323632019-09-20 15:11:04 -0500172
173 EXPECT_TRUE(pel.valid());
Matt Spinler97d19b42019-10-29 11:34:03 -0500174 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
175 EXPECT_EQ(pel.userHeader().severity(), 0x40);
Matt Spinlerb8323632019-09-20 15:11:04 -0500176
Matt Spinlerbd716f02019-10-15 10:54:11 -0500177 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
178 "BD051234 ");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600179
180 // Check that certain optional sections have been created
181 size_t mtmsCount = 0;
182 size_t euhCount = 0;
183 size_t udCount = 0;
184
185 for (const auto& section : pel.optionalSections())
186 {
187 if (section->header().id ==
188 static_cast<uint16_t>(SectionID::failingMTMS))
189 {
190 mtmsCount++;
191 }
192 else if (section->header().id ==
193 static_cast<uint16_t>(SectionID::extendedUserHeader))
194 {
195 euhCount++;
196 }
197 else if (section->header().id ==
198 static_cast<uint16_t>(SectionID::userData))
199 {
200 udCount++;
201 }
202 }
203
204 EXPECT_EQ(mtmsCount, 1);
205 EXPECT_EQ(euhCount, 1);
206 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
Matt Spinlerb8323632019-09-20 15:11:04 -0500207}
Matt Spinler131870c2019-09-25 13:29:04 -0500208
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500209// Test that when the AdditionalData size is over 16KB that
210// the PEL that's created is exactly 16KB since the UserData
211// section that contains all that data was pruned.
212TEST_F(PELTest, CreateTooBigADTest)
213{
214 message::Entry regEntry;
215 uint64_t timestamp = 5;
216
217 regEntry.name = "test";
218 regEntry.subsystem = 5;
219 regEntry.actionFlags = 0xC000;
220 regEntry.src.type = 0xBD;
221 regEntry.src.reasonCode = 0x1234;
Matt Spinler56ad2a02020-03-26 14:00:52 -0500222 PelFFDC ffdc;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500223
224 // Over the 16KB max PEL size
225 std::string bigAD{"KEY1="};
226 bigAD += std::string(17000, 'G');
227
228 std::vector<std::string> data{bigAD};
229 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500230 NiceMock<MockDataInterface> dataIface;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500231
Matt Spinler56ad2a02020-03-26 14:00:52 -0500232 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
233 ad, ffdc, dataIface};
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500234
235 EXPECT_TRUE(pel.valid());
236 EXPECT_EQ(pel.size(), 16384);
237
238 // Make sure that there are still 2 UD sections.
239 size_t udCount = 0;
240 for (const auto& section : pel.optionalSections())
241 {
242 if (section->header().id == static_cast<uint16_t>(SectionID::userData))
243 {
244 udCount++;
245 }
246 }
247
248 EXPECT_EQ(udCount, 2); // AD section and sysInfo section
249}
250
Matt Spinler131870c2019-09-25 13:29:04 -0500251// Test that we'll create Generic optional sections for sections that
252// there aren't explicit classes for.
253TEST_F(PELTest, GenericSectionTest)
254{
Matt Spinler42828bd2019-10-11 10:39:30 -0500255 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500256
257 std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX'
258 0x00, 0x18, // Size
259 0x01, 0x02, // version, subtype
260 0x03, 0x04, // comp ID
261
262 // some data
263 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63,
264 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A,
265 0x00};
266
267 std::vector<uint8_t> section2{
268 0x59, 0x59, // ID 'YY'
269 0x00, 0x20, // Size
270 0x01, 0x02, // version, subtype
271 0x03, 0x04, // comp ID
272
273 // some data
274 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F,
275 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
276
277 // Add the new sections at the end
Matt Spinler42828bd2019-10-11 10:39:30 -0500278 data.insert(data.end(), section1.begin(), section1.end());
279 data.insert(data.end(), section2.begin(), section2.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500280
281 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500282 data.at(27) += 2;
283 auto origData = data;
Matt Spinler131870c2019-09-25 13:29:04 -0500284
Matt Spinler42828bd2019-10-11 10:39:30 -0500285 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500286
287 const auto& sections = pel.optionalSections();
288
289 bool foundXX = false;
290 bool foundYY = false;
291
292 // Check that we can find these 2 Generic sections
293 for (const auto& section : sections)
294 {
295 if (section->header().id == 0x5858)
296 {
297 foundXX = true;
298 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
299 }
300 else if (section->header().id == 0x5959)
301 {
302 foundYY = true;
303 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr);
304 }
305 }
306
307 EXPECT_TRUE(foundXX);
308 EXPECT_TRUE(foundYY);
Matt Spinler07eefc52019-09-26 11:18:26 -0500309
310 // Now flatten and check
311 auto newData = pel.data();
312
313 EXPECT_EQ(origData, newData);
Matt Spinler131870c2019-09-25 13:29:04 -0500314}
315
316// Test that an invalid section will still get a Generic object
317TEST_F(PELTest, InvalidGenericTest)
318{
Matt Spinler42828bd2019-10-11 10:39:30 -0500319 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500320
321 // Not a valid section
322 std::vector<uint8_t> section1{0x01, 0x02, 0x03};
323
Matt Spinler42828bd2019-10-11 10:39:30 -0500324 data.insert(data.end(), section1.begin(), section1.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500325
326 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500327 data.at(27) += 1;
Matt Spinler131870c2019-09-25 13:29:04 -0500328
Matt Spinler42828bd2019-10-11 10:39:30 -0500329 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500330 EXPECT_FALSE(pel.valid());
331
332 const auto& sections = pel.optionalSections();
333
334 bool foundGeneric = false;
335 for (const auto& section : sections)
336 {
337 if (dynamic_cast<Generic*>(section.get()) != nullptr)
338 {
339 foundGeneric = true;
340 EXPECT_EQ(section->valid(), false);
341 break;
342 }
343 }
344
345 EXPECT_TRUE(foundGeneric);
346}
Matt Spinlerafa857c2019-10-24 13:03:46 -0500347
348// Create a UserData section out of AdditionalData
349TEST_F(PELTest, MakeUDSectionTest)
350{
351 std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3",
352 "ESEL=TEST"};
353 AdditionalData additionalData{ad};
354
355 auto ud = util::makeADUserDataSection(additionalData);
356
357 EXPECT_TRUE(ud->valid());
358 EXPECT_EQ(ud->header().id, 0x5544);
359 EXPECT_EQ(ud->header().version, 0x01);
360 EXPECT_EQ(ud->header().subType, 0x01);
361 EXPECT_EQ(ud->header().componentID, 0x2000);
362
363 const auto& d = ud->data();
364
365 std::string jsonString{d.begin(), d.end()};
Matt Spinler53407be2019-11-18 09:16:31 -0600366
367 std::string expectedJSON =
Matt Spinlerafa857c2019-10-24 13:03:46 -0500368 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
Matt Spinler53407be2019-11-18 09:16:31 -0600369
370 // The actual data is null padded to a 4B boundary.
371 std::vector<uint8_t> expectedData;
372 expectedData.resize(52, '\0');
373 memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size());
374
375 EXPECT_EQ(d, expectedData);
Matt Spinlerafa857c2019-10-24 13:03:46 -0500376
377 // Ensure we can read this as JSON
378 auto newJSON = nlohmann::json::parse(jsonString);
379 EXPECT_EQ(newJSON["KEY1"], "VALUE1");
380 EXPECT_EQ(newJSON["KEY2"], "VALUE2");
381 EXPECT_EQ(newJSON["KEY3"], "VALUE3");
Matt Spinler97d19b42019-10-29 11:34:03 -0500382}
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600383
384// Create the UserData section that contains system info
Matt Spinler677381b2020-01-23 10:04:29 -0600385TEST_F(PELTest, SysInfoSectionTest)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600386{
387 MockDataInterface dataIface;
388
Matt Spinler677381b2020-01-23 10:04:29 -0600389 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600390 EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready"));
391 EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On"));
392 EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off"));
Matt Spinler677381b2020-01-23 10:04:29 -0600393
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600394 std::string pid = "_PID=" + std::to_string(getpid());
395 std::vector<std::string> ad{pid};
396 AdditionalData additionalData{ad};
397
398 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
399
400 EXPECT_TRUE(ud->valid());
401 EXPECT_EQ(ud->header().id, 0x5544);
402 EXPECT_EQ(ud->header().version, 0x01);
403 EXPECT_EQ(ud->header().subType, 0x01);
404 EXPECT_EQ(ud->header().componentID, 0x2000);
405
406 // Pull out the JSON data and check it.
407 const auto& d = ud->data();
408 std::string jsonString{d.begin(), d.end()};
409 auto json = nlohmann::json::parse(jsonString);
410
411 // Ensure the 'Process Name' entry contains 'pel_test'
412 auto name = json["Process Name"].get<std::string>();
413 EXPECT_NE(name.find("pel_test"), std::string::npos);
Matt Spinler677381b2020-01-23 10:04:29 -0600414
415 auto version = json["BMC Version ID"].get<std::string>();
416 EXPECT_EQ(version, "ABCD1234");
Matt Spinler4aa23a12020-02-03 15:05:09 -0600417
418 auto state = json["BMCState"].get<std::string>();
419 EXPECT_EQ(state, "Ready");
420
421 state = json["ChassisState"].get<std::string>();
422 EXPECT_EQ(state, "On");
423
424 state = json["HostState"].get<std::string>();
425 EXPECT_EQ(state, "Off");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600426}
Matt Spinlerce3f4502020-01-22 15:44:35 -0600427
428// Test that the sections that override
429// virtual std::optional<std::string> Section::getJSON() const
430// return valid JSON.
431TEST_F(PELTest, SectionJSONTest)
432{
433 auto data = pelDataFactory(TestPELType::pelSimple);
434 PEL pel{data};
435
436 // Check that all JSON returned from the sections is
437 // parseable by nlohmann::json, which will throw an
438 // exception and fail the test if there is a problem.
439
440 // The getJSON() response needs to be wrapped in a { } to make
441 // actual valid JSON (PEL::toJSON() usually handles that).
442
443 auto jsonString = pel.privateHeader().getJSON();
444
445 // PrivateHeader always prints JSON
446 ASSERT_TRUE(jsonString);
447 *jsonString = '{' + *jsonString + '}';
448 auto json = nlohmann::json::parse(*jsonString);
449
450 jsonString = pel.userHeader().getJSON();
451
452 // UserHeader always prints JSON
453 ASSERT_TRUE(jsonString);
454 *jsonString = '{' + *jsonString + '}';
455 json = nlohmann::json::parse(*jsonString);
456
457 for (const auto& section : pel.optionalSections())
458 {
459 // The optional sections may or may not have implemented getJSON().
460 jsonString = section->getJSON();
461 if (jsonString)
462 {
463 *jsonString = '{' + *jsonString + '}';
464 auto json = nlohmann::json::parse(*jsonString);
465 }
466 }
467}
Matt Spinler5b289b22020-03-26 14:27:19 -0500468
469PelFFDCfile getJSONFFDC(const fs::path& dir)
470{
471 PelFFDCfile ffdc;
472 ffdc.format = UserDataFormat::json;
473 ffdc.subType = 5;
474 ffdc.version = 42;
475
476 auto inputJSON = R"({
477 "key1": "value1",
478 "key2": 42,
479 "key3" : [1, 2, 3, 4, 5],
480 "key4": {"key5": "value5"}
481 })"_json;
482
483 // Write the JSON to a file and get its descriptor.
484 auto s = inputJSON.dump();
485 std::vector<uint8_t> data{s.begin(), s.end()};
486 ffdc.fd = writeFileAndGetFD(dir, data);
487
488 return ffdc;
489}
490
491TEST_F(PELTest, MakeJSONFileUDSectionTest)
492{
493 auto dir = makeTempDir();
494
495 {
496 auto ffdc = getJSONFFDC(dir);
497
498 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
499 close(ffdc.fd);
500 ASSERT_TRUE(ud);
501 ASSERT_TRUE(ud->valid());
502 EXPECT_EQ(ud->header().id, 0x5544);
503
504 EXPECT_EQ(ud->header().version,
505 static_cast<uint8_t>(UserDataFormatVersion::json));
506 EXPECT_EQ(ud->header().subType,
507 static_cast<uint8_t>(UserDataFormat::json));
508 EXPECT_EQ(ud->header().componentID,
509 static_cast<uint16_t>(ComponentID::phosphorLogging));
510
511 // Pull the JSON back out of the the UserData section
512 const auto& d = ud->data();
513 std::string js{d.begin(), d.end()};
514 auto json = nlohmann::json::parse(js);
515
516 EXPECT_EQ("value1", json["key1"].get<std::string>());
517 EXPECT_EQ(42, json["key2"].get<int>());
518
519 std::vector<int> key3Values{1, 2, 3, 4, 5};
520 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
521
522 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
523 auto actual = json["key4"].get<std::map<std::string, std::string>>();
524 EXPECT_EQ(key4Values, actual);
525 }
526
527 {
528 // A bad FD
529 PelFFDCfile ffdc;
530 ffdc.format = UserDataFormat::json;
531 ffdc.subType = 5;
532 ffdc.version = 42;
533 ffdc.fd = 10000;
534
535 // The section shouldn't get made
536 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
537 ASSERT_FALSE(ud);
538 }
539
540 fs::remove_all(dir);
541}
542
543PelFFDCfile getCBORFFDC(const fs::path& dir)
544{
545 PelFFDCfile ffdc;
546 ffdc.format = UserDataFormat::cbor;
547 ffdc.subType = 5;
548 ffdc.version = 42;
549
550 auto inputJSON = R"({
551 "key1": "value1",
552 "key2": 42,
553 "key3" : [1, 2, 3, 4, 5],
554 "key4": {"key5": "value5"}
555 })"_json;
556
557 // Convert the JSON to CBOR and write it to a file
558 auto data = nlohmann::json::to_cbor(inputJSON);
559 ffdc.fd = writeFileAndGetFD(dir, data);
560
561 return ffdc;
562}
563
564TEST_F(PELTest, MakeCBORFileUDSectionTest)
565{
566 auto dir = makeTempDir();
567
568 auto ffdc = getCBORFFDC(dir);
569 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
570 close(ffdc.fd);
571 ASSERT_TRUE(ud);
572 ASSERT_TRUE(ud->valid());
573 EXPECT_EQ(ud->header().id, 0x5544);
574
575 EXPECT_EQ(ud->header().version,
576 static_cast<uint8_t>(UserDataFormatVersion::cbor));
577 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor));
578 EXPECT_EQ(ud->header().componentID,
579 static_cast<uint16_t>(ComponentID::phosphorLogging));
580
581 // Pull the CBOR back out of the PEL section
582 // The number of pad bytes to make the section be 4B aligned
583 // was added at the end, read it and then remove it and the
584 // padding before parsing it.
585 auto data = ud->data();
586 Stream stream{data};
587 stream.offset(data.size() - 4);
588 uint32_t pad;
589 stream >> pad;
590
591 data.resize(data.size() - 4 - pad);
592
593 auto json = nlohmann::json::from_cbor(data);
594
595 EXPECT_EQ("value1", json["key1"].get<std::string>());
596 EXPECT_EQ(42, json["key2"].get<int>());
597
598 std::vector<int> key3Values{1, 2, 3, 4, 5};
599 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>());
600
601 std::map<std::string, std::string> key4Values{{"key5", "value5"}};
602 auto actual = json["key4"].get<std::map<std::string, std::string>>();
603 EXPECT_EQ(key4Values, actual);
604
605 fs::remove_all(dir);
606}
607
608PelFFDCfile getTextFFDC(const fs::path& dir)
609{
610 PelFFDCfile ffdc;
611 ffdc.format = UserDataFormat::text;
612 ffdc.subType = 5;
613 ffdc.version = 42;
614
615 std::string text{"this is some text that will be used for FFDC"};
616 std::vector<uint8_t> data{text.begin(), text.end()};
617
618 ffdc.fd = writeFileAndGetFD(dir, data);
619
620 return ffdc;
621}
622
623TEST_F(PELTest, MakeTextFileUDSectionTest)
624{
625 auto dir = makeTempDir();
626
627 auto ffdc = getTextFFDC(dir);
628 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
629 close(ffdc.fd);
630 ASSERT_TRUE(ud);
631 ASSERT_TRUE(ud->valid());
632 EXPECT_EQ(ud->header().id, 0x5544);
633
634 EXPECT_EQ(ud->header().version,
635 static_cast<uint8_t>(UserDataFormatVersion::text));
636 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text));
637 EXPECT_EQ(ud->header().componentID,
638 static_cast<uint16_t>(ComponentID::phosphorLogging));
639
640 // Get the text back out
641 std::string text{ud->data().begin(), ud->data().end()};
642 EXPECT_EQ(text, "this is some text that will be used for FFDC");
643
644 fs::remove_all(dir);
645}
646
647PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data)
648{
649 PelFFDCfile ffdc;
650 ffdc.format = UserDataFormat::custom;
651 ffdc.subType = 5;
652 ffdc.version = 42;
653
654 ffdc.fd = writeFileAndGetFD(dir, data);
655
656 return ffdc;
657}
658
659TEST_F(PELTest, MakeCustomFileUDSectionTest)
660{
661 auto dir = makeTempDir();
662
663 {
664 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8};
665
666 auto ffdc = getCustomFFDC(dir, data);
667 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
668 close(ffdc.fd);
669 ASSERT_TRUE(ud);
670 ASSERT_TRUE(ud->valid());
671 EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size
672 EXPECT_EQ(ud->header().id, 0x5544);
673
674 EXPECT_EQ(ud->header().version, 42);
675 EXPECT_EQ(ud->header().subType, 5);
676 EXPECT_EQ(ud->header().componentID, 0x2002);
677
678 // Get the data back out
679 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
680 EXPECT_EQ(data, newData);
681 }
682
683 // Do the same thing again, but make it be non 4B aligned
684 // so the data gets padded.
685 {
686 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
687
688 auto ffdc = getCustomFFDC(dir, data);
689 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc);
690 close(ffdc.fd);
691 ASSERT_TRUE(ud);
692 ASSERT_TRUE(ud->valid());
693 EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size
694 EXPECT_EQ(ud->header().id, 0x5544);
695
696 EXPECT_EQ(ud->header().version, 42);
697 EXPECT_EQ(ud->header().subType, 5);
698 EXPECT_EQ(ud->header().componentID, 0x2002);
699
700 // Get the data back out
701 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()};
702
703 // pad the original to 12B so we can compare
704 data.push_back(0);
705 data.push_back(0);
706 data.push_back(0);
707
708 EXPECT_EQ(data, newData);
709 }
710
711 fs::remove_all(dir);
712}
713
714// Test Adding FFDC from files to a PEL
715TEST_F(PELTest, CreateWithFFDCTest)
716{
717 auto dir = makeTempDir();
718 message::Entry regEntry;
719 uint64_t timestamp = 5;
720
721 regEntry.name = "test";
722 regEntry.subsystem = 5;
723 regEntry.actionFlags = 0xC000;
724 regEntry.src.type = 0xBD;
725 regEntry.src.reasonCode = 0x1234;
726
727 std::vector<std::string> additionalData{"KEY1=VALUE1"};
728 AdditionalData ad{additionalData};
729 NiceMock<MockDataInterface> dataIface;
730 PelFFDC ffdc;
731
732 std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8};
733
734 // This will be trimmed when added
735 std::vector<uint8_t> hugeCustomData(17000, 0x42);
736
737 ffdc.emplace_back(std::move(getJSONFFDC(dir)));
738 ffdc.emplace_back(std::move(getCBORFFDC(dir)));
739 ffdc.emplace_back(std::move(getTextFFDC(dir)));
740 ffdc.emplace_back(std::move(getCustomFFDC(dir, customData)));
741 ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData)));
742
743 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
744 ad, ffdc, dataIface};
745
746 EXPECT_TRUE(pel.valid());
747
748 // Clipped to the max
749 EXPECT_EQ(pel.size(), 16384);
750
751 // Check for the FFDC sections
752 size_t udCount = 0;
753 Section* ud = nullptr;
754
755 for (const auto& section : pel.optionalSections())
756 {
757 if (section->header().id == static_cast<uint16_t>(SectionID::userData))
758 {
759 udCount++;
760 ud = section.get();
761 }
762 }
763
764 EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections
765
766 // Check the last section was trimmed to
767 // something a bit less that 17000.
768 EXPECT_GT(ud->header().size, 14000);
769 EXPECT_LT(ud->header().size, 16000);
770
771 fs::remove_all(dir);
772}
Matt Spinler0a90a852020-06-04 13:18:27 -0500773
774// Create a PEL with device callouts
775TEST_F(PELTest, CreateWithDevCalloutsTest)
776{
777 message::Entry regEntry;
778 uint64_t timestamp = 5;
779
780 regEntry.name = "test";
781 regEntry.subsystem = 5;
782 regEntry.actionFlags = 0xC000;
783 regEntry.src.type = 0xBD;
784 regEntry.src.reasonCode = 0x1234;
785
786 NiceMock<MockDataInterface> dataIface;
787 PelFFDC ffdc;
788
789 const auto calloutJSON = R"(
790 {
791 "I2C":
792 {
793 "14":
794 {
795 "114":
796 {
797 "Callouts":[
798 {
799 "Name": "/chassis/motherboard/cpu0",
800 "LocationCode": "P1",
801 "Priority": "H"
802 }
803 ],
804 "Dest": "proc 0 target"
805 }
806 }
807 }
808 })";
809
810 std::vector<std::string> names{"systemA"};
811 EXPECT_CALL(dataIface, getSystemNames)
812 .Times(2)
813 .WillRepeatedly(ReturnRef(names));
814
815 EXPECT_CALL(dataIface,
816 getLocationCode(
817 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"))
818 .WillOnce(Return("UXXX-P1"));
819
820 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0))
821 .WillOnce(
822 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
823
824 EXPECT_CALL(
825 dataIface,
826 getHWCalloutFields(
827 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
828 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
829 SetArgReferee<3>("123456789ABC")));
830
831 auto dataPath = getPELReadOnlyDataPath();
832 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
833 file << calloutJSON;
834 file.close();
835
836 {
837 std::vector<std::string> data{
838 "CALLOUT_ERRNO=5",
839 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
840 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
841
842 AdditionalData ad{data};
843
844 PEL pel{
845 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
846 ad, ffdc, dataIface};
847
848 ASSERT_TRUE(pel.primarySRC().value()->callouts());
849 auto& callouts = pel.primarySRC().value()->callouts()->callouts();
850 ASSERT_EQ(callouts.size(), 1);
851
852 EXPECT_EQ(callouts[0]->priority(), 'H');
853 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P1");
854
855 auto& fru = callouts[0]->fruIdentity();
856 EXPECT_EQ(fru->getPN().value(), "1234567");
857 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
858 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
859
860 const auto& section = pel.optionalSections().back();
861
862 ASSERT_EQ(section->header().id, 0x5544); // UD
863 auto ud = static_cast<UserData*>(section.get());
864
865 // Check that there was a UserData section added that
866 // contains debug details about the device.
867 const auto& d = ud->data();
868 std::string jsonString{d.begin(), d.end()};
869 auto actualJSON = nlohmann::json::parse(jsonString);
870
871 auto expectedJSON = R"(
872 {
873 "PEL Internal Debug Data": {
874 "SRC": [
875 "I2C: bus: 14 address: 114 dest: proc 0 target"
876 ]
877 }
878 }
879 )"_json;
880
881 EXPECT_EQ(actualJSON, expectedJSON);
882 }
883
884 {
885 // Device path not found (wrong i2c addr), so no callouts
886 std::vector<std::string> data{
887 "CALLOUT_ERRNO=5",
888 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
889 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0099"};
890
891 AdditionalData ad{data};
892
893 PEL pel{
894 regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
895 ad, ffdc, dataIface};
896
897 // no callouts
898 EXPECT_FALSE(pel.primarySRC().value()->callouts());
899
900 // Now check that there was a UserData section
901 // that contains the lookup error.
902 const auto& section = pel.optionalSections().back();
903
904 ASSERT_EQ(section->header().id, 0x5544); // UD
905 auto ud = static_cast<UserData*>(section.get());
906
907 const auto& d = ud->data();
908
909 std::string jsonString{d.begin(), d.end()};
910
911 auto actualJSON = nlohmann::json::parse(jsonString);
912
913 auto expectedJSON =
914 "{\"PEL Internal Debug Data\":{\"SRC\":"
915 "[\"Problem looking up I2C callouts on 14 153: "
916 "[json.exception.out_of_range.403] key '153' not found\"]}}"_json;
917
918 EXPECT_EQ(actualJSON, expectedJSON);
919 }
920
921 fs::remove_all(dataPath);
922}