blob: bb1ba7c4044e3960ffa6c5db8ee5cbe6d51e55b2 [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
208 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500209
210 EXPECT_TRUE(src.valid());
Mike Cappsa2d7b772022-03-07 15:47:48 -0500211 EXPECT_FALSE(src.isPowerFaultEvent());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500212 EXPECT_EQ(src.size(), baseSRCSize);
213
214 const auto& hexwords = src.hexwordData();
215
216 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
217 // array index starts at 0 use the math in the [] below to make it easier
218 // to tell what is being accessed.
219 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
220 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
221 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
222 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600223 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500224
225 // Validate more fields here as the code starts filling them in.
226
227 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
Matt Spinler3fe93e92023-04-14 14:06:59 -0500228 // And that none of the error status flags are set
Matt Spinlerbd716f02019-10-15 10:54:11 -0500229 EXPECT_EQ(hexwords[5 - 2], 0);
230
231 // The user defined hex word fields specifed in the additional data.
232 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
233 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
234 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
235 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
236
237 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
238
239 // No callouts
240 EXPECT_FALSE(src.callouts());
241
242 // May as well spot check the flatten/unflatten
243 std::vector<uint8_t> data;
244 Stream stream{data};
245 src.flatten(stream);
246
247 stream.offset(0);
248 SRC newSRC{stream};
249
250 EXPECT_TRUE(newSRC.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500251 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
252 EXPECT_FALSE(newSRC.callouts());
253}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800254
Matt Spinler075e5ba2020-02-21 15:46:00 -0600255// Test when the CCIN string isn't a 4 character number
256TEST_F(SRCTest, BadCCINTest)
257{
258 message::Entry entry;
259 entry.src.type = 0xBD;
260 entry.src.reasonCode = 0xABCD;
261 entry.subsystem = 0x42;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600262
263 std::vector<std::string> adData{};
264 AdditionalData ad{adData};
265 NiceMock<MockDataInterface> dataIface;
266
267 // First it isn't a number, then it is too long,
268 // then it is empty.
269 EXPECT_CALL(dataIface, getMotherboardCCIN)
270 .WillOnce(Return("X"))
271 .WillOnce(Return("12345"))
272 .WillOnce(Return(""));
273
274 // The CCIN in the first half should still be 0 each time.
275 {
276 SRC src{entry, ad, dataIface};
277 EXPECT_TRUE(src.valid());
278 const auto& hexwords = src.hexwordData();
279 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
280 }
281
282 {
283 SRC src{entry, ad, dataIface};
284 EXPECT_TRUE(src.valid());
285 const auto& hexwords = src.hexwordData();
286 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
287 }
288
289 {
290 SRC src{entry, ad, dataIface};
291 EXPECT_TRUE(src.valid());
292 const auto& hexwords = src.hexwordData();
293 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
294 }
295}
296
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800297// Test the getErrorDetails function
298TEST_F(SRCTest, MessageSubstitutionTest)
299{
300 auto path = SRCTest::writeData(testRegistry);
301 message::Registry registry{path};
302 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
303
304 std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
305 "DURATION=30", "ERRORCODE=0x01ABCDEF"};
306 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600307 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800308
Matt Spinler075e5ba2020-02-21 15:46:00 -0600309 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800310 EXPECT_TRUE(src.valid());
311
312 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
313 ASSERT_TRUE(errorDetails);
Zane Shelley39936e32021-11-13 16:19:34 -0600314 EXPECT_EQ(errorDetails.value(),
315 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
316 "with ErrorCode 0x01ABCDEF");
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800317}
Matt Spinlered046852020-03-13 13:58:15 -0500318// Test that an inventory path callout string is
319// converted into the appropriate FRU callout.
320TEST_F(SRCTest, InventoryCalloutTest)
321{
322 message::Entry entry;
323 entry.src.type = 0xBD;
324 entry.src.reasonCode = 0xABCD;
325 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500326
327 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
328 AdditionalData ad{adData};
329 NiceMock<MockDataInterface> dataIface;
330
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500331 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
332 .WillOnce(Return("UTMS-P1"));
333
334 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500335 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500336 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
337 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500338
339 SRC src{entry, ad, dataIface};
340 EXPECT_TRUE(src.valid());
341
342 ASSERT_TRUE(src.callouts());
343
344 EXPECT_EQ(src.callouts()->callouts().size(), 1);
345
346 auto& callout = src.callouts()->callouts().front();
347
348 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500349 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500350
351 auto& fru = callout->fruIdentity();
352
353 EXPECT_EQ(fru->getPN().value(), "1234567");
354 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
355 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
356
357 // flatten and unflatten
358 std::vector<uint8_t> data;
359 Stream stream{data};
360 src.flatten(stream);
361
362 stream.offset(0);
363 SRC newSRC{stream};
364 EXPECT_TRUE(newSRC.valid());
365 ASSERT_TRUE(src.callouts());
366 EXPECT_EQ(src.callouts()->callouts().size(), 1);
367}
368
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500369// Test that when the location code can't be obtained that
Matt Spinler479b6922021-08-17 16:34:59 -0500370// no callout is added.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500371TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
372{
373 message::Entry entry;
374 entry.src.type = 0xBD;
375 entry.src.reasonCode = 0xABCD;
376 entry.subsystem = 0x42;
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500377
378 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
379 AdditionalData ad{adData};
380 NiceMock<MockDataInterface> dataIface;
381
382 auto func = []() {
383 throw sdbusplus::exception::SdBusError(5, "Error");
384 return std::string{};
385 };
386
387 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
388 .Times(1)
389 .WillOnce(InvokeWithoutArgs(func));
390
391 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
392
393 SRC src{entry, ad, dataIface};
394 EXPECT_TRUE(src.valid());
395
Matt Spinler479b6922021-08-17 16:34:59 -0500396 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500397
398 // flatten and unflatten
399 std::vector<uint8_t> data;
400 Stream stream{data};
401 src.flatten(stream);
402
403 stream.offset(0);
404 SRC newSRC{stream};
405 EXPECT_TRUE(newSRC.valid());
Matt Spinler479b6922021-08-17 16:34:59 -0500406 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500407}
408
409// Test that when the VPD can't be obtained that
410// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500411TEST_F(SRCTest, InventoryCalloutNoVPDTest)
412{
413 message::Entry entry;
414 entry.src.type = 0xBD;
415 entry.src.reasonCode = 0xABCD;
416 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500417
418 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
419 AdditionalData ad{adData};
420 NiceMock<MockDataInterface> dataIface;
421
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500422 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
423 .Times(1)
424 .WillOnce(Return("UTMS-P10"));
425
Matt Spinlered046852020-03-13 13:58:15 -0500426 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
427
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500428 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500429 .Times(1)
430 .WillOnce(InvokeWithoutArgs(func));
431
432 SRC src{entry, ad, dataIface};
433 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500434 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500435 EXPECT_EQ(src.callouts()->callouts().size(), 1);
436
437 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500438 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500439 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500440
441 auto& fru = callout->fruIdentity();
442
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500443 EXPECT_EQ(fru->getPN(), "");
444 EXPECT_EQ(fru->getCCIN(), "");
445 EXPECT_EQ(fru->getSN(), "");
446 EXPECT_FALSE(fru->getMaintProc());
447
Matt Spinlered046852020-03-13 13:58:15 -0500448 // flatten and unflatten
449 std::vector<uint8_t> data;
450 Stream stream{data};
451 src.flatten(stream);
452
453 stream.offset(0);
454 SRC newSRC{stream};
455 EXPECT_TRUE(newSRC.valid());
456 ASSERT_TRUE(src.callouts());
457 EXPECT_EQ(src.callouts()->callouts().size(), 1);
458}
Matt Spinler03984582020-04-09 13:17:58 -0500459
460TEST_F(SRCTest, RegistryCalloutTest)
461{
462 message::Entry entry;
463 entry.src.type = 0xBD;
464 entry.src.reasonCode = 0xABCD;
Matt Spinler3fe93e92023-04-14 14:06:59 -0500465 entry.src.deconfigFlag = true;
Matt Spinler03984582020-04-09 13:17:58 -0500466 entry.subsystem = 0x42;
Matt Spinler03984582020-04-09 13:17:58 -0500467
468 entry.callouts = R"(
469 [
470 {
471 "System": "systemA",
472 "CalloutList":
473 [
474 {
475 "Priority": "high",
476 "SymbolicFRU": "service_docs"
477 },
478 {
479 "Priority": "medium",
Matt Spinler479b6922021-08-17 16:34:59 -0500480 "Procedure": "bmc_code"
Matt Spinler03984582020-04-09 13:17:58 -0500481 }
482 ]
483 },
484 {
485 "System": "systemB",
486 "CalloutList":
487 [
488 {
489 "Priority": "high",
490 "LocCode": "P0-C8",
491 "SymbolicFRUTrusted": "service_docs"
492 },
493 {
494 "Priority": "medium",
495 "SymbolicFRUTrusted": "service_docs"
496 }
497 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500498 },
499 {
500 "System": "systemC",
501 "CalloutList":
502 [
503 {
504 "Priority": "high",
505 "LocCode": "P0-C8"
506 },
507 {
508 "Priority": "medium",
509 "LocCode": "P0-C9"
510 }
511 ]
Matt Spinler03984582020-04-09 13:17:58 -0500512 }
513 ])"_json;
514
515 {
516 // Call out a symbolic FRU and a procedure
517 AdditionalData ad;
518 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500519 std::vector<std::string> names{"systemA"};
520
Matt Spinler1ab66962020-10-29 13:21:44 -0500521 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500522
523 SRC src{entry, ad, dataIface};
524
Matt Spinler3fe93e92023-04-14 14:06:59 -0500525 const auto& hexwords = src.hexwordData();
526 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured);
527 EXPECT_EQ(hexwords[5 - 2] & mask, mask);
528
Matt Spinler03984582020-04-09 13:17:58 -0500529 auto& callouts = src.callouts()->callouts();
530 ASSERT_EQ(callouts.size(), 2);
531
532 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
533 EXPECT_EQ(callouts[0]->priority(), 'H');
534
535 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
536 EXPECT_EQ(callouts[1]->priority(), 'M');
537
538 auto& fru1 = callouts[0]->fruIdentity();
539 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
540 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
541 EXPECT_FALSE(fru1->getMaintProc());
542 EXPECT_FALSE(fru1->getSN());
543 EXPECT_FALSE(fru1->getCCIN());
544
545 auto& fru2 = callouts[1]->fruIdentity();
Matt Spinlerea2873d2021-08-18 10:35:40 -0500546 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
Matt Spinler03984582020-04-09 13:17:58 -0500547 EXPECT_EQ(fru2->failingComponentType(),
548 src::FRUIdentity::maintenanceProc);
549 EXPECT_FALSE(fru2->getPN());
550 EXPECT_FALSE(fru2->getSN());
551 EXPECT_FALSE(fru2->getCCIN());
552 }
553
554 {
555 // Call out a trusted symbolic FRU with a location code, and
556 // another one without.
557 AdditionalData ad;
558 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500559 std::vector<std::string> names{"systemB"};
560
Matt Spinleraf191c72020-06-04 11:35:13 -0500561 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500562 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500563
564 SRC src{entry, ad, dataIface};
565
566 auto& callouts = src.callouts()->callouts();
567 EXPECT_EQ(callouts.size(), 2);
568
569 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
570 EXPECT_EQ(callouts[0]->priority(), 'H');
571
572 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
573 EXPECT_EQ(callouts[1]->priority(), 'M');
574
575 auto& fru1 = callouts[0]->fruIdentity();
576 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
577 EXPECT_EQ(fru1->failingComponentType(),
578 src::FRUIdentity::symbolicFRUTrustedLocCode);
579 EXPECT_FALSE(fru1->getMaintProc());
580 EXPECT_FALSE(fru1->getSN());
581 EXPECT_FALSE(fru1->getCCIN());
582
583 // It asked for a trusted symbolic FRU, but no location code
584 // was provided so it is switched back to a normal one
585 auto& fru2 = callouts[1]->fruIdentity();
586 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
587 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
588 EXPECT_FALSE(fru2->getMaintProc());
589 EXPECT_FALSE(fru2->getSN());
590 EXPECT_FALSE(fru2->getCCIN());
591 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500592
593 {
594 // Two hardware callouts
595 AdditionalData ad;
596 NiceMock<MockDataInterface> dataIface;
597 std::vector<std::string> names{"systemC"};
598
Matt Spinler1ab66962020-10-29 13:21:44 -0500599 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500600
601 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
602 .WillOnce(Return("UXXX-P0-C8"));
603
604 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
605 .WillOnce(Return("UXXX-P0-C9"));
606
Matt Spinler2f9225a2020-08-05 12:58:49 -0500607 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600608 .WillOnce(Return(std::vector<std::string>{
609 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500610
Matt Spinler2f9225a2020-08-05 12:58:49 -0500611 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600612 .WillOnce(Return(std::vector<std::string>{
613 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500614
615 EXPECT_CALL(
616 dataIface,
617 getHWCalloutFields(
618 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
619 _))
620 .Times(1)
621 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
622 SetArgReferee<2>("CCCC"),
623 SetArgReferee<3>("123456789ABC")));
624
625 EXPECT_CALL(
626 dataIface,
627 getHWCalloutFields(
628 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
629 _))
630 .Times(1)
631 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
632 SetArgReferee<2>("DDDD"),
633 SetArgReferee<3>("23456789ABCD")));
634
635 SRC src{entry, ad, dataIface};
636
637 auto& callouts = src.callouts()->callouts();
638 EXPECT_EQ(callouts.size(), 2);
639
640 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
641 EXPECT_EQ(callouts[0]->priority(), 'H');
642
643 auto& fru1 = callouts[0]->fruIdentity();
644 EXPECT_EQ(fru1->getPN().value(), "1234567");
645 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
646 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
647
648 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
649 EXPECT_EQ(callouts[1]->priority(), 'M');
650
651 auto& fru2 = callouts[1]->fruIdentity();
652 EXPECT_EQ(fru2->getPN().value(), "2345678");
653 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
654 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
655 }
Matt Spinler03984582020-04-09 13:17:58 -0500656}
Matt Spinler717de422020-06-04 13:10:14 -0500657
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500658// Test that a symbolic FRU with a trusted location code callout
659// from the registry can get its location from the
660// CALLOUT_INVENTORY_PATH AdditionalData entry.
661TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
662{
663 message::Entry entry;
664 entry.src.type = 0xBD;
665 entry.src.reasonCode = 0xABCD;
666 entry.subsystem = 0x42;
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500667
668 entry.callouts = R"(
669 [{
670 "CalloutList":
671 [
672 {
673 "Priority": "high",
674 "SymbolicFRUTrusted": "service_docs",
675 "UseInventoryLocCode": true
676 },
677 {
678 "Priority": "medium",
679 "LocCode": "P0-C8",
680 "SymbolicFRUTrusted": "pwrsply"
681 }
682 ]
683 }])"_json;
684
685 {
686 // The location code for the first symbolic FRU callout will
687 // come from this inventory path since UseInventoryLocCode is set.
688 // In this case there will be no normal FRU callout for the motherboard.
689 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
690 AdditionalData ad{adData};
691 NiceMock<MockDataInterface> dataIface;
692 std::vector<std::string> names{"systemA"};
693
Matt Spinler1ab66962020-10-29 13:21:44 -0500694 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500695
696 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
697 .Times(1)
698 .WillOnce(Return("Ufcs-P10"));
699
700 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
701 .WillOnce(Return("Ufcs-P0-C8"));
702
703 SRC src{entry, ad, dataIface};
704
705 auto& callouts = src.callouts()->callouts();
706 EXPECT_EQ(callouts.size(), 2);
707
708 // The location code for the first symbolic FRU callout with a
709 // trusted location code comes from the motherboard.
710 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
711 EXPECT_EQ(callouts[0]->priority(), 'H');
712 auto& fru1 = callouts[0]->fruIdentity();
713 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
714 EXPECT_EQ(fru1->failingComponentType(),
715 src::FRUIdentity::symbolicFRUTrustedLocCode);
716
717 // The second trusted symbolic FRU callouts uses the location
718 // code in the registry as usual.
719 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
720 EXPECT_EQ(callouts[1]->priority(), 'M');
721 auto& fru2 = callouts[1]->fruIdentity();
722 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
723 EXPECT_EQ(fru2->failingComponentType(),
724 src::FRUIdentity::symbolicFRUTrustedLocCode);
725 }
726
727 {
728 // This time say we want to use the location code from
729 // the inventory, but don't pass it in and the callout should
730 // end up a regular symbolic FRU
731 entry.callouts = R"(
732 [{
733 "CalloutList":
734 [
735 {
736 "Priority": "high",
737 "SymbolicFRUTrusted": "service_docs",
738 "UseInventoryLocCode": true
739 }
740 ]
741 }])"_json;
742
743 AdditionalData ad;
744 NiceMock<MockDataInterface> dataIface;
745 std::vector<std::string> names{"systemA"};
746
Matt Spinler1ab66962020-10-29 13:21:44 -0500747 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500748
749 SRC src{entry, ad, dataIface};
750
751 auto& callouts = src.callouts()->callouts();
752 EXPECT_EQ(callouts.size(), 1);
753
754 EXPECT_EQ(callouts[0]->locationCode(), "");
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(), src::FRUIdentity::symbolicFRU);
759 }
760}
761
Matt Spinler717de422020-06-04 13:10:14 -0500762// Test looking up device path fails in the callout jSON.
763TEST_F(SRCTest, DevicePathCalloutTest)
764{
765 message::Entry entry;
766 entry.src.type = 0xBD;
767 entry.src.reasonCode = 0xABCD;
768 entry.subsystem = 0x42;
Matt Spinler717de422020-06-04 13:10:14 -0500769
770 const auto calloutJSON = R"(
771 {
772 "I2C":
773 {
774 "14":
775 {
776 "114":
777 {
778 "Callouts":[
779 {
780 "Name": "/chassis/motherboard/cpu0",
781 "LocationCode": "P1-C40",
782 "Priority": "H"
783 },
784 {
785 "Name": "/chassis/motherboard",
786 "LocationCode": "P1",
787 "Priority": "M"
788 },
789 {
790 "Name": "/chassis/motherboard/bmc",
791 "LocationCode": "P1-C15",
792 "Priority": "L"
793 }
794 ],
795 "Dest": "proc 0 target"
796 }
797 }
798 }
799 })";
800
801 auto dataPath = getPELReadOnlyDataPath();
802 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
803 file << calloutJSON;
804 file.close();
805
806 NiceMock<MockDataInterface> dataIface;
807 std::vector<std::string> names{"systemA"};
808
809 EXPECT_CALL(dataIface, getSystemNames)
810 .Times(5)
Matt Spinler1ab66962020-10-29 13:21:44 -0500811 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500812
Matt Spinler2f9225a2020-08-05 12:58:49 -0500813 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500814 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600815 .WillRepeatedly(Return(std::vector<std::string>{
816 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
Matt Spinler717de422020-06-04 13:10:14 -0500817
Matt Spinler2f9225a2020-08-05 12:58:49 -0500818 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500819 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600820 .WillRepeatedly(Return(std::vector<std::string>{
821 "/xyz/openbmc_project/inventory/chassis/motherboard"}));
Matt Spinler717de422020-06-04 13:10:14 -0500822
Matt Spinler2f9225a2020-08-05 12:58:49 -0500823 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500824 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600825 .WillRepeatedly(Return(std::vector<std::string>{
826 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"}));
Matt Spinler717de422020-06-04 13:10:14 -0500827
Matt Spinler0d92b522021-06-16 13:28:17 -0600828 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500829 .Times(3)
830 .WillRepeatedly(Return("Ufcs-P1-C40"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600831
832 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500833 .Times(3)
834 .WillRepeatedly(Return("Ufcs-P1"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600835
836 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500837 .Times(3)
838 .WillRepeatedly(Return("Ufcs-P1-C15"));
839
840 EXPECT_CALL(
841 dataIface,
842 getHWCalloutFields(
843 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
844 .Times(3)
845 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
846 SetArgReferee<2>("CCCC"),
847 SetArgReferee<3>("123456789ABC")));
848 EXPECT_CALL(
849 dataIface,
850 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
851 _, _, _))
852 .Times(3)
853 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
854 SetArgReferee<2>("MMMM"),
855 SetArgReferee<3>("CBA987654321")));
856 EXPECT_CALL(
857 dataIface,
858 getHWCalloutFields(
859 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
860 .Times(3)
861 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
862 SetArgReferee<2>("BBBB"),
863 SetArgReferee<3>("C123456789AB")));
864
865 // Call this below with different AdditionalData values that
866 // result in the same callouts.
867 auto checkCallouts = [&entry, &dataIface](const auto& items) {
868 AdditionalData ad{items};
869 SRC src{entry, ad, dataIface};
870
871 ASSERT_TRUE(src.callouts());
872 auto& callouts = src.callouts()->callouts();
873
874 ASSERT_EQ(callouts.size(), 3);
875
876 {
877 EXPECT_EQ(callouts[0]->priority(), 'H');
878 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
879
880 auto& fru = callouts[0]->fruIdentity();
881 EXPECT_EQ(fru->getPN().value(), "1234567");
882 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
883 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
884 }
885 {
886 EXPECT_EQ(callouts[1]->priority(), 'M');
887 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
888
889 auto& fru = callouts[1]->fruIdentity();
890 EXPECT_EQ(fru->getPN().value(), "7654321");
891 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
892 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
893 }
894 {
895 EXPECT_EQ(callouts[2]->priority(), 'L');
896 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
897
898 auto& fru = callouts[2]->fruIdentity();
899 EXPECT_EQ(fru->getPN().value(), "7123456");
900 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
901 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
902 }
903 };
904
905 {
906 // Callouts based on the device path
907 std::vector<std::string> items{
908 "CALLOUT_ERRNO=5",
909 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
910 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
911
912 checkCallouts(items);
913 }
914
915 {
916 // Callouts based on the I2C bus and address
917 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
918 "CALLOUT_IIC_ADDR=0x72"};
919 checkCallouts(items);
920 }
921
922 {
923 // Also based on I2C bus and address, but with bus = /dev/i2c-14
924 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
925 "CALLOUT_IIC_ADDR=0x72"};
926 checkCallouts(items);
927 }
928
929 {
930 // Callout not found
931 std::vector<std::string> items{
932 "CALLOUT_ERRNO=5",
933 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
934 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
935
936 AdditionalData ad{items};
937 SRC src{entry, ad, dataIface};
938
939 EXPECT_FALSE(src.callouts());
940 ASSERT_EQ(src.getDebugData().size(), 1);
941 EXPECT_EQ(src.getDebugData()[0],
942 "Problem looking up I2C callouts on 24 18: "
943 "[json.exception.out_of_range.403] key '24' not found");
944 }
945
946 {
947 // Callout not found
948 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
949 "CALLOUT_IIC_ADDR=0x99"};
950 AdditionalData ad{items};
951 SRC src{entry, ad, dataIface};
952
953 EXPECT_FALSE(src.callouts());
954 ASSERT_EQ(src.getDebugData().size(), 1);
955 EXPECT_EQ(src.getDebugData()[0],
956 "Problem looking up I2C callouts on 22 153: "
957 "[json.exception.out_of_range.403] key '22' not found");
958 }
959
960 fs::remove_all(dataPath);
961}
Matt Spinler3bdd0112020-08-27 10:24:34 -0500962
963// Test when callouts are passed in via JSON
964TEST_F(SRCTest, JsonCalloutsTest)
965{
966 const auto jsonCallouts = R"(
967 [
968 {
969 "LocationCode": "P0-C1",
970 "Priority": "H",
971 "MRUs": [
972 {
973 "ID": 42,
974 "Priority": "H"
975 },
976 {
977 "ID": 43,
978 "Priority": "M"
979 }
980 ]
981 },
982 {
983 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
984 "Priority": "M",
985 "Guarded": true,
986 "Deconfigured": true
987 },
988 {
989 "Procedure": "PROCEDU",
990 "Priority": "A"
991 },
992 {
993 "SymbolicFRU": "TRUSTED",
994 "Priority": "B",
995 "TrustedLocationCode": true,
996 "LocationCode": "P1-C23"
997 },
998 {
999 "SymbolicFRU": "FRUTST1",
1000 "Priority": "C",
1001 "LocationCode": "P1-C24"
1002 },
1003 {
1004 "SymbolicFRU": "FRUTST2LONG",
1005 "Priority": "L"
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001006 },
1007 {
1008 "Procedure": "fsi_path",
1009 "Priority": "L"
1010 },
1011 {
1012 "SymbolicFRU": "ambient_temp",
1013 "Priority": "L"
Matt Spinler3bdd0112020-08-27 10:24:34 -05001014 }
1015 ]
1016 )"_json;
1017
1018 message::Entry entry;
1019 entry.src.type = 0xBD;
1020 entry.src.reasonCode = 0xABCD;
1021 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001022
1023 AdditionalData ad;
1024 NiceMock<MockDataInterface> dataIface;
1025
1026 // Callout 0 mock calls
1027 {
1028 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1029 .Times(1)
1030 .WillOnce(Return("UXXX-P0-C1"));
1031 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1032 .Times(1)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001033 .WillOnce(Return(std::vector<std::string>{
1034 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001035 EXPECT_CALL(
1036 dataIface,
1037 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1038 .Times(1)
1039 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1040 SetArgReferee<2>("CCCC"),
1041 SetArgReferee<3>("123456789ABC")));
1042 }
1043 // Callout 1 mock calls
1044 {
1045 EXPECT_CALL(dataIface,
1046 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1047 .WillOnce(Return("UYYY-P5"));
1048 EXPECT_CALL(
1049 dataIface,
1050 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1051 .Times(1)
1052 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
1053 SetArgReferee<2>("DDDD"),
1054 SetArgReferee<3>("23456789ABCD")));
1055 }
1056 // Callout 3 mock calls
1057 {
1058 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1059 .Times(1)
1060 .WillOnce(Return("UXXX-P1-C23"));
1061 }
1062 // Callout 4 mock calls
1063 {
1064 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1065 .Times(1)
1066 .WillOnce(Return("UXXX-P1-C24"));
1067 }
1068
1069 SRC src{entry, ad, jsonCallouts, dataIface};
1070 ASSERT_TRUE(src.callouts());
1071
Matt Spinlerafa2c792020-08-27 11:01:39 -05001072 // Check the guarded and deconfigured flags
1073 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1074
Matt Spinler3bdd0112020-08-27 10:24:34 -05001075 const auto& callouts = src.callouts()->callouts();
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001076 ASSERT_EQ(callouts.size(), 8);
Matt Spinler3bdd0112020-08-27 10:24:34 -05001077
1078 // Check callout 0
1079 {
1080 EXPECT_EQ(callouts[0]->priority(), 'H');
1081 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1082
1083 auto& fru = callouts[0]->fruIdentity();
1084 EXPECT_EQ(fru->getPN().value(), "1234567");
1085 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1086 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1087 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001088
1089 auto& mruCallouts = callouts[0]->mru();
1090 ASSERT_TRUE(mruCallouts);
1091 auto& mrus = mruCallouts->mrus();
1092 ASSERT_EQ(mrus.size(), 2);
1093 EXPECT_EQ(mrus[0].id, 42);
1094 EXPECT_EQ(mrus[0].priority, 'H');
1095 EXPECT_EQ(mrus[1].id, 43);
1096 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001097 }
1098
1099 // Check callout 1
1100 {
1101 EXPECT_EQ(callouts[1]->priority(), 'M');
1102 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1103
1104 auto& fru = callouts[1]->fruIdentity();
1105 EXPECT_EQ(fru->getPN().value(), "2345678");
1106 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1107 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1108 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1109 }
1110
1111 // Check callout 2
1112 {
1113 EXPECT_EQ(callouts[2]->priority(), 'A');
1114 EXPECT_EQ(callouts[2]->locationCode(), "");
1115
1116 auto& fru = callouts[2]->fruIdentity();
1117 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1118 EXPECT_EQ(fru->failingComponentType(),
1119 src::FRUIdentity::maintenanceProc);
1120 }
1121
1122 // Check callout 3
1123 {
1124 EXPECT_EQ(callouts[3]->priority(), 'B');
1125 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1126
1127 auto& fru = callouts[3]->fruIdentity();
1128 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1129 EXPECT_EQ(fru->failingComponentType(),
1130 src::FRUIdentity::symbolicFRUTrustedLocCode);
1131 }
1132
1133 // Check callout 4
1134 {
1135 EXPECT_EQ(callouts[4]->priority(), 'C');
1136 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1137
1138 auto& fru = callouts[4]->fruIdentity();
1139 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1140 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1141 }
1142
1143 // Check callout 5
1144 {
1145 EXPECT_EQ(callouts[5]->priority(), 'L');
1146 EXPECT_EQ(callouts[5]->locationCode(), "");
1147
1148 auto& fru = callouts[5]->fruIdentity();
1149 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1150 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1151 }
1152
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001153 // Check callout 6
1154 {
1155 EXPECT_EQ(callouts[6]->priority(), 'L');
1156 EXPECT_EQ(callouts[6]->locationCode(), "");
1157
1158 auto& fru = callouts[6]->fruIdentity();
1159 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004");
1160 EXPECT_EQ(fru->failingComponentType(),
1161 src::FRUIdentity::maintenanceProc);
1162 }
1163
1164 // Check callout 7
1165 {
1166 EXPECT_EQ(callouts[7]->priority(), 'L');
1167 EXPECT_EQ(callouts[7]->locationCode(), "");
1168
1169 auto& fru = callouts[7]->fruIdentity();
1170 EXPECT_EQ(fru->getPN().value(), "AMBTEMP");
1171 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1172 }
1173
Matt Spinler3bdd0112020-08-27 10:24:34 -05001174 // Check that it didn't find any errors
1175 const auto& data = src.getDebugData();
1176 EXPECT_TRUE(data.empty());
1177}
1178
1179TEST_F(SRCTest, JsonBadCalloutsTest)
1180{
1181 // The first call will have a Throw in a mock call.
1182 // The second will have a different Throw in a mock call.
1183 // The others have issues with the Priority field.
1184 const auto jsonCallouts = R"(
1185 [
1186 {
1187 "LocationCode": "P0-C1",
1188 "Priority": "H"
1189 },
1190 {
1191 "LocationCode": "P0-C2",
1192 "Priority": "H"
1193 },
1194 {
1195 "LocationCode": "P0-C3"
1196 },
1197 {
1198 "LocationCode": "P0-C4",
1199 "Priority": "X"
1200 }
1201 ]
1202 )"_json;
1203
1204 message::Entry entry;
1205 entry.src.type = 0xBD;
1206 entry.src.reasonCode = 0xABCD;
1207 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001208
1209 AdditionalData ad;
1210 NiceMock<MockDataInterface> dataIface;
1211
1212 // Callout 0 mock calls
1213 // Expand location code will fail, so the unexpanded location
1214 // code should show up in the callout instead.
1215 {
1216 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1217 .WillOnce(Throw(std::runtime_error("Fail")));
1218
1219 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1220 .Times(1)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001221 .WillOnce(Return(std::vector<std::string>{
1222 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001223 EXPECT_CALL(
1224 dataIface,
1225 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1226 .Times(1)
1227 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
1228 SetArgReferee<2>("CCCC"),
1229 SetArgReferee<3>("123456789ABC")));
1230 }
1231
1232 // Callout 1 mock calls
1233 // getInventoryFromLocCode will fail
1234 {
1235 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1236 .Times(1)
1237 .WillOnce(Return("UXXX-P0-C2"));
1238
1239 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1240 .Times(1)
1241 .WillOnce(Throw(std::runtime_error("Fail")));
1242 }
1243
1244 SRC src{entry, ad, jsonCallouts, dataIface};
1245
1246 ASSERT_TRUE(src.callouts());
1247
1248 const auto& callouts = src.callouts()->callouts();
1249
1250 // Only the first callout was successful
1251 ASSERT_EQ(callouts.size(), 1);
1252
1253 {
1254 EXPECT_EQ(callouts[0]->priority(), 'H');
1255 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1256
1257 auto& fru = callouts[0]->fruIdentity();
1258 EXPECT_EQ(fru->getPN().value(), "1234567");
1259 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1260 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1261 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1262 }
1263
1264 const auto& data = src.getDebugData();
1265 ASSERT_EQ(data.size(), 4);
1266 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1267 EXPECT_STREQ(data[1].c_str(),
1268 "Failed extracting callout data from JSON: Unable to "
1269 "get inventory path from location code: P0-C2: Fail");
1270 EXPECT_STREQ(data[2].c_str(),
1271 "Failed extracting callout data from JSON: "
1272 "[json.exception.out_of_range.403] key 'Priority' not found");
1273 EXPECT_STREQ(data[3].c_str(),
1274 "Failed extracting callout data from JSON: Invalid "
1275 "priority 'X' found in JSON callout");
1276}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001277
1278// Test that an inventory path callout can have
1279// a different priority than H.
1280TEST_F(SRCTest, InventoryCalloutTestPriority)
1281{
1282 message::Entry entry;
1283 entry.src.type = 0xBD;
1284 entry.src.reasonCode = 0xABCD;
1285 entry.subsystem = 0x42;
Miguel Gomez53ef1552020-10-14 21:16:32 +00001286
1287 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1288 "CALLOUT_PRIORITY=M"};
1289 AdditionalData ad{adData};
1290 NiceMock<MockDataInterface> dataIface;
1291
1292 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1293 .WillOnce(Return("UTMS-P1"));
1294
1295 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1296 .Times(1)
1297 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1298 SetArgReferee<3>("123456789ABC")));
1299
1300 SRC src{entry, ad, dataIface};
1301 EXPECT_TRUE(src.valid());
1302
1303 ASSERT_TRUE(src.callouts());
1304
1305 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1306
1307 auto& callout = src.callouts()->callouts().front();
1308
1309 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1310 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001311}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001312
1313// Test for bmc & platform dump status bits
1314TEST_F(SRCTest, DumpStatusBitsCheck)
1315{
1316 message::Entry entry;
1317 entry.src.type = 0xBD;
1318 entry.src.reasonCode = 0xABCD;
1319 entry.subsystem = 0x42;
Sumit Kumar9d43a722021-08-24 09:46:19 -05001320
1321 AdditionalData ad;
1322 NiceMock<MockDataInterface> dataIface;
1323 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1324 "system/entry"};
1325
1326 {
1327 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1328 .WillOnce(Return(std::vector<bool>{true, false, false}));
1329
1330 SRC src{entry, ad, dataIface};
1331 EXPECT_TRUE(src.valid());
1332
1333 const auto& hexwords = src.hexwordData();
1334 EXPECT_EQ(0x00080055, hexwords[0]);
1335 }
1336
1337 {
1338 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1339 .WillOnce(Return(std::vector<bool>{false, true, false}));
1340
1341 SRC src{entry, ad, dataIface};
1342 EXPECT_TRUE(src.valid());
1343
1344 const auto& hexwords = src.hexwordData();
1345 EXPECT_EQ(0x00000255, hexwords[0]);
1346 }
1347
1348 {
1349 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1350 .WillOnce(Return(std::vector<bool>{false, false, true}));
1351
1352 SRC src{entry, ad, dataIface};
1353 EXPECT_TRUE(src.valid());
1354
1355 const auto& hexwords = src.hexwordData();
1356 EXPECT_EQ(0x00000455, hexwords[0]);
1357 }
1358
1359 {
1360 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1361 .WillOnce(Return(std::vector<bool>{true, true, true}));
1362
1363 SRC src{entry, ad, dataIface};
1364 EXPECT_TRUE(src.valid());
1365
1366 const auto& hexwords = src.hexwordData();
1367 EXPECT_EQ(0x00080655, hexwords[0]);
1368 }
1369}
Sumit Kumar50bfa692022-01-06 06:48:26 -06001370
1371// Test SRC with additional data - PEL_SUBSYSTEM
1372TEST_F(SRCTest, TestPELSubsystem)
1373{
1374 message::Entry entry;
1375 entry.src.type = 0xBD;
1376 entry.src.reasonCode = 0xABCD;
1377 entry.subsystem = 0x42;
Sumit Kumar50bfa692022-01-06 06:48:26 -06001378
1379 // Values for the SRC words pointed to above
1380 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
1381 AdditionalData ad{adData};
1382 NiceMock<MockDataInterface> dataIface;
1383
1384 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1385
Sumit Kumar50bfa692022-01-06 06:48:26 -06001386 SRC src{entry, ad, dataIface};
1387
1388 EXPECT_TRUE(src.valid());
1389
1390 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1391}
Vijay Lobo875b6c72021-10-20 17:38:56 -05001392
1393void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1394{
1395 assert(40 + value.size() <= src.size());
1396
1397 for (size_t i = 0; i < value.size(); i++)
1398 {
1399 src[40 + i] = value[i];
1400 }
1401}
1402
1403TEST_F(SRCTest, TestGetProgressCode)
1404{
1405 {
1406 // A real SRC with CC009184
1407 std::vector<uint8_t> src{
1408 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1409 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1411 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1412 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1413
1414 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1415 }
1416
1417 {
1418 // A real SRC with STANDBY
1419 std::vector<uint8_t> src{
1420 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1421 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1422 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1423 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1424 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1425
1426 EXPECT_EQ(SRC::getProgressCode(src), 0);
1427 }
1428
1429 {
1430 // A real SRC with CC009184, but 1 byte too short
1431 std::vector<uint8_t> src{
1432 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1433 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1434 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1435 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1436 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1437 src.resize(71);
1438 EXPECT_EQ(SRC::getProgressCode(src), 0);
1439 }
1440
1441 {
1442 // A few different ones
1443 const std::map<std::string, uint32_t> progressCodes{
1444 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1445 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1446 {"1234567X", 0}, {"1 ", 0}};
1447
1448 std::vector<uint8_t> src(72, 0x0);
1449
1450 for (const auto& [code, expected] : progressCodes)
1451 {
1452 setAsciiString(src, code);
1453 EXPECT_EQ(SRC::getProgressCode(src), expected);
1454 }
1455
1456 // empty
1457 src.clear();
1458 EXPECT_EQ(SRC::getProgressCode(src), 0);
1459 }
1460}
1461
1462// Test progress is in right SRC hex data field
1463TEST_F(SRCTest, TestProgressCodeField)
1464{
1465 message::Entry entry;
1466 entry.src.type = 0xBD;
1467 entry.src.reasonCode = 0xABCD;
1468 entry.subsystem = 0x42;
1469
1470 AdditionalData ad;
1471 NiceMock<MockDataInterface> dataIface;
Vijay Lobo875b6c72021-10-20 17:38:56 -05001472 EXPECT_CALL(dataIface, getRawProgressSRC())
1473 .WillOnce(Return(std::vector<uint8_t>{
1474 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1475 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1476 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1477 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1478 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1479
1480 SRC src{entry, ad, dataIface};
1481 EXPECT_TRUE(src.valid());
1482
1483 // Verify that the hex vlue is set at the right hexword
1484 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1485}