1 /**
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 */
16 #include "extensions/openpower-pels/src.hpp"
17 #include "mocks.hpp"
18 #include "pel_utils.hpp"
19
20 #include <fstream>
21
22 #include <gtest/gtest.h>
23
24 using namespace openpower::pels;
25 using ::testing::_;
26 using ::testing::DoAll;
27 using ::testing::InvokeWithoutArgs;
28 using ::testing::NiceMock;
29 using ::testing::Return;
30 using ::testing::SetArgReferee;
31 using ::testing::Throw;
32 namespace fs = std::filesystem;
33
34 const 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
82 class SRCTest : public ::testing::Test
83 {
84 protected:
SetUpTestCase()85 static void SetUpTestCase()
86 {
87 char path[] = "/tmp/srctestXXXXXX";
88 regDir = mkdtemp(path);
89 }
90
TearDownTestCase()91 static void TearDownTestCase()
92 {
93 fs::remove_all(regDir);
94 }
95
writeData(const char * data)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
107 fs::path SRCTest::regDir{};
108
TEST_F(SRCTest,UnflattenFlattenTestNoCallouts)109 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
110 {
111 auto data = pelDataFactory(TestPELType::primarySRCSection);
112
113 Stream stream{data};
114 SRC src{stream};
115
116 EXPECT_TRUE(src.valid());
117
118 EXPECT_EQ(src.header().id, 0x5053);
119 EXPECT_EQ(src.header().size, 0x50);
120 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();
130 EXPECT_EQ(0x02020255, hexwords[0]);
131 EXPECT_EQ(0x03030310, hexwords[1]);
132 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
TEST_F(SRCTest,UnflattenFlattenTest2Callouts)150 TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
151 {
152 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
153
154 Stream stream{data};
155 SRC src{stream};
156
157 EXPECT_TRUE(src.valid());
158 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
159
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 }
185
186 // Create an SRC from the message registry
TEST_F(SRCTest,CreateTestNoCallouts)187 TEST_F(SRCTest, CreateTestNoCallouts)
188 {
189 message::Entry entry;
190 entry.src.type = 0xBD;
191 entry.src.reasonCode = 0xABCD;
192 entry.subsystem = 0x42;
193 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"}}};
199
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};
204 NiceMock<MockDataInterface> dataIface;
205
206 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
207
208 SRC src{entry, ad, dataIface};
209
210 EXPECT_TRUE(src.valid());
211 EXPECT_FALSE(src.isPowerFaultEvent());
212 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
223 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
224
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
228 // And that none of the error status flags are set
229 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());
251 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
252 EXPECT_FALSE(newSRC.callouts());
253 }
254
255 // Test when the CCIN string isn't a 4 character number
TEST_F(SRCTest,BadCCINTest)256 TEST_F(SRCTest, BadCCINTest)
257 {
258 message::Entry entry;
259 entry.src.type = 0xBD;
260 entry.src.reasonCode = 0xABCD;
261 entry.subsystem = 0x42;
262
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
297 // Test the getErrorDetails function
TEST_F(SRCTest,MessageSubstitutionTest)298 TEST_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};
307 NiceMock<MockDataInterface> dataIface;
308
309 SRC src{*entry, ad, dataIface};
310 EXPECT_TRUE(src.valid());
311
312 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
313 ASSERT_TRUE(errorDetails);
314 EXPECT_EQ(errorDetails.value(),
315 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
316 "with ErrorCode 0x01ABCDEF");
317 }
318 // Test that an inventory path callout string is
319 // converted into the appropriate FRU callout.
TEST_F(SRCTest,InventoryCalloutTest)320 TEST_F(SRCTest, InventoryCalloutTest)
321 {
322 message::Entry entry;
323 entry.src.type = 0xBD;
324 entry.src.reasonCode = 0xABCD;
325 entry.subsystem = 0x42;
326
327 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
328 AdditionalData ad{adData};
329 NiceMock<MockDataInterface> dataIface;
330
331 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
332 .WillOnce(Return("UTMS-P1"));
333
334 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
335 .Times(1)
336 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
337 SetArgReferee<3>("123456789ABC")));
338
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");
349 EXPECT_EQ(callout->priority(), 'H');
350
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
369 // Test that when the location code can't be obtained that
370 // no callout is added.
TEST_F(SRCTest,InventoryCalloutNoLocCodeTest)371 TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
372 {
373 message::Entry entry;
374 entry.src.type = 0xBD;
375 entry.src.reasonCode = 0xABCD;
376 entry.subsystem = 0x42;
377
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
396 ASSERT_FALSE(src.callouts());
397
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());
406 ASSERT_FALSE(src.callouts());
407 }
408
409 // Test that when the VPD can't be obtained that
410 // a callout is still created.
TEST_F(SRCTest,InventoryCalloutNoVPDTest)411 TEST_F(SRCTest, InventoryCalloutNoVPDTest)
412 {
413 message::Entry entry;
414 entry.src.type = 0xBD;
415 entry.src.reasonCode = 0xABCD;
416 entry.subsystem = 0x42;
417
418 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
419 AdditionalData ad{adData};
420 NiceMock<MockDataInterface> dataIface;
421
422 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
423 .Times(1)
424 .WillOnce(Return("UTMS-P10"));
425
426 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
427
428 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
429 .Times(1)
430 .WillOnce(InvokeWithoutArgs(func));
431
432 SRC src{entry, ad, dataIface};
433 EXPECT_TRUE(src.valid());
434 ASSERT_TRUE(src.callouts());
435 EXPECT_EQ(src.callouts()->callouts().size(), 1);
436
437 auto& callout = src.callouts()->callouts().front();
438 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
439 EXPECT_EQ(callout->priority(), 'H');
440
441 auto& fru = callout->fruIdentity();
442
443 EXPECT_EQ(fru->getPN(), "");
444 EXPECT_EQ(fru->getCCIN(), "");
445 EXPECT_EQ(fru->getSN(), "");
446 EXPECT_FALSE(fru->getMaintProc());
447
448 // 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 }
459
TEST_F(SRCTest,RegistryCalloutTest)460 TEST_F(SRCTest, RegistryCalloutTest)
461 {
462 message::Entry entry;
463 entry.src.type = 0xBD;
464 entry.src.reasonCode = 0xABCD;
465 entry.src.deconfigFlag = true;
466 entry.src.checkstopFlag = true;
467 entry.subsystem = 0x42;
468
469 entry.callouts = R"(
470 [
471 {
472 "System": "systemA",
473 "CalloutList":
474 [
475 {
476 "Priority": "high",
477 "SymbolicFRU": "service_docs"
478 },
479 {
480 "Priority": "medium",
481 "Procedure": "BMC0001"
482 }
483 ]
484 },
485 {
486 "System": "systemB",
487 "CalloutList":
488 [
489 {
490 "Priority": "high",
491 "LocCode": "P0-C8",
492 "SymbolicFRUTrusted": "service_docs"
493 },
494 {
495 "Priority": "medium",
496 "SymbolicFRUTrusted": "service_docs"
497 }
498 ]
499 },
500 {
501 "System": "systemC",
502 "CalloutList":
503 [
504 {
505 "Priority": "high",
506 "LocCode": "P0-C8"
507 },
508 {
509 "Priority": "medium",
510 "LocCode": "P0-C9"
511 }
512 ]
513 }
514 ])"_json;
515
516 {
517 // Call out a symbolic FRU and a procedure
518 AdditionalData ad;
519 NiceMock<MockDataInterface> dataIface;
520 std::vector<std::string> names{"systemA"};
521
522 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
523
524 SRC src{entry, ad, dataIface};
525
526 EXPECT_TRUE(
527 src.getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured));
528 EXPECT_TRUE(src.getErrorStatusFlag(SRC::ErrorStatusFlags::hwCheckstop));
529
530 const auto& hexwords = src.hexwordData();
531 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured) |
532 static_cast<uint32_t>(SRC::ErrorStatusFlags::hwCheckstop);
533 EXPECT_EQ(hexwords[5 - 2] & mask, mask);
534
535 auto& callouts = src.callouts()->callouts();
536 ASSERT_EQ(callouts.size(), 2);
537
538 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
539 EXPECT_EQ(callouts[0]->priority(), 'H');
540
541 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
542 EXPECT_EQ(callouts[1]->priority(), 'M');
543
544 auto& fru1 = callouts[0]->fruIdentity();
545 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
546 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
547 EXPECT_FALSE(fru1->getMaintProc());
548 EXPECT_FALSE(fru1->getSN());
549 EXPECT_FALSE(fru1->getCCIN());
550
551 auto& fru2 = callouts[1]->fruIdentity();
552 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
553 EXPECT_EQ(fru2->failingComponentType(),
554 src::FRUIdentity::maintenanceProc);
555 EXPECT_FALSE(fru2->getPN());
556 EXPECT_FALSE(fru2->getSN());
557 EXPECT_FALSE(fru2->getCCIN());
558 }
559
560 {
561 // Call out a trusted symbolic FRU with a location code, and
562 // another one without.
563 AdditionalData ad;
564 NiceMock<MockDataInterface> dataIface;
565 std::vector<std::string> names{"systemB"};
566
567 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
568 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
569
570 SRC src{entry, ad, dataIface};
571
572 auto& callouts = src.callouts()->callouts();
573 EXPECT_EQ(callouts.size(), 2);
574
575 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
576 EXPECT_EQ(callouts[0]->priority(), 'H');
577
578 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
579 EXPECT_EQ(callouts[1]->priority(), 'M');
580
581 auto& fru1 = callouts[0]->fruIdentity();
582 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
583 EXPECT_EQ(fru1->failingComponentType(),
584 src::FRUIdentity::symbolicFRUTrustedLocCode);
585 EXPECT_FALSE(fru1->getMaintProc());
586 EXPECT_FALSE(fru1->getSN());
587 EXPECT_FALSE(fru1->getCCIN());
588
589 // It asked for a trusted symbolic FRU, but no location code
590 // was provided so it is switched back to a normal one
591 auto& fru2 = callouts[1]->fruIdentity();
592 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
593 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
594 EXPECT_FALSE(fru2->getMaintProc());
595 EXPECT_FALSE(fru2->getSN());
596 EXPECT_FALSE(fru2->getCCIN());
597 }
598
599 {
600 // Two hardware callouts
601 AdditionalData ad;
602 NiceMock<MockDataInterface> dataIface;
603 std::vector<std::string> names{"systemC"};
604
605 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
606
607 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
608 .WillOnce(Return("UXXX-P0-C8"));
609
610 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
611 .WillOnce(Return("UXXX-P0-C9"));
612
613 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
614 .WillOnce(Return(std::vector<std::string>{
615 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
616
617 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
618 .WillOnce(Return(std::vector<std::string>{
619 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"}));
620
621 EXPECT_CALL(
622 dataIface,
623 getHWCalloutFields(
624 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
625 _))
626 .Times(1)
627 .WillOnce(
628 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
629 SetArgReferee<3>("123456789ABC")));
630
631 EXPECT_CALL(
632 dataIface,
633 getHWCalloutFields(
634 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
635 _))
636 .Times(1)
637 .WillOnce(
638 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
639 SetArgReferee<3>("23456789ABCD")));
640
641 SRC src{entry, ad, dataIface};
642
643 auto& callouts = src.callouts()->callouts();
644 EXPECT_EQ(callouts.size(), 2);
645
646 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
647 EXPECT_EQ(callouts[0]->priority(), 'H');
648
649 auto& fru1 = callouts[0]->fruIdentity();
650 EXPECT_EQ(fru1->getPN().value(), "1234567");
651 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
652 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
653
654 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
655 EXPECT_EQ(callouts[1]->priority(), 'M');
656
657 auto& fru2 = callouts[1]->fruIdentity();
658 EXPECT_EQ(fru2->getPN().value(), "2345678");
659 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
660 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
661 }
662 }
663
664 // Test that a symbolic FRU with a trusted location code callout
665 // from the registry can get its location from the
666 // CALLOUT_INVENTORY_PATH AdditionalData entry.
TEST_F(SRCTest,SymbolicFRUWithInvPathTest)667 TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
668 {
669 message::Entry entry;
670 entry.src.type = 0xBD;
671 entry.src.reasonCode = 0xABCD;
672 entry.subsystem = 0x42;
673
674 entry.callouts = R"(
675 [{
676 "CalloutList":
677 [
678 {
679 "Priority": "high",
680 "SymbolicFRUTrusted": "service_docs",
681 "UseInventoryLocCode": true
682 },
683 {
684 "Priority": "medium",
685 "LocCode": "P0-C8",
686 "SymbolicFRUTrusted": "pwrsply"
687 }
688 ]
689 }])"_json;
690
691 {
692 // The location code for the first symbolic FRU callout will
693 // come from this inventory path since UseInventoryLocCode is set.
694 // In this case there will be no normal FRU callout for the motherboard.
695 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
696 AdditionalData ad{adData};
697 NiceMock<MockDataInterface> dataIface;
698 std::vector<std::string> names{"systemA"};
699
700 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
701
702 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
703 .Times(1)
704 .WillOnce(Return("Ufcs-P10"));
705
706 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
707 .WillOnce(Return("Ufcs-P0-C8"));
708
709 SRC src{entry, ad, dataIface};
710
711 auto& callouts = src.callouts()->callouts();
712 EXPECT_EQ(callouts.size(), 2);
713
714 // The location code for the first symbolic FRU callout with a
715 // trusted location code comes from the motherboard.
716 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
717 EXPECT_EQ(callouts[0]->priority(), 'H');
718 auto& fru1 = callouts[0]->fruIdentity();
719 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
720 EXPECT_EQ(fru1->failingComponentType(),
721 src::FRUIdentity::symbolicFRUTrustedLocCode);
722
723 // The second trusted symbolic FRU callouts uses the location
724 // code in the registry as usual.
725 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
726 EXPECT_EQ(callouts[1]->priority(), 'M');
727 auto& fru2 = callouts[1]->fruIdentity();
728 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
729 EXPECT_EQ(fru2->failingComponentType(),
730 src::FRUIdentity::symbolicFRUTrustedLocCode);
731 }
732
733 {
734 // This time say we want to use the location code from
735 // the inventory, but don't pass it in and the callout should
736 // end up a regular symbolic FRU
737 entry.callouts = R"(
738 [{
739 "CalloutList":
740 [
741 {
742 "Priority": "high",
743 "SymbolicFRUTrusted": "service_docs",
744 "UseInventoryLocCode": true
745 }
746 ]
747 }])"_json;
748
749 AdditionalData ad;
750 NiceMock<MockDataInterface> dataIface;
751 std::vector<std::string> names{"systemA"};
752
753 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
754
755 SRC src{entry, ad, dataIface};
756
757 auto& callouts = src.callouts()->callouts();
758 EXPECT_EQ(callouts.size(), 1);
759
760 EXPECT_EQ(callouts[0]->locationCode(), "");
761 EXPECT_EQ(callouts[0]->priority(), 'H');
762 auto& fru1 = callouts[0]->fruIdentity();
763 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
764 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
765 }
766 }
767
768 // Test looking up device path fails in the callout jSON.
TEST_F(SRCTest,DevicePathCalloutTest)769 TEST_F(SRCTest, DevicePathCalloutTest)
770 {
771 message::Entry entry;
772 entry.src.type = 0xBD;
773 entry.src.reasonCode = 0xABCD;
774 entry.subsystem = 0x42;
775
776 const auto calloutJSON = R"(
777 {
778 "I2C":
779 {
780 "14":
781 {
782 "114":
783 {
784 "Callouts":[
785 {
786 "Name": "/chassis/motherboard/cpu0",
787 "LocationCode": "P1-C40",
788 "Priority": "H"
789 },
790 {
791 "Name": "/chassis/motherboard",
792 "LocationCode": "P1",
793 "Priority": "M"
794 },
795 {
796 "Name": "/chassis/motherboard/bmc",
797 "LocationCode": "P1-C15",
798 "Priority": "L"
799 }
800 ],
801 "Dest": "proc 0 target"
802 }
803 }
804 }
805 })";
806
807 auto dataPath = getPELReadOnlyDataPath();
808 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
809 file << calloutJSON;
810 file.close();
811
812 NiceMock<MockDataInterface> dataIface;
813 std::vector<std::string> names{"systemA"};
814
815 EXPECT_CALL(dataIface, getSystemNames)
816 .Times(5)
817 .WillRepeatedly(Return(names));
818
819 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
820 .Times(3)
821 .WillRepeatedly(Return(std::vector<std::string>{
822 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
823
824 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
825 .Times(3)
826 .WillRepeatedly(Return(std::vector<std::string>{
827 "/xyz/openbmc_project/inventory/chassis/motherboard"}));
828
829 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
830 .Times(3)
831 .WillRepeatedly(Return(std::vector<std::string>{
832 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"}));
833
834 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
835 .Times(3)
836 .WillRepeatedly(Return("Ufcs-P1-C40"));
837
838 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
839 .Times(3)
840 .WillRepeatedly(Return("Ufcs-P1"));
841
842 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
843 .Times(3)
844 .WillRepeatedly(Return("Ufcs-P1-C15"));
845
846 EXPECT_CALL(dataIface,
847 getHWCalloutFields(
848 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0",
849 _, _, _))
850 .Times(3)
851 .WillRepeatedly(
852 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
853 SetArgReferee<3>("123456789ABC")));
854 EXPECT_CALL(
855 dataIface,
856 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
857 _, _, _))
858 .Times(3)
859 .WillRepeatedly(
860 DoAll(SetArgReferee<1>("7654321"), SetArgReferee<2>("MMMM"),
861 SetArgReferee<3>("CBA987654321")));
862 EXPECT_CALL(dataIface,
863 getHWCalloutFields(
864 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _,
865 _, _))
866 .Times(3)
867 .WillRepeatedly(
868 DoAll(SetArgReferee<1>("7123456"), SetArgReferee<2>("BBBB"),
869 SetArgReferee<3>("C123456789AB")));
870
871 // Call this below with different AdditionalData values that
872 // result in the same callouts.
873 auto checkCallouts = [&entry, &dataIface](const auto& items) {
874 AdditionalData ad{items};
875 SRC src{entry, ad, dataIface};
876
877 ASSERT_TRUE(src.callouts());
878 auto& callouts = src.callouts()->callouts();
879
880 ASSERT_EQ(callouts.size(), 3);
881
882 {
883 EXPECT_EQ(callouts[0]->priority(), 'H');
884 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
885
886 auto& fru = callouts[0]->fruIdentity();
887 EXPECT_EQ(fru->getPN().value(), "1234567");
888 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
889 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
890 }
891 {
892 EXPECT_EQ(callouts[1]->priority(), 'M');
893 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
894
895 auto& fru = callouts[1]->fruIdentity();
896 EXPECT_EQ(fru->getPN().value(), "7654321");
897 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
898 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
899 }
900 {
901 EXPECT_EQ(callouts[2]->priority(), 'L');
902 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
903
904 auto& fru = callouts[2]->fruIdentity();
905 EXPECT_EQ(fru->getPN().value(), "7123456");
906 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
907 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
908 }
909 };
910
911 {
912 // Callouts based on the device path
913 std::vector<std::string> items{
914 "CALLOUT_ERRNO=5",
915 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
916 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
917
918 checkCallouts(items);
919 }
920
921 {
922 // Callouts based on the I2C bus and address
923 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
924 "CALLOUT_IIC_ADDR=0x72"};
925 checkCallouts(items);
926 }
927
928 {
929 // Also based on I2C bus and address, but with bus = /dev/i2c-14
930 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
931 "CALLOUT_IIC_ADDR=0x72"};
932 checkCallouts(items);
933 }
934
935 {
936 // Callout not found
937 std::vector<std::string> items{
938 "CALLOUT_ERRNO=5",
939 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
940 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
941
942 AdditionalData ad{items};
943 SRC src{entry, ad, dataIface};
944
945 EXPECT_FALSE(src.callouts());
946 ASSERT_EQ(src.getDebugData().size(), 1);
947 EXPECT_EQ(src.getDebugData()[0],
948 "Problem looking up I2C callouts on 24 18: "
949 "[json.exception.out_of_range.403] key '24' not found");
950 }
951
952 {
953 // Callout not found
954 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
955 "CALLOUT_IIC_ADDR=0x99"};
956 AdditionalData ad{items};
957 SRC src{entry, ad, dataIface};
958
959 EXPECT_FALSE(src.callouts());
960 ASSERT_EQ(src.getDebugData().size(), 1);
961 EXPECT_EQ(src.getDebugData()[0],
962 "Problem looking up I2C callouts on 22 153: "
963 "[json.exception.out_of_range.403] key '22' not found");
964 }
965
966 fs::remove_all(dataPath);
967 }
968
969 // Test when callouts are passed in via JSON
TEST_F(SRCTest,JsonCalloutsTest)970 TEST_F(SRCTest, JsonCalloutsTest)
971 {
972 const auto jsonCallouts = R"(
973 [
974 {
975 "LocationCode": "P0-C1",
976 "Priority": "H",
977 "MRUs": [
978 {
979 "ID": 42,
980 "Priority": "H"
981 },
982 {
983 "ID": 43,
984 "Priority": "M"
985 }
986 ]
987 },
988 {
989 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
990 "Priority": "M",
991 "Guarded": true,
992 "Deconfigured": true
993 },
994 {
995 "Procedure": "PROCEDU",
996 "Priority": "A"
997 },
998 {
999 "SymbolicFRU": "TRUSTED",
1000 "Priority": "B",
1001 "TrustedLocationCode": true,
1002 "LocationCode": "P1-C23"
1003 },
1004 {
1005 "SymbolicFRU": "FRUTST1",
1006 "Priority": "C",
1007 "LocationCode": "P1-C24"
1008 },
1009 {
1010 "SymbolicFRU": "FRUTST2LONG",
1011 "Priority": "L"
1012 },
1013 {
1014 "Procedure": "fsi_path",
1015 "Priority": "L"
1016 },
1017 {
1018 "SymbolicFRU": "ambient_temp",
1019 "Priority": "L"
1020 }
1021 ]
1022 )"_json;
1023
1024 message::Entry entry;
1025 entry.src.type = 0xBD;
1026 entry.src.reasonCode = 0xABCD;
1027 entry.subsystem = 0x42;
1028
1029 AdditionalData ad;
1030 NiceMock<MockDataInterface> dataIface;
1031
1032 // Callout 0 mock calls
1033 {
1034 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1035 .Times(1)
1036 .WillOnce(Return("UXXX-P0-C1"));
1037 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1038 .Times(1)
1039 .WillOnce(Return(std::vector<std::string>{
1040 "/inv/system/chassis/motherboard/bmc"}));
1041 EXPECT_CALL(
1042 dataIface,
1043 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1044 .Times(1)
1045 .WillOnce(
1046 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1047 SetArgReferee<3>("123456789ABC")));
1048 }
1049 // Callout 1 mock calls
1050 {
1051 EXPECT_CALL(dataIface,
1052 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1053 .WillOnce(Return("UYYY-P5"));
1054 EXPECT_CALL(
1055 dataIface,
1056 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1057 .Times(1)
1058 .WillOnce(
1059 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
1060 SetArgReferee<3>("23456789ABCD")));
1061 }
1062 // Callout 3 mock calls
1063 {
1064 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1065 .Times(1)
1066 .WillOnce(Return("UXXX-P1-C23"));
1067 }
1068 // Callout 4 mock calls
1069 {
1070 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1071 .Times(1)
1072 .WillOnce(Return("UXXX-P1-C24"));
1073 }
1074
1075 SRC src{entry, ad, jsonCallouts, dataIface};
1076 ASSERT_TRUE(src.callouts());
1077
1078 // Check the guarded and deconfigured flags
1079 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1080
1081 const auto& callouts = src.callouts()->callouts();
1082 ASSERT_EQ(callouts.size(), 8);
1083
1084 // Check callout 0
1085 {
1086 EXPECT_EQ(callouts[0]->priority(), 'H');
1087 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1088
1089 auto& fru = callouts[0]->fruIdentity();
1090 EXPECT_EQ(fru->getPN().value(), "1234567");
1091 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1092 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1093 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1094
1095 auto& mruCallouts = callouts[0]->mru();
1096 ASSERT_TRUE(mruCallouts);
1097 auto& mrus = mruCallouts->mrus();
1098 ASSERT_EQ(mrus.size(), 2);
1099 EXPECT_EQ(mrus[0].id, 42);
1100 EXPECT_EQ(mrus[0].priority, 'H');
1101 EXPECT_EQ(mrus[1].id, 43);
1102 EXPECT_EQ(mrus[1].priority, 'M');
1103 }
1104
1105 // Check callout 1
1106 {
1107 EXPECT_EQ(callouts[1]->priority(), 'M');
1108 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1109
1110 auto& fru = callouts[1]->fruIdentity();
1111 EXPECT_EQ(fru->getPN().value(), "2345678");
1112 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1113 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1114 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1115 }
1116
1117 // Check callout 2
1118 {
1119 EXPECT_EQ(callouts[2]->priority(), 'A');
1120 EXPECT_EQ(callouts[2]->locationCode(), "");
1121
1122 auto& fru = callouts[2]->fruIdentity();
1123 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1124 EXPECT_EQ(fru->failingComponentType(),
1125 src::FRUIdentity::maintenanceProc);
1126 }
1127
1128 // Check callout 3
1129 {
1130 EXPECT_EQ(callouts[3]->priority(), 'B');
1131 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1132
1133 auto& fru = callouts[3]->fruIdentity();
1134 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1135 EXPECT_EQ(fru->failingComponentType(),
1136 src::FRUIdentity::symbolicFRUTrustedLocCode);
1137 }
1138
1139 // Check callout 4
1140 {
1141 EXPECT_EQ(callouts[4]->priority(), 'C');
1142 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1143
1144 auto& fru = callouts[4]->fruIdentity();
1145 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1146 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1147 }
1148
1149 // Check callout 5
1150 {
1151 EXPECT_EQ(callouts[5]->priority(), 'L');
1152 EXPECT_EQ(callouts[5]->locationCode(), "");
1153
1154 auto& fru = callouts[5]->fruIdentity();
1155 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1156 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1157 }
1158
1159 // Check callout 6
1160 {
1161 EXPECT_EQ(callouts[6]->priority(), 'L');
1162 EXPECT_EQ(callouts[6]->locationCode(), "");
1163
1164 auto& fru = callouts[6]->fruIdentity();
1165 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004");
1166 EXPECT_EQ(fru->failingComponentType(),
1167 src::FRUIdentity::maintenanceProc);
1168 }
1169
1170 // Check callout 7
1171 {
1172 EXPECT_EQ(callouts[7]->priority(), 'L');
1173 EXPECT_EQ(callouts[7]->locationCode(), "");
1174
1175 auto& fru = callouts[7]->fruIdentity();
1176 EXPECT_EQ(fru->getPN().value(), "AMBTEMP");
1177 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1178 }
1179
1180 // Check that it didn't find any errors
1181 const auto& data = src.getDebugData();
1182 EXPECT_TRUE(data.empty());
1183 }
1184
TEST_F(SRCTest,JsonBadCalloutsTest)1185 TEST_F(SRCTest, JsonBadCalloutsTest)
1186 {
1187 // The first call will have a Throw in a mock call.
1188 // The second will have a different Throw in a mock call.
1189 // The others have issues with the Priority field.
1190 const auto jsonCallouts = R"(
1191 [
1192 {
1193 "LocationCode": "P0-C1",
1194 "Priority": "H"
1195 },
1196 {
1197 "LocationCode": "P0-C2",
1198 "Priority": "H"
1199 },
1200 {
1201 "LocationCode": "P0-C3"
1202 },
1203 {
1204 "LocationCode": "P0-C4",
1205 "Priority": "X"
1206 }
1207 ]
1208 )"_json;
1209
1210 message::Entry entry;
1211 entry.src.type = 0xBD;
1212 entry.src.reasonCode = 0xABCD;
1213 entry.subsystem = 0x42;
1214
1215 AdditionalData ad;
1216 NiceMock<MockDataInterface> dataIface;
1217
1218 // Callout 0 mock calls
1219 // Expand location code will fail, so the unexpanded location
1220 // code should show up in the callout instead.
1221 {
1222 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1223 .WillOnce(Throw(std::runtime_error("Fail")));
1224
1225 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1226 .Times(1)
1227 .WillOnce(Return(std::vector<std::string>{
1228 "/inv/system/chassis/motherboard/bmc"}));
1229 EXPECT_CALL(
1230 dataIface,
1231 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1232 .Times(1)
1233 .WillOnce(
1234 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1235 SetArgReferee<3>("123456789ABC")));
1236 }
1237
1238 // Callout 1 mock calls
1239 // getInventoryFromLocCode will fail
1240 {
1241 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1242 .Times(1)
1243 .WillOnce(Return("UXXX-P0-C2"));
1244
1245 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1246 .Times(1)
1247 .WillOnce(Throw(std::runtime_error("Fail")));
1248 }
1249
1250 SRC src{entry, ad, jsonCallouts, dataIface};
1251
1252 ASSERT_TRUE(src.callouts());
1253
1254 const auto& callouts = src.callouts()->callouts();
1255
1256 // Only the first callout was successful
1257 ASSERT_EQ(callouts.size(), 1);
1258
1259 {
1260 EXPECT_EQ(callouts[0]->priority(), 'H');
1261 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1262
1263 auto& fru = callouts[0]->fruIdentity();
1264 EXPECT_EQ(fru->getPN().value(), "1234567");
1265 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1266 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1267 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1268 }
1269
1270 const auto& data = src.getDebugData();
1271 ASSERT_EQ(data.size(), 4);
1272 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1273 EXPECT_STREQ(data[1].c_str(),
1274 "Failed extracting callout data from JSON: Unable to "
1275 "get inventory path from location code: P0-C2: Fail");
1276 EXPECT_STREQ(data[2].c_str(),
1277 "Failed extracting callout data from JSON: "
1278 "[json.exception.out_of_range.403] key 'Priority' not found");
1279 EXPECT_STREQ(data[3].c_str(),
1280 "Failed extracting callout data from JSON: Invalid "
1281 "priority 'X' found in JSON callout");
1282 }
1283
1284 // Test that an inventory path callout can have
1285 // a different priority than H.
TEST_F(SRCTest,InventoryCalloutTestPriority)1286 TEST_F(SRCTest, InventoryCalloutTestPriority)
1287 {
1288 message::Entry entry;
1289 entry.src.type = 0xBD;
1290 entry.src.reasonCode = 0xABCD;
1291 entry.subsystem = 0x42;
1292
1293 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
1294 "CALLOUT_PRIORITY=M"};
1295 AdditionalData ad{adData};
1296 NiceMock<MockDataInterface> dataIface;
1297
1298 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1299 .WillOnce(Return("UTMS-P1"));
1300
1301 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1302 .Times(1)
1303 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1304 SetArgReferee<3>("123456789ABC")));
1305
1306 SRC src{entry, ad, dataIface};
1307 EXPECT_TRUE(src.valid());
1308
1309 ASSERT_TRUE(src.callouts());
1310
1311 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1312
1313 auto& callout = src.callouts()->callouts().front();
1314
1315 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1316 EXPECT_EQ(callout->priority(), 'M');
1317 }
1318
1319 // Test SRC with additional data - PEL_SUBSYSTEM
TEST_F(SRCTest,TestPELSubsystem)1320 TEST_F(SRCTest, TestPELSubsystem)
1321 {
1322 message::Entry entry;
1323 entry.src.type = 0xBD;
1324 entry.src.reasonCode = 0xABCD;
1325 entry.subsystem = 0x42;
1326
1327 // Values for the SRC words pointed to above
1328 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
1329 AdditionalData ad{adData};
1330 NiceMock<MockDataInterface> dataIface;
1331
1332 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1333
1334 SRC src{entry, ad, dataIface};
1335
1336 EXPECT_TRUE(src.valid());
1337
1338 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1339 }
1340
setAsciiString(std::vector<uint8_t> & src,const std::string & value)1341 void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1342 {
1343 assert(40 + value.size() <= src.size());
1344
1345 for (size_t i = 0; i < value.size(); i++)
1346 {
1347 src[40 + i] = value[i];
1348 }
1349 }
1350
TEST_F(SRCTest,TestGetProgressCode)1351 TEST_F(SRCTest, TestGetProgressCode)
1352 {
1353 {
1354 // A real SRC with CC009184
1355 std::vector<uint8_t> src{
1356 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1357 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1358 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1359 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1360 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1361
1362 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1363 }
1364
1365 {
1366 // A real SRC with STANDBY
1367 std::vector<uint8_t> src{
1368 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1369 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1370 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1371 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1372 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1373
1374 EXPECT_EQ(SRC::getProgressCode(src), 0);
1375 }
1376
1377 {
1378 // A real SRC with CC009184, but 1 byte too short
1379 std::vector<uint8_t> src{
1380 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1381 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1382 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1383 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1384 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1385 src.resize(71);
1386 EXPECT_EQ(SRC::getProgressCode(src), 0);
1387 }
1388
1389 {
1390 // A few different ones
1391 const std::map<std::string, uint32_t> progressCodes{
1392 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1393 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1394 {"1234567X", 0}, {"1 ", 0}};
1395
1396 std::vector<uint8_t> src(72, 0x0);
1397
1398 for (const auto& [code, expected] : progressCodes)
1399 {
1400 setAsciiString(src, code);
1401 EXPECT_EQ(SRC::getProgressCode(src), expected);
1402 }
1403
1404 // empty
1405 src.clear();
1406 EXPECT_EQ(SRC::getProgressCode(src), 0);
1407 }
1408 }
1409
1410 // Test progress is in right SRC hex data field
TEST_F(SRCTest,TestProgressCodeField)1411 TEST_F(SRCTest, TestProgressCodeField)
1412 {
1413 message::Entry entry;
1414 entry.src.type = 0xBD;
1415 entry.src.reasonCode = 0xABCD;
1416 entry.subsystem = 0x42;
1417
1418 AdditionalData ad;
1419 NiceMock<MockDataInterface> dataIface;
1420 EXPECT_CALL(dataIface, getRawProgressSRC())
1421 .WillOnce(Return(std::vector<uint8_t>{
1422 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1423 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1425 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1426 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1427
1428 SRC src{entry, ad, dataIface};
1429 EXPECT_TRUE(src.valid());
1430
1431 // Verify that the hex vlue is set at the right hexword
1432 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1433 }
1434