blob: ea497a3e34b3e223b476b88f3b03a1bf636bc68f [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"
1060 }
1061 ]
1062 )"_json;
1063
1064 message::Entry entry;
1065 entry.src.type = 0xBD;
1066 entry.src.reasonCode = 0xABCD;
1067 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001068
1069 AdditionalData ad;
1070 NiceMock<MockDataInterface> dataIface;
1071
Sumit Kumar9d43a722021-08-24 09:46:19 -05001072 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1073 "system/entry"};
1074 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1075 .WillOnce(Return(std::vector<bool>{false, false, false}));
1076
Matt Spinler3bdd0112020-08-27 10:24:34 -05001077 // Callout 0 mock calls
1078 {
1079 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1080 .Times(1)
1081 .WillOnce(Return("UXXX-P0-C1"));
1082 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1083 .Times(1)
1084 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1085 EXPECT_CALL(
1086 dataIface,
1087 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1088 .Times(1)
1089 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1090 SetArgReferee<2>("CCCC"),
1091 SetArgReferee<3>("123456789ABC")));
1092 }
1093 // Callout 1 mock calls
1094 {
1095 EXPECT_CALL(dataIface,
1096 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1097 .WillOnce(Return("UYYY-P5"));
1098 EXPECT_CALL(
1099 dataIface,
1100 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1101 .Times(1)
1102 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1103 SetArgReferee<2>("DDDD"),
1104 SetArgReferee<3>("23456789ABCD")));
1105 }
1106 // Callout 3 mock calls
1107 {
1108 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1109 .Times(1)
1110 .WillOnce(Return("UXXX-P1-C23"));
1111 }
1112 // Callout 4 mock calls
1113 {
1114 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1115 .Times(1)
1116 .WillOnce(Return("UXXX-P1-C24"));
1117 }
1118
1119 SRC src{entry, ad, jsonCallouts, dataIface};
1120 ASSERT_TRUE(src.callouts());
1121
Matt Spinlerafa2c792020-08-27 11:01:39 -05001122 // Check the guarded and deconfigured flags
1123 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1124
Matt Spinler3bdd0112020-08-27 10:24:34 -05001125 const auto& callouts = src.callouts()->callouts();
1126 ASSERT_EQ(callouts.size(), 6);
1127
1128 // Check callout 0
1129 {
1130 EXPECT_EQ(callouts[0]->priority(), 'H');
1131 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1132
1133 auto& fru = callouts[0]->fruIdentity();
1134 EXPECT_EQ(fru->getPN().value(), "1234567");
1135 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1136 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1137 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001138
1139 auto& mruCallouts = callouts[0]->mru();
1140 ASSERT_TRUE(mruCallouts);
1141 auto& mrus = mruCallouts->mrus();
1142 ASSERT_EQ(mrus.size(), 2);
1143 EXPECT_EQ(mrus[0].id, 42);
1144 EXPECT_EQ(mrus[0].priority, 'H');
1145 EXPECT_EQ(mrus[1].id, 43);
1146 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001147 }
1148
1149 // Check callout 1
1150 {
1151 EXPECT_EQ(callouts[1]->priority(), 'M');
1152 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1153
1154 auto& fru = callouts[1]->fruIdentity();
1155 EXPECT_EQ(fru->getPN().value(), "2345678");
1156 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1157 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1158 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1159 }
1160
1161 // Check callout 2
1162 {
1163 EXPECT_EQ(callouts[2]->priority(), 'A');
1164 EXPECT_EQ(callouts[2]->locationCode(), "");
1165
1166 auto& fru = callouts[2]->fruIdentity();
1167 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1168 EXPECT_EQ(fru->failingComponentType(),
1169 src::FRUIdentity::maintenanceProc);
1170 }
1171
1172 // Check callout 3
1173 {
1174 EXPECT_EQ(callouts[3]->priority(), 'B');
1175 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1176
1177 auto& fru = callouts[3]->fruIdentity();
1178 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1179 EXPECT_EQ(fru->failingComponentType(),
1180 src::FRUIdentity::symbolicFRUTrustedLocCode);
1181 }
1182
1183 // Check callout 4
1184 {
1185 EXPECT_EQ(callouts[4]->priority(), 'C');
1186 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1187
1188 auto& fru = callouts[4]->fruIdentity();
1189 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1190 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1191 }
1192
1193 // Check callout 5
1194 {
1195 EXPECT_EQ(callouts[5]->priority(), 'L');
1196 EXPECT_EQ(callouts[5]->locationCode(), "");
1197
1198 auto& fru = callouts[5]->fruIdentity();
1199 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1200 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1201 }
1202
1203 // Check that it didn't find any errors
1204 const auto& data = src.getDebugData();
1205 EXPECT_TRUE(data.empty());
1206}
1207
1208TEST_F(SRCTest, JsonBadCalloutsTest)
1209{
1210 // The first call will have a Throw in a mock call.
1211 // The second will have a different Throw in a mock call.
1212 // The others have issues with the Priority field.
1213 const auto jsonCallouts = R"(
1214 [
1215 {
1216 "LocationCode": "P0-C1",
1217 "Priority": "H"
1218 },
1219 {
1220 "LocationCode": "P0-C2",
1221 "Priority": "H"
1222 },
1223 {
1224 "LocationCode": "P0-C3"
1225 },
1226 {
1227 "LocationCode": "P0-C4",
1228 "Priority": "X"
1229 }
1230 ]
1231 )"_json;
1232
1233 message::Entry entry;
1234 entry.src.type = 0xBD;
1235 entry.src.reasonCode = 0xABCD;
1236 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001237
1238 AdditionalData ad;
1239 NiceMock<MockDataInterface> dataIface;
1240
Sumit Kumar9d43a722021-08-24 09:46:19 -05001241 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1242 "system/entry"};
1243 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1244 .WillRepeatedly(Return(std::vector<bool>{false, false, false}));
1245
Matt Spinler3bdd0112020-08-27 10:24:34 -05001246 // Callout 0 mock calls
1247 // Expand location code will fail, so the unexpanded location
1248 // code should show up in the callout instead.
1249 {
1250 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1251 .WillOnce(Throw(std::runtime_error("Fail")));
1252
1253 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1254 .Times(1)
1255 .WillOnce(Return("/inv/system/chassis/motherboard/bmc"));
1256 EXPECT_CALL(
1257 dataIface,
1258 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1259 .Times(1)
1260 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1261 SetArgReferee<2>("CCCC"),
1262 SetArgReferee<3>("123456789ABC")));
1263 }
1264
1265 // Callout 1 mock calls
1266 // getInventoryFromLocCode will fail
1267 {
1268 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1269 .Times(1)
1270 .WillOnce(Return("UXXX-P0-C2"));
1271
1272 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1273 .Times(1)
1274 .WillOnce(Throw(std::runtime_error("Fail")));
1275 }
1276
1277 SRC src{entry, ad, jsonCallouts, dataIface};
1278
1279 ASSERT_TRUE(src.callouts());
1280
1281 const auto& callouts = src.callouts()->callouts();
1282
1283 // Only the first callout was successful
1284 ASSERT_EQ(callouts.size(), 1);
1285
1286 {
1287 EXPECT_EQ(callouts[0]->priority(), 'H');
1288 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1289
1290 auto& fru = callouts[0]->fruIdentity();
1291 EXPECT_EQ(fru->getPN().value(), "1234567");
1292 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1293 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1294 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1295 }
1296
1297 const auto& data = src.getDebugData();
1298 ASSERT_EQ(data.size(), 4);
1299 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1300 EXPECT_STREQ(data[1].c_str(),
1301 "Failed extracting callout data from JSON: Unable to "
1302 "get inventory path from location code: P0-C2: Fail");
1303 EXPECT_STREQ(data[2].c_str(),
1304 "Failed extracting callout data from JSON: "
1305 "[json.exception.out_of_range.403] key 'Priority' not found");
1306 EXPECT_STREQ(data[3].c_str(),
1307 "Failed extracting callout data from JSON: Invalid "
1308 "priority 'X' found in JSON callout");
1309}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001310
1311// Test that an inventory path callout can have
1312// a different priority than H.
1313TEST_F(SRCTest, InventoryCalloutTestPriority)
1314{
1315 message::Entry entry;
1316 entry.src.type = 0xBD;
1317 entry.src.reasonCode = 0xABCD;
1318 entry.subsystem = 0x42;
Miguel Gomez53ef1552020-10-14 21:16:32 +00001319
1320 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1321 "CALLOUT_PRIORITY=M"};
1322 AdditionalData ad{adData};
1323 NiceMock<MockDataInterface> dataIface;
1324
Sumit Kumar9d43a722021-08-24 09:46:19 -05001325 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1326 "system/entry"};
1327 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1328 .WillOnce(Return(std::vector<bool>{false, false, false}));
1329
Miguel Gomez53ef1552020-10-14 21:16:32 +00001330 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1331 .WillOnce(Return("UTMS-P1"));
1332
1333 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1334 .Times(1)
1335 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1336 SetArgReferee<3>("123456789ABC")));
1337
1338 SRC src{entry, ad, dataIface};
1339 EXPECT_TRUE(src.valid());
1340
1341 ASSERT_TRUE(src.callouts());
1342
1343 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1344
1345 auto& callout = src.callouts()->callouts().front();
1346
1347 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1348 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001349}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001350
1351// Test for bmc & platform dump status bits
1352TEST_F(SRCTest, DumpStatusBitsCheck)
1353{
1354 message::Entry entry;
1355 entry.src.type = 0xBD;
1356 entry.src.reasonCode = 0xABCD;
1357 entry.subsystem = 0x42;
Sumit Kumar9d43a722021-08-24 09:46:19 -05001358
1359 AdditionalData ad;
1360 NiceMock<MockDataInterface> dataIface;
1361 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1362 "system/entry"};
1363
1364 {
1365 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1366 .WillOnce(Return(std::vector<bool>{true, false, false}));
1367
1368 SRC src{entry, ad, dataIface};
1369 EXPECT_TRUE(src.valid());
1370
1371 const auto& hexwords = src.hexwordData();
1372 EXPECT_EQ(0x00080055, hexwords[0]);
1373 }
1374
1375 {
1376 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1377 .WillOnce(Return(std::vector<bool>{false, true, false}));
1378
1379 SRC src{entry, ad, dataIface};
1380 EXPECT_TRUE(src.valid());
1381
1382 const auto& hexwords = src.hexwordData();
1383 EXPECT_EQ(0x00000255, hexwords[0]);
1384 }
1385
1386 {
1387 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1388 .WillOnce(Return(std::vector<bool>{false, false, true}));
1389
1390 SRC src{entry, ad, dataIface};
1391 EXPECT_TRUE(src.valid());
1392
1393 const auto& hexwords = src.hexwordData();
1394 EXPECT_EQ(0x00000455, hexwords[0]);
1395 }
1396
1397 {
1398 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1399 .WillOnce(Return(std::vector<bool>{true, true, true}));
1400
1401 SRC src{entry, ad, dataIface};
1402 EXPECT_TRUE(src.valid());
1403
1404 const auto& hexwords = src.hexwordData();
1405 EXPECT_EQ(0x00080655, hexwords[0]);
1406 }
1407}
Sumit Kumar50bfa692022-01-06 06:48:26 -06001408
1409// Test SRC with additional data - PEL_SUBSYSTEM
1410TEST_F(SRCTest, TestPELSubsystem)
1411{
1412 message::Entry entry;
1413 entry.src.type = 0xBD;
1414 entry.src.reasonCode = 0xABCD;
1415 entry.subsystem = 0x42;
Sumit Kumar50bfa692022-01-06 06:48:26 -06001416
1417 // Values for the SRC words pointed to above
1418 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
1419 AdditionalData ad{adData};
1420 NiceMock<MockDataInterface> dataIface;
1421
1422 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1423
1424 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1425 "system/entry"};
1426 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1427 .WillOnce(Return(std::vector<bool>{false, false, false}));
1428
1429 SRC src{entry, ad, dataIface};
1430
1431 EXPECT_TRUE(src.valid());
1432
1433 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1434}