blob: f07250887a8afe15fb7ed77b01259c5dce907509 [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 Spinlerf9bae182019-10-09 13:37:38 -050016#include "extensions/openpower-pels/src.hpp"
Matt Spinler075e5ba2020-02-21 15:46:00 -060017#include "mocks.hpp"
Matt Spinlerf9bae182019-10-09 13:37:38 -050018#include "pel_utils.hpp"
19
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080020#include <fstream>
21
Matt Spinlerf9bae182019-10-09 13:37:38 -050022#include <gtest/gtest.h>
23
24using namespace openpower::pels;
Matt Spinlered046852020-03-13 13:58:15 -050025using ::testing::_;
William A. Kennington IIIb41fa542021-05-29 14:45:16 -070026using ::testing::DoAll;
Matt Spinlered046852020-03-13 13:58:15 -050027using ::testing::InvokeWithoutArgs;
Matt Spinler075e5ba2020-02-21 15:46:00 -060028using ::testing::NiceMock;
29using ::testing::Return;
Matt Spinlered046852020-03-13 13:58:15 -050030using ::testing::SetArgReferee;
Matt Spinler3bdd0112020-08-27 10:24:34 -050031using ::testing::Throw;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080032namespace fs = std::filesystem;
Matt Spinlerf9bae182019-10-09 13:37:38 -050033
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080034const auto testRegistry = R"(
35{
36"PELs":
37[
38 {
39 "Name": "xyz.openbmc_project.Error.Test",
40 "Subsystem": "bmc_firmware",
41 "SRC":
42 {
43 "ReasonCode": "0xABCD",
44 "Words6To9":
45 {
46 "6":
47 {
48 "Description": "Component ID",
49 "AdditionalDataPropSource": "COMPID"
50 },
51 "7":
52 {
53 "Description": "Failure count",
54 "AdditionalDataPropSource": "FREQUENCY"
55 },
56 "8":
57 {
58 "Description": "Time period",
59 "AdditionalDataPropSource": "DURATION"
60 },
61 "9":
62 {
63 "Description": "Error code",
64 "AdditionalDataPropSource": "ERRORCODE"
65 }
66 }
67 },
68 "Documentation":
69 {
70 "Description": "A Component Fault",
71 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
72 "MessageArgSources":
73 [
74 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
75 ]
76 }
77 }
78]
79}
80)";
81
82class SRCTest : public ::testing::Test
83{
84 protected:
85 static void SetUpTestCase()
86 {
87 char path[] = "/tmp/srctestXXXXXX";
88 regDir = mkdtemp(path);
89 }
90
91 static void TearDownTestCase()
92 {
93 fs::remove_all(regDir);
94 }
95
96 static std::string writeData(const char* data)
97 {
98 fs::path path = regDir / "registry.json";
99 std::ofstream stream{path};
100 stream << data;
101 return path;
102 }
103
104 static fs::path regDir;
105};
106
107fs::path SRCTest::regDir{};
108
109TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500110{
Matt Spinler42828bd2019-10-11 10:39:30 -0500111 auto data = pelDataFactory(TestPELType::primarySRCSection);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500112
113 Stream stream{data};
114 SRC src{stream};
115
116 EXPECT_TRUE(src.valid());
117
118 EXPECT_EQ(src.header().id, 0x5053);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600119 EXPECT_EQ(src.header().size, 0x50);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500120 EXPECT_EQ(src.header().version, 0x01);
121 EXPECT_EQ(src.header().subType, 0x01);
122 EXPECT_EQ(src.header().componentID, 0x0202);
123
124 EXPECT_EQ(src.version(), 0x02);
125 EXPECT_EQ(src.flags(), 0x00);
126 EXPECT_EQ(src.hexWordCount(), 9);
127 EXPECT_EQ(src.size(), 0x48);
128
129 const auto& hexwords = src.hexwordData();
Matt Spinlerbd716f02019-10-15 10:54:11 -0500130 EXPECT_EQ(0x02020255, hexwords[0]);
131 EXPECT_EQ(0x03030310, hexwords[1]);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500132 EXPECT_EQ(0x04040404, hexwords[2]);
133 EXPECT_EQ(0x05050505, hexwords[3]);
134 EXPECT_EQ(0x06060606, hexwords[4]);
135 EXPECT_EQ(0x07070707, hexwords[5]);
136 EXPECT_EQ(0x08080808, hexwords[6]);
137 EXPECT_EQ(0x09090909, hexwords[7]);
138
139 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
140 EXPECT_FALSE(src.callouts());
141
142 // Flatten
143 std::vector<uint8_t> newData;
144 Stream newStream{newData};
145
146 src.flatten(newStream);
147 EXPECT_EQ(data, newData);
148}
149
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800150TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500151{
Matt Spinler42828bd2019-10-11 10:39:30 -0500152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500153
154 Stream stream{data};
155 SRC src{stream};
156
157 EXPECT_TRUE(src.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
Matt Spinlerf9bae182019-10-09 13:37:38 -0500159
160 // Spot check the SRC fields, but they're the same as above
161 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
162
163 // There should be 2 callouts
164 const auto& calloutsSection = src.callouts();
165 ASSERT_TRUE(calloutsSection);
166 const auto& callouts = calloutsSection->callouts();
167 EXPECT_EQ(callouts.size(), 2);
168
169 // spot check that each callout has the right substructures
170 EXPECT_TRUE(callouts.front()->fruIdentity());
171 EXPECT_FALSE(callouts.front()->pceIdentity());
172 EXPECT_FALSE(callouts.front()->mru());
173
174 EXPECT_TRUE(callouts.back()->fruIdentity());
175 EXPECT_TRUE(callouts.back()->pceIdentity());
176 EXPECT_TRUE(callouts.back()->mru());
177
178 // Flatten
179 std::vector<uint8_t> newData;
180 Stream newStream{newData};
181
182 src.flatten(newStream);
183 EXPECT_EQ(data, newData);
184}
Matt Spinlerbd716f02019-10-15 10:54:11 -0500185
186// Create an SRC from the message registry
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800187TEST_F(SRCTest, CreateTestNoCallouts)
Matt Spinlerbd716f02019-10-15 10:54:11 -0500188{
189 message::Entry entry;
190 entry.src.type = 0xBD;
191 entry.src.reasonCode = 0xABCD;
192 entry.subsystem = 0x42;
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +0800193 entry.src.hexwordADFields = {
194 {5, {"TEST1", "DESCR1"}}, // Not a user defined word
195 {6, {"TEST1", "DESCR1"}},
196 {7, {"TEST2", "DESCR2"}},
197 {8, {"TEST3", "DESCR3"}},
198 {9, {"TEST4", "DESCR4"}}};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500199
200 // Values for the SRC words pointed to above
201 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
202 "TEST3=0XDEF", "TEST4=Z"};
203 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600204 NiceMock<MockDataInterface> dataIface;
205
206 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
207
Sumit Kumar9d43a722021-08-24 09:46:19 -0500208 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
209 "system/entry"};
210 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
211 .WillOnce(Return(std::vector<bool>{false, false, false}));
212
Matt Spinler075e5ba2020-02-21 15:46:00 -0600213 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500214
215 EXPECT_TRUE(src.valid());
Mike Cappsa2d7b772022-03-07 15:47:48 -0500216 EXPECT_FALSE(src.isPowerFaultEvent());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500217 EXPECT_EQ(src.size(), baseSRCSize);
218
219 const auto& hexwords = src.hexwordData();
220
221 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
222 // array index starts at 0 use the math in the [] below to make it easier
223 // to tell what is being accessed.
224 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
225 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
226 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
227 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600228 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500229
230 // Validate more fields here as the code starts filling them in.
231
232 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
233 EXPECT_EQ(hexwords[5 - 2], 0);
234
235 // The user defined hex word fields specifed in the additional data.
236 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
237 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
238 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
239 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
240
241 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
242
243 // No callouts
244 EXPECT_FALSE(src.callouts());
245
246 // May as well spot check the flatten/unflatten
247 std::vector<uint8_t> data;
248 Stream stream{data};
249 src.flatten(stream);
250
251 stream.offset(0);
252 SRC newSRC{stream};
253
254 EXPECT_TRUE(newSRC.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500255 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
256 EXPECT_FALSE(newSRC.callouts());
257}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800258
Matt Spinler075e5ba2020-02-21 15:46:00 -0600259// Test when the CCIN string isn't a 4 character number
260TEST_F(SRCTest, BadCCINTest)
261{
262 message::Entry entry;
263 entry.src.type = 0xBD;
264 entry.src.reasonCode = 0xABCD;
265 entry.subsystem = 0x42;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600266
267 std::vector<std::string> adData{};
268 AdditionalData ad{adData};
269 NiceMock<MockDataInterface> dataIface;
270
Sumit Kumar9d43a722021-08-24 09:46:19 -0500271 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
272 "system/entry"};
273 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
274 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
275
Matt Spinler075e5ba2020-02-21 15:46:00 -0600276 // First it isn't a number, then it is too long,
277 // then it is empty.
278 EXPECT_CALL(dataIface, getMotherboardCCIN)
279 .WillOnce(Return("X"))
280 .WillOnce(Return("12345"))
281 .WillOnce(Return(""));
282
283 // The CCIN in the first half should still be 0 each time.
284 {
285 SRC src{entry, ad, dataIface};
286 EXPECT_TRUE(src.valid());
287 const auto& hexwords = src.hexwordData();
288 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
289 }
290
291 {
292 SRC src{entry, ad, dataIface};
293 EXPECT_TRUE(src.valid());
294 const auto& hexwords = src.hexwordData();
295 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
296 }
297
298 {
299 SRC src{entry, ad, dataIface};
300 EXPECT_TRUE(src.valid());
301 const auto& hexwords = src.hexwordData();
302 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
303 }
304}
305
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800306// Test the getErrorDetails function
307TEST_F(SRCTest, MessageSubstitutionTest)
308{
309 auto path = SRCTest::writeData(testRegistry);
310 message::Registry registry{path};
311 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
312
313 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
314 "DURATION=30", "ERRORCODE=0x01ABCDEF"};
315 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600316 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800317
Sumit Kumar9d43a722021-08-24 09:46:19 -0500318 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
319 "system/entry"};
320 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
321 .WillOnce(Return(std::vector<bool>{false, false, false}));
322
Matt Spinler075e5ba2020-02-21 15:46:00 -0600323 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800324 EXPECT_TRUE(src.valid());
325
326 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
327 ASSERT_TRUE(errorDetails);
Zane Shelley39936e32021-11-13 16:19:34 -0600328 EXPECT_EQ(errorDetails.value(),
329 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
330 "with ErrorCode 0x01ABCDEF");
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800331}
Matt Spinlered046852020-03-13 13:58:15 -0500332// Test that an inventory path callout string is
333// converted into the appropriate FRU callout.
334TEST_F(SRCTest, InventoryCalloutTest)
335{
336 message::Entry entry;
337 entry.src.type = 0xBD;
338 entry.src.reasonCode = 0xABCD;
339 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500340
341 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
342 AdditionalData ad{adData};
343 NiceMock<MockDataInterface> dataIface;
344
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500345 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
346 .WillOnce(Return("UTMS-P1"));
347
348 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500349 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500350 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
351 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500352
Sumit Kumar9d43a722021-08-24 09:46:19 -0500353 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
354 "system/entry"};
355 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
356 .WillOnce(Return(std::vector<bool>{false, false, false}));
357
Matt Spinlered046852020-03-13 13:58:15 -0500358 SRC src{entry, ad, dataIface};
359 EXPECT_TRUE(src.valid());
360
361 ASSERT_TRUE(src.callouts());
362
363 EXPECT_EQ(src.callouts()->callouts().size(), 1);
364
365 auto& callout = src.callouts()->callouts().front();
366
367 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500368 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500369
370 auto& fru = callout->fruIdentity();
371
372 EXPECT_EQ(fru->getPN().value(), "1234567");
373 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
374 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
375
376 // flatten and unflatten
377 std::vector<uint8_t> data;
378 Stream stream{data};
379 src.flatten(stream);
380
381 stream.offset(0);
382 SRC newSRC{stream};
383 EXPECT_TRUE(newSRC.valid());
384 ASSERT_TRUE(src.callouts());
385 EXPECT_EQ(src.callouts()->callouts().size(), 1);
386}
387
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500388// Test that when the location code can't be obtained that
Matt Spinler479b6922021-08-17 16:34:59 -0500389// no callout is added.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500390TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
391{
392 message::Entry entry;
393 entry.src.type = 0xBD;
394 entry.src.reasonCode = 0xABCD;
395 entry.subsystem = 0x42;
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500396
397 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
398 AdditionalData ad{adData};
399 NiceMock<MockDataInterface> dataIface;
400
401 auto func = []() {
402 throw sdbusplus::exception::SdBusError(5, "Error");
403 return std::string{};
404 };
405
406 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
407 .Times(1)
408 .WillOnce(InvokeWithoutArgs(func));
409
Sumit Kumar9d43a722021-08-24 09:46:19 -0500410 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
411 "system/entry"};
412 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
413 .WillOnce(Return(std::vector<bool>{false, false, false}));
414
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500415 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
416
417 SRC src{entry, ad, dataIface};
418 EXPECT_TRUE(src.valid());
419
Matt Spinler479b6922021-08-17 16:34:59 -0500420 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500421
422 // flatten and unflatten
423 std::vector<uint8_t> data;
424 Stream stream{data};
425 src.flatten(stream);
426
427 stream.offset(0);
428 SRC newSRC{stream};
429 EXPECT_TRUE(newSRC.valid());
Matt Spinler479b6922021-08-17 16:34:59 -0500430 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500431}
432
433// Test that when the VPD can't be obtained that
434// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500435TEST_F(SRCTest, InventoryCalloutNoVPDTest)
436{
437 message::Entry entry;
438 entry.src.type = 0xBD;
439 entry.src.reasonCode = 0xABCD;
440 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500441
442 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
443 AdditionalData ad{adData};
444 NiceMock<MockDataInterface> dataIface;
445
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500446 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
447 .Times(1)
448 .WillOnce(Return("UTMS-P10"));
449
Matt Spinlered046852020-03-13 13:58:15 -0500450 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
451
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500452 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500453 .Times(1)
454 .WillOnce(InvokeWithoutArgs(func));
455
Sumit Kumar9d43a722021-08-24 09:46:19 -0500456 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
457 "system/entry"};
458 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
459 .WillOnce(Return(std::vector<bool>{false, false, false}));
460
Matt Spinlered046852020-03-13 13:58:15 -0500461 SRC src{entry, ad, dataIface};
462 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500463 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500464 EXPECT_EQ(src.callouts()->callouts().size(), 1);
465
466 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500467 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500468 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500469
470 auto& fru = callout->fruIdentity();
471
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500472 EXPECT_EQ(fru->getPN(), "");
473 EXPECT_EQ(fru->getCCIN(), "");
474 EXPECT_EQ(fru->getSN(), "");
475 EXPECT_FALSE(fru->getMaintProc());
476
Matt Spinlered046852020-03-13 13:58:15 -0500477 // flatten and unflatten
478 std::vector<uint8_t> data;
479 Stream stream{data};
480 src.flatten(stream);
481
482 stream.offset(0);
483 SRC newSRC{stream};
484 EXPECT_TRUE(newSRC.valid());
485 ASSERT_TRUE(src.callouts());
486 EXPECT_EQ(src.callouts()->callouts().size(), 1);
487}
Matt Spinler03984582020-04-09 13:17:58 -0500488
489TEST_F(SRCTest, RegistryCalloutTest)
490{
491 message::Entry entry;
492 entry.src.type = 0xBD;
493 entry.src.reasonCode = 0xABCD;
494 entry.subsystem = 0x42;
Matt Spinler03984582020-04-09 13:17:58 -0500495
496 entry.callouts = R"(
497 [
498 {
499 "System": "systemA",
500 "CalloutList":
501 [
502 {
503 "Priority": "high",
504 "SymbolicFRU": "service_docs"
505 },
506 {
507 "Priority": "medium",
Matt Spinler479b6922021-08-17 16:34:59 -0500508 "Procedure": "bmc_code"
Matt Spinler03984582020-04-09 13:17:58 -0500509 }
510 ]
511 },
512 {
513 "System": "systemB",
514 "CalloutList":
515 [
516 {
517 "Priority": "high",
518 "LocCode": "P0-C8",
519 "SymbolicFRUTrusted": "service_docs"
520 },
521 {
522 "Priority": "medium",
523 "SymbolicFRUTrusted": "service_docs"
524 }
525 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500526 },
527 {
528 "System": "systemC",
529 "CalloutList":
530 [
531 {
532 "Priority": "high",
533 "LocCode": "P0-C8"
534 },
535 {
536 "Priority": "medium",
537 "LocCode": "P0-C9"
538 }
539 ]
Matt Spinler03984582020-04-09 13:17:58 -0500540 }
541 ])"_json;
542
543 {
544 // Call out a symbolic FRU and a procedure
545 AdditionalData ad;
546 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500547 std::vector<std::string> names{"systemA"};
548
Matt Spinler1ab66962020-10-29 13:21:44 -0500549 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500550
Sumit Kumar9d43a722021-08-24 09:46:19 -0500551 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
552 "system/entry"};
553 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
554 .WillOnce(Return(std::vector<bool>{false, false, false}));
555
Matt Spinler03984582020-04-09 13:17:58 -0500556 SRC src{entry, ad, dataIface};
557
558 auto& callouts = src.callouts()->callouts();
559 ASSERT_EQ(callouts.size(), 2);
560
561 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
562 EXPECT_EQ(callouts[0]->priority(), 'H');
563
564 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
565 EXPECT_EQ(callouts[1]->priority(), 'M');
566
567 auto& fru1 = callouts[0]->fruIdentity();
568 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
569 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
570 EXPECT_FALSE(fru1->getMaintProc());
571 EXPECT_FALSE(fru1->getSN());
572 EXPECT_FALSE(fru1->getCCIN());
573
574 auto& fru2 = callouts[1]->fruIdentity();
Matt Spinlerea2873d2021-08-18 10:35:40 -0500575 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
Matt Spinler03984582020-04-09 13:17:58 -0500576 EXPECT_EQ(fru2->failingComponentType(),
577 src::FRUIdentity::maintenanceProc);
578 EXPECT_FALSE(fru2->getPN());
579 EXPECT_FALSE(fru2->getSN());
580 EXPECT_FALSE(fru2->getCCIN());
581 }
582
583 {
584 // Call out a trusted symbolic FRU with a location code, and
585 // another one without.
586 AdditionalData ad;
587 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500588 std::vector<std::string> names{"systemB"};
589
Matt Spinleraf191c72020-06-04 11:35:13 -0500590 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500591 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500592
Sumit Kumar9d43a722021-08-24 09:46:19 -0500593 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
594 "system/entry"};
595 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
596 .WillOnce(Return(std::vector<bool>{false, false, false}));
597
Matt Spinler03984582020-04-09 13:17:58 -0500598 SRC src{entry, ad, dataIface};
599
600 auto& callouts = src.callouts()->callouts();
601 EXPECT_EQ(callouts.size(), 2);
602
603 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
604 EXPECT_EQ(callouts[0]->priority(), 'H');
605
606 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
607 EXPECT_EQ(callouts[1]->priority(), 'M');
608
609 auto& fru1 = callouts[0]->fruIdentity();
610 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
611 EXPECT_EQ(fru1->failingComponentType(),
612 src::FRUIdentity::symbolicFRUTrustedLocCode);
613 EXPECT_FALSE(fru1->getMaintProc());
614 EXPECT_FALSE(fru1->getSN());
615 EXPECT_FALSE(fru1->getCCIN());
616
617 // It asked for a trusted symbolic FRU, but no location code
618 // was provided so it is switched back to a normal one
619 auto& fru2 = callouts[1]->fruIdentity();
620 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
621 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
622 EXPECT_FALSE(fru2->getMaintProc());
623 EXPECT_FALSE(fru2->getSN());
624 EXPECT_FALSE(fru2->getCCIN());
625 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500626
627 {
628 // Two hardware callouts
629 AdditionalData ad;
630 NiceMock<MockDataInterface> dataIface;
631 std::vector<std::string> names{"systemC"};
632
Sumit Kumar9d43a722021-08-24 09:46:19 -0500633 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
634 "system/entry"};
635 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
636 .WillOnce(Return(std::vector<bool>{false, false, false}));
637
Matt Spinler1ab66962020-10-29 13:21:44 -0500638 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500639
640 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
641 .WillOnce(Return("UXXX-P0-C8"));
642
643 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
644 .WillOnce(Return("UXXX-P0-C9"));
645
Matt Spinler2f9225a2020-08-05 12:58:49 -0500646 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500647 .WillOnce(Return(
648 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
649
Matt Spinler2f9225a2020-08-05 12:58:49 -0500650 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinleraf191c72020-06-04 11:35:13 -0500651 .WillOnce(Return(
652 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
653
654 EXPECT_CALL(
655 dataIface,
656 getHWCalloutFields(
657 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
658 _))
659 .Times(1)
660 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
661 SetArgReferee<2>("CCCC"),
662 SetArgReferee<3>("123456789ABC")));
663
664 EXPECT_CALL(
665 dataIface,
666 getHWCalloutFields(
667 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
668 _))
669 .Times(1)
670 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
671 SetArgReferee<2>("DDDD"),
672 SetArgReferee<3>("23456789ABCD")));
673
674 SRC src{entry, ad, dataIface};
675
676 auto& callouts = src.callouts()->callouts();
677 EXPECT_EQ(callouts.size(), 2);
678
679 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
680 EXPECT_EQ(callouts[0]->priority(), 'H');
681
682 auto& fru1 = callouts[0]->fruIdentity();
683 EXPECT_EQ(fru1->getPN().value(), "1234567");
684 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
685 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
686
687 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
688 EXPECT_EQ(callouts[1]->priority(), 'M');
689
690 auto& fru2 = callouts[1]->fruIdentity();
691 EXPECT_EQ(fru2->getPN().value(), "2345678");
692 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
693 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
694 }
Matt Spinler03984582020-04-09 13:17:58 -0500695}
Matt Spinler717de422020-06-04 13:10:14 -0500696
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500697// Test that a symbolic FRU with a trusted location code callout
698// from the registry can get its location from the
699// CALLOUT_INVENTORY_PATH AdditionalData entry.
700TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
701{
702 message::Entry entry;
703 entry.src.type = 0xBD;
704 entry.src.reasonCode = 0xABCD;
705 entry.subsystem = 0x42;
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500706
707 entry.callouts = R"(
708 [{
709 "CalloutList":
710 [
711 {
712 "Priority": "high",
713 "SymbolicFRUTrusted": "service_docs",
714 "UseInventoryLocCode": true
715 },
716 {
717 "Priority": "medium",
718 "LocCode": "P0-C8",
719 "SymbolicFRUTrusted": "pwrsply"
720 }
721 ]
722 }])"_json;
723
724 {
725 // The location code for the first symbolic FRU callout will
726 // come from this inventory path since UseInventoryLocCode is set.
727 // In this case there will be no normal FRU callout for the motherboard.
728 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
729 AdditionalData ad{adData};
730 NiceMock<MockDataInterface> dataIface;
731 std::vector<std::string> names{"systemA"};
732
Sumit Kumar9d43a722021-08-24 09:46:19 -0500733 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
734 "system/entry"};
735 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
736 .WillOnce(Return(std::vector<bool>{false, false, false}));
737
Matt Spinler1ab66962020-10-29 13:21:44 -0500738 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500739
740 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
741 .Times(1)
742 .WillOnce(Return("Ufcs-P10"));
743
744 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
745 .WillOnce(Return("Ufcs-P0-C8"));
746
747 SRC src{entry, ad, dataIface};
748
749 auto& callouts = src.callouts()->callouts();
750 EXPECT_EQ(callouts.size(), 2);
751
752 // The location code for the first symbolic FRU callout with a
753 // trusted location code comes from the motherboard.
754 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
755 EXPECT_EQ(callouts[0]->priority(), 'H');
756 auto& fru1 = callouts[0]->fruIdentity();
757 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
758 EXPECT_EQ(fru1->failingComponentType(),
759 src::FRUIdentity::symbolicFRUTrustedLocCode);
760
761 // The second trusted symbolic FRU callouts uses the location
762 // code in the registry as usual.
763 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
764 EXPECT_EQ(callouts[1]->priority(), 'M');
765 auto& fru2 = callouts[1]->fruIdentity();
766 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
767 EXPECT_EQ(fru2->failingComponentType(),
768 src::FRUIdentity::symbolicFRUTrustedLocCode);
769 }
770
771 {
772 // This time say we want to use the location code from
773 // the inventory, but don't pass it in and the callout should
774 // end up a regular symbolic FRU
775 entry.callouts = R"(
776 [{
777 "CalloutList":
778 [
779 {
780 "Priority": "high",
781 "SymbolicFRUTrusted": "service_docs",
782 "UseInventoryLocCode": true
783 }
784 ]
785 }])"_json;
786
787 AdditionalData ad;
788 NiceMock<MockDataInterface> dataIface;
789 std::vector<std::string> names{"systemA"};
790
Sumit Kumar9d43a722021-08-24 09:46:19 -0500791 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
792 "system/entry"};
793 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
794 .WillOnce(Return(std::vector<bool>{false, false, false}));
795
Matt Spinler1ab66962020-10-29 13:21:44 -0500796 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500797
798 SRC src{entry, ad, dataIface};
799
800 auto& callouts = src.callouts()->callouts();
801 EXPECT_EQ(callouts.size(), 1);
802
803 EXPECT_EQ(callouts[0]->locationCode(), "");
804 EXPECT_EQ(callouts[0]->priority(), 'H');
805 auto& fru1 = callouts[0]->fruIdentity();
806 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
807 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
808 }
809}
810
Matt Spinler717de422020-06-04 13:10:14 -0500811// Test looking up device path fails in the callout jSON.
812TEST_F(SRCTest, DevicePathCalloutTest)
813{
814 message::Entry entry;
815 entry.src.type = 0xBD;
816 entry.src.reasonCode = 0xABCD;
817 entry.subsystem = 0x42;
Matt Spinler717de422020-06-04 13:10:14 -0500818
819 const auto calloutJSON = R"(
820 {
821 "I2C":
822 {
823 "14":
824 {
825 "114":
826 {
827 "Callouts":[
828 {
829 "Name": "/chassis/motherboard/cpu0",
830 "LocationCode": "P1-C40",
831 "Priority": "H"
832 },
833 {
834 "Name": "/chassis/motherboard",
835 "LocationCode": "P1",
836 "Priority": "M"
837 },
838 {
839 "Name": "/chassis/motherboard/bmc",
840 "LocationCode": "P1-C15",
841 "Priority": "L"
842 }
843 ],
844 "Dest": "proc 0 target"
845 }
846 }
847 }
848 })";
849
850 auto dataPath = getPELReadOnlyDataPath();
851 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
852 file << calloutJSON;
853 file.close();
854
855 NiceMock<MockDataInterface> dataIface;
856 std::vector<std::string> names{"systemA"};
857
Sumit Kumar9d43a722021-08-24 09:46:19 -0500858 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
859 "system/entry"};
860 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
861 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
862
Matt Spinler717de422020-06-04 13:10:14 -0500863 EXPECT_CALL(dataIface, getSystemNames)
864 .Times(5)
Matt Spinler1ab66962020-10-29 13:21:44 -0500865 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500866
Matt Spinler2f9225a2020-08-05 12:58:49 -0500867 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500868 .Times(3)
869 .WillRepeatedly(
870 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
871
Matt Spinler2f9225a2020-08-05 12:58:49 -0500872 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500873 .Times(3)
874 .WillRepeatedly(
875 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
876
Matt Spinler2f9225a2020-08-05 12:58:49 -0500877 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500878 .Times(3)
879 .WillRepeatedly(
880 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
881
Matt Spinler0d92b522021-06-16 13:28:17 -0600882 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500883 .Times(3)
884 .WillRepeatedly(Return("Ufcs-P1-C40"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600885
886 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500887 .Times(3)
888 .WillRepeatedly(Return("Ufcs-P1"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600889
890 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500891 .Times(3)
892 .WillRepeatedly(Return("Ufcs-P1-C15"));
893
894 EXPECT_CALL(
895 dataIface,
896 getHWCalloutFields(
897 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
898 .Times(3)
899 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
900 SetArgReferee<2>("CCCC"),
901 SetArgReferee<3>("123456789ABC")));
902 EXPECT_CALL(
903 dataIface,
904 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
905 _, _, _))
906 .Times(3)
907 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
908 SetArgReferee<2>("MMMM"),
909 SetArgReferee<3>("CBA987654321")));
910 EXPECT_CALL(
911 dataIface,
912 getHWCalloutFields(
913 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
914 .Times(3)
915 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
916 SetArgReferee<2>("BBBB"),
917 SetArgReferee<3>("C123456789AB")));
918
919 // Call this below with different AdditionalData values that
920 // result in the same callouts.
921 auto checkCallouts = [&entry, &dataIface](const auto& items) {
922 AdditionalData ad{items};
923 SRC src{entry, ad, dataIface};
924
925 ASSERT_TRUE(src.callouts());
926 auto& callouts = src.callouts()->callouts();
927
928 ASSERT_EQ(callouts.size(), 3);
929
930 {
931 EXPECT_EQ(callouts[0]->priority(), 'H');
932 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
933
934 auto& fru = callouts[0]->fruIdentity();
935 EXPECT_EQ(fru->getPN().value(), "1234567");
936 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
937 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
938 }
939 {
940 EXPECT_EQ(callouts[1]->priority(), 'M');
941 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
942
943 auto& fru = callouts[1]->fruIdentity();
944 EXPECT_EQ(fru->getPN().value(), "7654321");
945 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
946 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
947 }
948 {
949 EXPECT_EQ(callouts[2]->priority(), 'L');
950 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
951
952 auto& fru = callouts[2]->fruIdentity();
953 EXPECT_EQ(fru->getPN().value(), "7123456");
954 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
955 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
956 }
957 };
958
959 {
960 // Callouts based on the device path
961 std::vector<std::string> items{
962 "CALLOUT_ERRNO=5",
963 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
964 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
965
966 checkCallouts(items);
967 }
968
969 {
970 // Callouts based on the I2C bus and address
971 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
972 "CALLOUT_IIC_ADDR=0x72"};
973 checkCallouts(items);
974 }
975
976 {
977 // Also based on I2C bus and address, but with bus = /dev/i2c-14
978 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
979 "CALLOUT_IIC_ADDR=0x72"};
980 checkCallouts(items);
981 }
982
983 {
984 // Callout not found
985 std::vector<std::string> items{
986 "CALLOUT_ERRNO=5",
987 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
988 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
989
990 AdditionalData ad{items};
991 SRC src{entry, ad, dataIface};
992
993 EXPECT_FALSE(src.callouts());
994 ASSERT_EQ(src.getDebugData().size(), 1);
995 EXPECT_EQ(src.getDebugData()[0],
996 "Problem looking up I2C callouts on 24 18: "
997 "[json.exception.out_of_range.403] key '24' not found");
998 }
999
1000 {
1001 // Callout not found
1002 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
1003 "CALLOUT_IIC_ADDR=0x99"};
1004 AdditionalData ad{items};
1005 SRC src{entry, ad, dataIface};
1006
1007 EXPECT_FALSE(src.callouts());
1008 ASSERT_EQ(src.getDebugData().size(), 1);
1009 EXPECT_EQ(src.getDebugData()[0],
1010 "Problem looking up I2C callouts on 22 153: "
1011 "[json.exception.out_of_range.403] key '22' not found");
1012 }
1013
1014 fs::remove_all(dataPath);
1015}
Matt Spinler3bdd0112020-08-27 10:24:34 -05001016
1017// Test when callouts are passed in via JSON
1018TEST_F(SRCTest, JsonCalloutsTest)
1019{
1020 const auto jsonCallouts = R"(
1021 [
1022 {
1023 "LocationCode": "P0-C1",
1024 "Priority": "H",
1025 "MRUs": [
1026 {
1027 "ID": 42,
1028 "Priority": "H"
1029 },
1030 {
1031 "ID": 43,
1032 "Priority": "M"
1033 }
1034 ]
1035 },
1036 {
1037 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1038 "Priority": "M",
1039 "Guarded": true,
1040 "Deconfigured": true
1041 },
1042 {
1043 "Procedure": "PROCEDU",
1044 "Priority": "A"
1045 },
1046 {
1047 "SymbolicFRU": "TRUSTED",
1048 "Priority": "B",
1049 "TrustedLocationCode": true,
1050 "LocationCode": "P1-C23"
1051 },
1052 {
1053 "SymbolicFRU": "FRUTST1",
1054 "Priority": "C",
1055 "LocationCode": "P1-C24"
1056 },
1057 {
1058 "SymbolicFRU": "FRUTST2LONG",
1059 "Priority": "L"
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001060 },
1061 {
1062 "Procedure": "fsi_path",
1063 "Priority": "L"
1064 },
1065 {
1066 "SymbolicFRU": "ambient_temp",
1067 "Priority": "L"
Matt Spinler3bdd0112020-08-27 10:24:34 -05001068 }
1069 ]
1070 )"_json;
1071
1072 message::Entry entry;
1073 entry.src.type = 0xBD;
1074 entry.src.reasonCode = 0xABCD;
1075 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001076
1077 AdditionalData ad;
1078 NiceMock<MockDataInterface> dataIface;
1079
Sumit Kumar9d43a722021-08-24 09:46:19 -05001080 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1081 "system/entry"};
1082 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1083 .WillOnce(Return(std::vector<bool>{false, false, false}));
1084
Matt Spinler3bdd0112020-08-27 10:24:34 -05001085 // Callout 0 mock calls
1086 {
1087 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1088 .Times(1)
1089 .WillOnce(Return("UXXX-P0-C1"));
1090 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1091 .Times(1)
1092 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1093 EXPECT_CALL(
1094 dataIface,
1095 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1096 .Times(1)
1097 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1098 SetArgReferee<2>("CCCC"),
1099 SetArgReferee<3>("123456789ABC")));
1100 }
1101 // Callout 1 mock calls
1102 {
1103 EXPECT_CALL(dataIface,
1104 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1105 .WillOnce(Return("UYYY-P5"));
1106 EXPECT_CALL(
1107 dataIface,
1108 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1109 .Times(1)
1110 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1111 SetArgReferee<2>("DDDD"),
1112 SetArgReferee<3>("23456789ABCD")));
1113 }
1114 // Callout 3 mock calls
1115 {
1116 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1117 .Times(1)
1118 .WillOnce(Return("UXXX-P1-C23"));
1119 }
1120 // Callout 4 mock calls
1121 {
1122 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1123 .Times(1)
1124 .WillOnce(Return("UXXX-P1-C24"));
1125 }
1126
1127 SRC src{entry, ad, jsonCallouts, dataIface};
1128 ASSERT_TRUE(src.callouts());
1129
Matt Spinlerafa2c792020-08-27 11:01:39 -05001130 // Check the guarded and deconfigured flags
1131 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1132
Matt Spinler3bdd0112020-08-27 10:24:34 -05001133 const auto& callouts = src.callouts()->callouts();
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001134 ASSERT_EQ(callouts.size(), 8);
Matt Spinler3bdd0112020-08-27 10:24:34 -05001135
1136 // Check callout 0
1137 {
1138 EXPECT_EQ(callouts[0]->priority(), 'H');
1139 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1140
1141 auto& fru = callouts[0]->fruIdentity();
1142 EXPECT_EQ(fru->getPN().value(), "1234567");
1143 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1144 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1145 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001146
1147 auto& mruCallouts = callouts[0]->mru();
1148 ASSERT_TRUE(mruCallouts);
1149 auto& mrus = mruCallouts->mrus();
1150 ASSERT_EQ(mrus.size(), 2);
1151 EXPECT_EQ(mrus[0].id, 42);
1152 EXPECT_EQ(mrus[0].priority, 'H');
1153 EXPECT_EQ(mrus[1].id, 43);
1154 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001155 }
1156
1157 // Check callout 1
1158 {
1159 EXPECT_EQ(callouts[1]->priority(), 'M');
1160 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1161
1162 auto& fru = callouts[1]->fruIdentity();
1163 EXPECT_EQ(fru->getPN().value(), "2345678");
1164 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1165 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1166 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1167 }
1168
1169 // Check callout 2
1170 {
1171 EXPECT_EQ(callouts[2]->priority(), 'A');
1172 EXPECT_EQ(callouts[2]->locationCode(), "");
1173
1174 auto& fru = callouts[2]->fruIdentity();
1175 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1176 EXPECT_EQ(fru->failingComponentType(),
1177 src::FRUIdentity::maintenanceProc);
1178 }
1179
1180 // Check callout 3
1181 {
1182 EXPECT_EQ(callouts[3]->priority(), 'B');
1183 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1184
1185 auto& fru = callouts[3]->fruIdentity();
1186 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1187 EXPECT_EQ(fru->failingComponentType(),
1188 src::FRUIdentity::symbolicFRUTrustedLocCode);
1189 }
1190
1191 // Check callout 4
1192 {
1193 EXPECT_EQ(callouts[4]->priority(), 'C');
1194 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1195
1196 auto& fru = callouts[4]->fruIdentity();
1197 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1198 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1199 }
1200
1201 // Check callout 5
1202 {
1203 EXPECT_EQ(callouts[5]->priority(), 'L');
1204 EXPECT_EQ(callouts[5]->locationCode(), "");
1205
1206 auto& fru = callouts[5]->fruIdentity();
1207 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1208 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1209 }
1210
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001211 // Check callout 6
1212 {
1213 EXPECT_EQ(callouts[6]->priority(), 'L');
1214 EXPECT_EQ(callouts[6]->locationCode(), "");
1215
1216 auto& fru = callouts[6]->fruIdentity();
1217 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004");
1218 EXPECT_EQ(fru->failingComponentType(),
1219 src::FRUIdentity::maintenanceProc);
1220 }
1221
1222 // Check callout 7
1223 {
1224 EXPECT_EQ(callouts[7]->priority(), 'L');
1225 EXPECT_EQ(callouts[7]->locationCode(), "");
1226
1227 auto& fru = callouts[7]->fruIdentity();
1228 EXPECT_EQ(fru->getPN().value(), "AMBTEMP");
1229 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1230 }
1231
Matt Spinler3bdd0112020-08-27 10:24:34 -05001232 // Check that it didn't find any errors
1233 const auto& data = src.getDebugData();
1234 EXPECT_TRUE(data.empty());
1235}
1236
1237TEST_F(SRCTest, JsonBadCalloutsTest)
1238{
1239 // The first call will have a Throw in a mock call.
1240 // The second will have a different Throw in a mock call.
1241 // The others have issues with the Priority field.
1242 const auto jsonCallouts = R"(
1243 [
1244 {
1245 "LocationCode": "P0-C1",
1246 "Priority": "H"
1247 },
1248 {
1249 "LocationCode": "P0-C2",
1250 "Priority": "H"
1251 },
1252 {
1253 "LocationCode": "P0-C3"
1254 },
1255 {
1256 "LocationCode": "P0-C4",
1257 "Priority": "X"
1258 }
1259 ]
1260 )"_json;
1261
1262 message::Entry entry;
1263 entry.src.type = 0xBD;
1264 entry.src.reasonCode = 0xABCD;
1265 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001266
1267 AdditionalData ad;
1268 NiceMock<MockDataInterface> dataIface;
1269
Sumit Kumar9d43a722021-08-24 09:46:19 -05001270 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1271 "system/entry"};
1272 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1273 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1274
Matt Spinler3bdd0112020-08-27 10:24:34 -05001275 // Callout 0 mock calls
1276 // Expand location code will fail, so the unexpanded location
1277 // code should show up in the callout instead.
1278 {
1279 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1280 .WillOnce(Throw(std::runtime_error("Fail")));
1281
1282 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1283 .Times(1)
1284 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1285 EXPECT_CALL(
1286 dataIface,
1287 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1288 .Times(1)
1289 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1290 SetArgReferee<2>("CCCC"),
1291 SetArgReferee<3>("123456789ABC")));
1292 }
1293
1294 // Callout 1 mock calls
1295 // getInventoryFromLocCode will fail
1296 {
1297 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1298 .Times(1)
1299 .WillOnce(Return("UXXX-P0-C2"));
1300
1301 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1302 .Times(1)
1303 .WillOnce(Throw(std::runtime_error("Fail")));
1304 }
1305
1306 SRC src{entry, ad, jsonCallouts, dataIface};
1307
1308 ASSERT_TRUE(src.callouts());
1309
1310 const auto& callouts = src.callouts()->callouts();
1311
1312 // Only the first callout was successful
1313 ASSERT_EQ(callouts.size(), 1);
1314
1315 {
1316 EXPECT_EQ(callouts[0]->priority(), 'H');
1317 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1318
1319 auto& fru = callouts[0]->fruIdentity();
1320 EXPECT_EQ(fru->getPN().value(), "1234567");
1321 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1322 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1323 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1324 }
1325
1326 const auto& data = src.getDebugData();
1327 ASSERT_EQ(data.size(), 4);
1328 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1329 EXPECT_STREQ(data[1].c_str(),
1330 "Failed extracting callout data from JSON: Unable to "
1331 "get inventory path from location code: P0-C2: Fail");
1332 EXPECT_STREQ(data[2].c_str(),
1333 "Failed extracting callout data from JSON: "
1334 "[json.exception.out_of_range.403] key 'Priority' not found");
1335 EXPECT_STREQ(data[3].c_str(),
1336 "Failed extracting callout data from JSON: Invalid "
1337 "priority 'X' found in JSON callout");
1338}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001339
1340// Test that an inventory path callout can have
1341// a different priority than H.
1342TEST_F(SRCTest, InventoryCalloutTestPriority)
1343{
1344 message::Entry entry;
1345 entry.src.type = 0xBD;
1346 entry.src.reasonCode = 0xABCD;
1347 entry.subsystem = 0x42;
Miguel Gomez53ef1552020-10-14 21:16:32 +00001348
1349 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1350 "CALLOUT_PRIORITY=M"};
1351 AdditionalData ad{adData};
1352 NiceMock<MockDataInterface> dataIface;
1353
Sumit Kumar9d43a722021-08-24 09:46:19 -05001354 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1355 "system/entry"};
1356 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1357 .WillOnce(Return(std::vector<bool>{false, false, false}));
1358
Miguel Gomez53ef1552020-10-14 21:16:32 +00001359 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1360 .WillOnce(Return("UTMS-P1"));
1361
1362 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1363 .Times(1)
1364 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1365 SetArgReferee<3>("123456789ABC")));
1366
1367 SRC src{entry, ad, dataIface};
1368 EXPECT_TRUE(src.valid());
1369
1370 ASSERT_TRUE(src.callouts());
1371
1372 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1373
1374 auto& callout = src.callouts()->callouts().front();
1375
1376 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1377 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001378}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001379
1380// Test for bmc & platform dump status bits
1381TEST_F(SRCTest, DumpStatusBitsCheck)
1382{
1383 message::Entry entry;
1384 entry.src.type = 0xBD;
1385 entry.src.reasonCode = 0xABCD;
1386 entry.subsystem = 0x42;
Sumit Kumar9d43a722021-08-24 09:46:19 -05001387
1388 AdditionalData ad;
1389 NiceMock<MockDataInterface> dataIface;
1390 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1391 "system/entry"};
1392
1393 {
1394 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1395 .WillOnce(Return(std::vector<bool>{true, false, false}));
1396
1397 SRC src{entry, ad, dataIface};
1398 EXPECT_TRUE(src.valid());
1399
1400 const auto& hexwords = src.hexwordData();
1401 EXPECT_EQ(0x00080055, hexwords[0]);
1402 }
1403
1404 {
1405 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1406 .WillOnce(Return(std::vector<bool>{false, true, false}));
1407
1408 SRC src{entry, ad, dataIface};
1409 EXPECT_TRUE(src.valid());
1410
1411 const auto& hexwords = src.hexwordData();
1412 EXPECT_EQ(0x00000255, hexwords[0]);
1413 }
1414
1415 {
1416 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1417 .WillOnce(Return(std::vector<bool>{false, false, true}));
1418
1419 SRC src{entry, ad, dataIface};
1420 EXPECT_TRUE(src.valid());
1421
1422 const auto& hexwords = src.hexwordData();
1423 EXPECT_EQ(0x00000455, hexwords[0]);
1424 }
1425
1426 {
1427 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1428 .WillOnce(Return(std::vector<bool>{true, true, true}));
1429
1430 SRC src{entry, ad, dataIface};
1431 EXPECT_TRUE(src.valid());
1432
1433 const auto& hexwords = src.hexwordData();
1434 EXPECT_EQ(0x00080655, hexwords[0]);
1435 }
1436}
Sumit Kumar50bfa692022-01-06 06:48:26 -06001437
1438// Test SRC with additional data - PEL_SUBSYSTEM
1439TEST_F(SRCTest, TestPELSubsystem)
1440{
1441 message::Entry entry;
1442 entry.src.type = 0xBD;
1443 entry.src.reasonCode = 0xABCD;
1444 entry.subsystem = 0x42;
Sumit Kumar50bfa692022-01-06 06:48:26 -06001445
1446 // Values for the SRC words pointed to above
1447 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
1448 AdditionalData ad{adData};
1449 NiceMock<MockDataInterface> dataIface;
1450
1451 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1452
1453 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1454 "system/entry"};
1455 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1456 .WillOnce(Return(std::vector<bool>{false, false, false}));
1457
1458 SRC src{entry, ad, dataIface};
1459
1460 EXPECT_TRUE(src.valid());
1461
1462 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1463}
Vijay Lobo875b6c72021-10-20 17:38:56 -05001464
1465void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1466{
1467 assert(40 + value.size() <= src.size());
1468
1469 for (size_t i = 0; i < value.size(); i++)
1470 {
1471 src[40 + i] = value[i];
1472 }
1473}
1474
1475TEST_F(SRCTest, TestGetProgressCode)
1476{
1477 {
1478 // A real SRC with CC009184
1479 std::vector<uint8_t> src{
1480 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1481 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1482 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1483 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1484 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1485
1486 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1487 }
1488
1489 {
1490 // A real SRC with STANDBY
1491 std::vector<uint8_t> src{
1492 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1493 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1494 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1495 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1496 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1497
1498 EXPECT_EQ(SRC::getProgressCode(src), 0);
1499 }
1500
1501 {
1502 // A real SRC with CC009184, but 1 byte too short
1503 std::vector<uint8_t> src{
1504 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1505 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1507 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1508 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1509 src.resize(71);
1510 EXPECT_EQ(SRC::getProgressCode(src), 0);
1511 }
1512
1513 {
1514 // A few different ones
1515 const std::map<std::string, uint32_t> progressCodes{
1516 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1517 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1518 {"1234567X", 0}, {"1 ", 0}};
1519
1520 std::vector<uint8_t> src(72, 0x0);
1521
1522 for (const auto& [code, expected] : progressCodes)
1523 {
1524 setAsciiString(src, code);
1525 EXPECT_EQ(SRC::getProgressCode(src), expected);
1526 }
1527
1528 // empty
1529 src.clear();
1530 EXPECT_EQ(SRC::getProgressCode(src), 0);
1531 }
1532}
1533
1534// Test progress is in right SRC hex data field
1535TEST_F(SRCTest, TestProgressCodeField)
1536{
1537 message::Entry entry;
1538 entry.src.type = 0xBD;
1539 entry.src.reasonCode = 0xABCD;
1540 entry.subsystem = 0x42;
1541
1542 AdditionalData ad;
1543 NiceMock<MockDataInterface> dataIface;
1544 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1545 "system/entry"};
1546 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1547 .WillOnce(Return(std::vector<bool>{false, false, false}));
1548 EXPECT_CALL(dataIface, getRawProgressSRC())
1549 .WillOnce(Return(std::vector<uint8_t>{
1550 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1551 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1552 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1553 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1554 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1555
1556 SRC src{entry, ad, dataIface};
1557 EXPECT_TRUE(src.valid());
1558
1559 // Verify that the hex vlue is set at the right hexword
1560 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1561}