blob: cac653aabd3df5e8a1e5dc766a27fc96f91bb5a9 [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::_;
26using ::testing::InvokeWithoutArgs;
Matt Spinler075e5ba2020-02-21 15:46:00 -060027using ::testing::NiceMock;
28using ::testing::Return;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -050029using ::testing::ReturnRef;
Matt Spinlered046852020-03-13 13:58:15 -050030using ::testing::SetArgReferee;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080031namespace fs = std::filesystem;
Matt Spinlerf9bae182019-10-09 13:37:38 -050032
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +080033const auto testRegistry = R"(
34{
35"PELs":
36[
37 {
38 "Name": "xyz.openbmc_project.Error.Test",
39 "Subsystem": "bmc_firmware",
40 "SRC":
41 {
42 "ReasonCode": "0xABCD",
43 "Words6To9":
44 {
45 "6":
46 {
47 "Description": "Component ID",
48 "AdditionalDataPropSource": "COMPID"
49 },
50 "7":
51 {
52 "Description": "Failure count",
53 "AdditionalDataPropSource": "FREQUENCY"
54 },
55 "8":
56 {
57 "Description": "Time period",
58 "AdditionalDataPropSource": "DURATION"
59 },
60 "9":
61 {
62 "Description": "Error code",
63 "AdditionalDataPropSource": "ERRORCODE"
64 }
65 }
66 },
67 "Documentation":
68 {
69 "Description": "A Component Fault",
70 "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
71 "MessageArgSources":
72 [
73 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
74 ]
75 }
76 }
77]
78}
79)";
80
81class SRCTest : public ::testing::Test
82{
83 protected:
84 static void SetUpTestCase()
85 {
86 char path[] = "/tmp/srctestXXXXXX";
87 regDir = mkdtemp(path);
88 }
89
90 static void TearDownTestCase()
91 {
92 fs::remove_all(regDir);
93 }
94
95 static std::string writeData(const char* data)
96 {
97 fs::path path = regDir / "registry.json";
98 std::ofstream stream{path};
99 stream << data;
100 return path;
101 }
102
103 static fs::path regDir;
104};
105
106fs::path SRCTest::regDir{};
107
108TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500109{
Matt Spinler42828bd2019-10-11 10:39:30 -0500110 auto data = pelDataFactory(TestPELType::primarySRCSection);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500111
112 Stream stream{data};
113 SRC src{stream};
114
115 EXPECT_TRUE(src.valid());
116
117 EXPECT_EQ(src.header().id, 0x5053);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600118 EXPECT_EQ(src.header().size, 0x50);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500119 EXPECT_EQ(src.header().version, 0x01);
120 EXPECT_EQ(src.header().subType, 0x01);
121 EXPECT_EQ(src.header().componentID, 0x0202);
122
123 EXPECT_EQ(src.version(), 0x02);
124 EXPECT_EQ(src.flags(), 0x00);
125 EXPECT_EQ(src.hexWordCount(), 9);
126 EXPECT_EQ(src.size(), 0x48);
127
128 const auto& hexwords = src.hexwordData();
Matt Spinlerbd716f02019-10-15 10:54:11 -0500129 EXPECT_EQ(0x02020255, hexwords[0]);
130 EXPECT_EQ(0x03030310, hexwords[1]);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500131 EXPECT_EQ(0x04040404, hexwords[2]);
132 EXPECT_EQ(0x05050505, hexwords[3]);
133 EXPECT_EQ(0x06060606, hexwords[4]);
134 EXPECT_EQ(0x07070707, hexwords[5]);
135 EXPECT_EQ(0x08080808, hexwords[6]);
136 EXPECT_EQ(0x09090909, hexwords[7]);
137
138 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
139 EXPECT_FALSE(src.callouts());
140
141 // Flatten
142 std::vector<uint8_t> newData;
143 Stream newStream{newData};
144
145 src.flatten(newStream);
146 EXPECT_EQ(data, newData);
147}
148
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800149TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
Matt Spinlerf9bae182019-10-09 13:37:38 -0500150{
Matt Spinler42828bd2019-10-11 10:39:30 -0500151 auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
Matt Spinlerf9bae182019-10-09 13:37:38 -0500152
153 Stream stream{data};
154 SRC src{stream};
155
156 EXPECT_TRUE(src.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500157 EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
Matt Spinlerf9bae182019-10-09 13:37:38 -0500158
159 // Spot check the SRC fields, but they're the same as above
160 EXPECT_EQ(src.asciiString(), "BD8D5678 ");
161
162 // There should be 2 callouts
163 const auto& calloutsSection = src.callouts();
164 ASSERT_TRUE(calloutsSection);
165 const auto& callouts = calloutsSection->callouts();
166 EXPECT_EQ(callouts.size(), 2);
167
168 // spot check that each callout has the right substructures
169 EXPECT_TRUE(callouts.front()->fruIdentity());
170 EXPECT_FALSE(callouts.front()->pceIdentity());
171 EXPECT_FALSE(callouts.front()->mru());
172
173 EXPECT_TRUE(callouts.back()->fruIdentity());
174 EXPECT_TRUE(callouts.back()->pceIdentity());
175 EXPECT_TRUE(callouts.back()->mru());
176
177 // Flatten
178 std::vector<uint8_t> newData;
179 Stream newStream{newData};
180
181 src.flatten(newStream);
182 EXPECT_EQ(data, newData);
183}
Matt Spinlerbd716f02019-10-15 10:54:11 -0500184
185// Create an SRC from the message registry
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800186TEST_F(SRCTest, CreateTestNoCallouts)
Matt Spinlerbd716f02019-10-15 10:54:11 -0500187{
188 message::Entry entry;
189 entry.src.type = 0xBD;
190 entry.src.reasonCode = 0xABCD;
191 entry.subsystem = 0x42;
192 entry.src.powerFault = true;
193 entry.src.hexwordADFields = {{5, "TEST1"}, // Not a user defined word
194 {6, "TEST1"},
195 {7, "TEST2"},
196 {8, "TEST3"},
197 {9, "TEST4"}};
198
199 // Values for the SRC words pointed to above
200 std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
201 "TEST3=0XDEF", "TEST4=Z"};
202 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600203 NiceMock<MockDataInterface> dataIface;
204
205 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
206
207 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500208
209 EXPECT_TRUE(src.valid());
210 EXPECT_TRUE(src.isPowerFaultEvent());
211 EXPECT_EQ(src.size(), baseSRCSize);
212
213 const auto& hexwords = src.hexwordData();
214
215 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
216 // array index starts at 0 use the math in the [] below to make it easier
217 // to tell what is being accessed.
218 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
219 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
220 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
221 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600222 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500223
224 // Validate more fields here as the code starts filling them in.
225
226 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
227 EXPECT_EQ(hexwords[5 - 2], 0);
228
229 // The user defined hex word fields specifed in the additional data.
230 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
231 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
232 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
233 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
234
235 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
236
237 // No callouts
238 EXPECT_FALSE(src.callouts());
239
240 // May as well spot check the flatten/unflatten
241 std::vector<uint8_t> data;
242 Stream stream{data};
243 src.flatten(stream);
244
245 stream.offset(0);
246 SRC newSRC{stream};
247
248 EXPECT_TRUE(newSRC.valid());
249 EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent());
250 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
251 EXPECT_FALSE(newSRC.callouts());
252}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800253
Matt Spinler075e5ba2020-02-21 15:46:00 -0600254// Test when the CCIN string isn't a 4 character number
255TEST_F(SRCTest, BadCCINTest)
256{
257 message::Entry entry;
258 entry.src.type = 0xBD;
259 entry.src.reasonCode = 0xABCD;
260 entry.subsystem = 0x42;
261 entry.src.powerFault = false;
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
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);
314 EXPECT_EQ(
315 errorDetails.value(),
316 "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF");
317}
Matt Spinlered046852020-03-13 13:58:15 -0500318
319// Test that an inventory path callout string is
320// converted into the appropriate FRU callout.
321TEST_F(SRCTest, InventoryCalloutTest)
322{
323 message::Entry entry;
324 entry.src.type = 0xBD;
325 entry.src.reasonCode = 0xABCD;
326 entry.subsystem = 0x42;
327 entry.src.powerFault = false;
328
329 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
330 AdditionalData ad{adData};
331 NiceMock<MockDataInterface> dataIface;
332
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500333 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
334 .WillOnce(Return("UTMS-P1"));
335
336 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500337 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500338 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
339 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500340
341 SRC src{entry, ad, dataIface};
342 EXPECT_TRUE(src.valid());
343
344 ASSERT_TRUE(src.callouts());
345
346 EXPECT_EQ(src.callouts()->callouts().size(), 1);
347
348 auto& callout = src.callouts()->callouts().front();
349
350 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500351 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500352
353 auto& fru = callout->fruIdentity();
354
355 EXPECT_EQ(fru->getPN().value(), "1234567");
356 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
357 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
358
359 // flatten and unflatten
360 std::vector<uint8_t> data;
361 Stream stream{data};
362 src.flatten(stream);
363
364 stream.offset(0);
365 SRC newSRC{stream};
366 EXPECT_TRUE(newSRC.valid());
367 ASSERT_TRUE(src.callouts());
368 EXPECT_EQ(src.callouts()->callouts().size(), 1);
369}
370
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500371// Test that when the location code can't be obtained that
Matt Spinlered046852020-03-13 13:58:15 -0500372// a procedure callout is used.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500373TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
374{
375 message::Entry entry;
376 entry.src.type = 0xBD;
377 entry.src.reasonCode = 0xABCD;
378 entry.subsystem = 0x42;
379 entry.src.powerFault = false;
380
381 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
382 AdditionalData ad{adData};
383 NiceMock<MockDataInterface> dataIface;
384
385 auto func = []() {
386 throw sdbusplus::exception::SdBusError(5, "Error");
387 return std::string{};
388 };
389
390 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
391 .Times(1)
392 .WillOnce(InvokeWithoutArgs(func));
393
394 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
395
396 SRC src{entry, ad, dataIface};
397 EXPECT_TRUE(src.valid());
398
399 ASSERT_TRUE(src.callouts());
400
401 EXPECT_EQ(src.callouts()->callouts().size(), 1);
402
403 auto& callout = src.callouts()->callouts().front();
404 EXPECT_EQ(callout->locationCodeSize(), 0);
Matt Spinler717de422020-06-04 13:10:14 -0500405 EXPECT_EQ(callout->priority(), 'H');
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500406
407 auto& fru = callout->fruIdentity();
408
409 EXPECT_EQ(fru->getMaintProc().value(), "BMCSP01");
410 EXPECT_FALSE(fru->getPN());
411 EXPECT_FALSE(fru->getSN());
412 EXPECT_FALSE(fru->getCCIN());
413
414 // flatten and unflatten
415 std::vector<uint8_t> data;
416 Stream stream{data};
417 src.flatten(stream);
418
419 stream.offset(0);
420 SRC newSRC{stream};
421 EXPECT_TRUE(newSRC.valid());
422 ASSERT_TRUE(src.callouts());
423 EXPECT_EQ(src.callouts()->callouts().size(), 1);
424}
425
426// Test that when the VPD can't be obtained that
427// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500428TEST_F(SRCTest, InventoryCalloutNoVPDTest)
429{
430 message::Entry entry;
431 entry.src.type = 0xBD;
432 entry.src.reasonCode = 0xABCD;
433 entry.subsystem = 0x42;
434 entry.src.powerFault = false;
435
436 std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard"};
437 AdditionalData ad{adData};
438 NiceMock<MockDataInterface> dataIface;
439
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500440 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
441 .Times(1)
442 .WillOnce(Return("UTMS-P10"));
443
Matt Spinlered046852020-03-13 13:58:15 -0500444 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
445
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500446 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500447 .Times(1)
448 .WillOnce(InvokeWithoutArgs(func));
449
450 SRC src{entry, ad, dataIface};
451 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500452 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500453 EXPECT_EQ(src.callouts()->callouts().size(), 1);
454
455 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500456 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500457 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500458
459 auto& fru = callout->fruIdentity();
460
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500461 EXPECT_EQ(fru->getPN(), "");
462 EXPECT_EQ(fru->getCCIN(), "");
463 EXPECT_EQ(fru->getSN(), "");
464 EXPECT_FALSE(fru->getMaintProc());
465
Matt Spinlered046852020-03-13 13:58:15 -0500466 // flatten and unflatten
467 std::vector<uint8_t> data;
468 Stream stream{data};
469 src.flatten(stream);
470
471 stream.offset(0);
472 SRC newSRC{stream};
473 EXPECT_TRUE(newSRC.valid());
474 ASSERT_TRUE(src.callouts());
475 EXPECT_EQ(src.callouts()->callouts().size(), 1);
476}
Matt Spinler03984582020-04-09 13:17:58 -0500477
478TEST_F(SRCTest, RegistryCalloutTest)
479{
480 message::Entry entry;
481 entry.src.type = 0xBD;
482 entry.src.reasonCode = 0xABCD;
483 entry.subsystem = 0x42;
484 entry.src.powerFault = false;
485
486 entry.callouts = R"(
487 [
488 {
489 "System": "systemA",
490 "CalloutList":
491 [
492 {
493 "Priority": "high",
494 "SymbolicFRU": "service_docs"
495 },
496 {
497 "Priority": "medium",
498 "Procedure": "no_vpd_for_fru"
499 }
500 ]
501 },
502 {
503 "System": "systemB",
504 "CalloutList":
505 [
506 {
507 "Priority": "high",
508 "LocCode": "P0-C8",
509 "SymbolicFRUTrusted": "service_docs"
510 },
511 {
512 "Priority": "medium",
513 "SymbolicFRUTrusted": "service_docs"
514 }
515 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500516 },
517 {
518 "System": "systemC",
519 "CalloutList":
520 [
521 {
522 "Priority": "high",
523 "LocCode": "P0-C8"
524 },
525 {
526 "Priority": "medium",
527 "LocCode": "P0-C9"
528 }
529 ]
Matt Spinler03984582020-04-09 13:17:58 -0500530 }
531 ])"_json;
532
533 {
534 // Call out a symbolic FRU and a procedure
535 AdditionalData ad;
536 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500537 std::vector<std::string> names{"systemA"};
538
539 EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
Matt Spinler03984582020-04-09 13:17:58 -0500540
541 SRC src{entry, ad, dataIface};
542
543 auto& callouts = src.callouts()->callouts();
544 ASSERT_EQ(callouts.size(), 2);
545
546 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
547 EXPECT_EQ(callouts[0]->priority(), 'H');
548
549 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
550 EXPECT_EQ(callouts[1]->priority(), 'M');
551
552 auto& fru1 = callouts[0]->fruIdentity();
553 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
554 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
555 EXPECT_FALSE(fru1->getMaintProc());
556 EXPECT_FALSE(fru1->getSN());
557 EXPECT_FALSE(fru1->getCCIN());
558
559 auto& fru2 = callouts[1]->fruIdentity();
560 EXPECT_EQ(fru2->getMaintProc().value(), "BMCSP01");
561 EXPECT_EQ(fru2->failingComponentType(),
562 src::FRUIdentity::maintenanceProc);
563 EXPECT_FALSE(fru2->getPN());
564 EXPECT_FALSE(fru2->getSN());
565 EXPECT_FALSE(fru2->getCCIN());
566 }
567
568 {
569 // Call out a trusted symbolic FRU with a location code, and
570 // another one without.
571 AdditionalData ad;
572 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500573 std::vector<std::string> names{"systemB"};
574
Matt Spinleraf191c72020-06-04 11:35:13 -0500575 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500576 EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
Matt Spinler03984582020-04-09 13:17:58 -0500577
578 SRC src{entry, ad, dataIface};
579
580 auto& callouts = src.callouts()->callouts();
581 EXPECT_EQ(callouts.size(), 2);
582
583 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
584 EXPECT_EQ(callouts[0]->priority(), 'H');
585
586 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
587 EXPECT_EQ(callouts[1]->priority(), 'M');
588
589 auto& fru1 = callouts[0]->fruIdentity();
590 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
591 EXPECT_EQ(fru1->failingComponentType(),
592 src::FRUIdentity::symbolicFRUTrustedLocCode);
593 EXPECT_FALSE(fru1->getMaintProc());
594 EXPECT_FALSE(fru1->getSN());
595 EXPECT_FALSE(fru1->getCCIN());
596
597 // It asked for a trusted symbolic FRU, but no location code
598 // was provided so it is switched back to a normal one
599 auto& fru2 = callouts[1]->fruIdentity();
600 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
601 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
602 EXPECT_FALSE(fru2->getMaintProc());
603 EXPECT_FALSE(fru2->getSN());
604 EXPECT_FALSE(fru2->getCCIN());
605 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500606
607 {
608 // Two hardware callouts
609 AdditionalData ad;
610 NiceMock<MockDataInterface> dataIface;
611 std::vector<std::string> names{"systemC"};
612
613 EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
614
615 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
616 .WillOnce(Return("UXXX-P0-C8"));
617
618 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
619 .WillOnce(Return("UXXX-P0-C9"));
620
621 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0))
622 .WillOnce(Return(
623 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
624
625 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0))
626 .WillOnce(Return(
627 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
628
629 EXPECT_CALL(
630 dataIface,
631 getHWCalloutFields(
632 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
633 _))
634 .Times(1)
635 .WillOnce(DoAll(SetArgReferee<1>("1234567"),
636 SetArgReferee<2>("CCCC"),
637 SetArgReferee<3>("123456789ABC")));
638
639 EXPECT_CALL(
640 dataIface,
641 getHWCalloutFields(
642 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
643 _))
644 .Times(1)
645 .WillOnce(DoAll(SetArgReferee<1>("2345678"),
646 SetArgReferee<2>("DDDD"),
647 SetArgReferee<3>("23456789ABCD")));
648
649 SRC src{entry, ad, dataIface};
650
651 auto& callouts = src.callouts()->callouts();
652 EXPECT_EQ(callouts.size(), 2);
653
654 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
655 EXPECT_EQ(callouts[0]->priority(), 'H');
656
657 auto& fru1 = callouts[0]->fruIdentity();
658 EXPECT_EQ(fru1->getPN().value(), "1234567");
659 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
660 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
661
662 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
663 EXPECT_EQ(callouts[1]->priority(), 'M');
664
665 auto& fru2 = callouts[1]->fruIdentity();
666 EXPECT_EQ(fru2->getPN().value(), "2345678");
667 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
668 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
669 }
Matt Spinler03984582020-04-09 13:17:58 -0500670}
Matt Spinler717de422020-06-04 13:10:14 -0500671
672// Test looking up device path fails in the callout jSON.
673TEST_F(SRCTest, DevicePathCalloutTest)
674{
675 message::Entry entry;
676 entry.src.type = 0xBD;
677 entry.src.reasonCode = 0xABCD;
678 entry.subsystem = 0x42;
679 entry.src.powerFault = false;
680
681 const auto calloutJSON = R"(
682 {
683 "I2C":
684 {
685 "14":
686 {
687 "114":
688 {
689 "Callouts":[
690 {
691 "Name": "/chassis/motherboard/cpu0",
692 "LocationCode": "P1-C40",
693 "Priority": "H"
694 },
695 {
696 "Name": "/chassis/motherboard",
697 "LocationCode": "P1",
698 "Priority": "M"
699 },
700 {
701 "Name": "/chassis/motherboard/bmc",
702 "LocationCode": "P1-C15",
703 "Priority": "L"
704 }
705 ],
706 "Dest": "proc 0 target"
707 }
708 }
709 }
710 })";
711
712 auto dataPath = getPELReadOnlyDataPath();
713 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
714 file << calloutJSON;
715 file.close();
716
717 NiceMock<MockDataInterface> dataIface;
718 std::vector<std::string> names{"systemA"};
719
720 EXPECT_CALL(dataIface, getSystemNames)
721 .Times(5)
722 .WillRepeatedly(ReturnRef(names));
723
724 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0))
725 .Times(3)
726 .WillRepeatedly(
727 Return("/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
728
729 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0))
730 .Times(3)
731 .WillRepeatedly(
732 Return("/xyz/openbmc_project/inventory/chassis/motherboard"));
733
734 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0))
735 .Times(3)
736 .WillRepeatedly(
737 Return("/xyz/openbmc_project/inventory/chassis/motherboard/bmc"));
738
739 EXPECT_CALL(dataIface,
740 getLocationCode(
741 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"))
742 .Times(3)
743 .WillRepeatedly(Return("Ufcs-P1-C40"));
744 EXPECT_CALL(
745 dataIface,
746 getLocationCode("/xyz/openbmc_project/inventory/chassis/motherboard"))
747 .Times(3)
748 .WillRepeatedly(Return("Ufcs-P1"));
749 EXPECT_CALL(dataIface,
750 getLocationCode(
751 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"))
752 .Times(3)
753 .WillRepeatedly(Return("Ufcs-P1-C15"));
754
755 EXPECT_CALL(
756 dataIface,
757 getHWCalloutFields(
758 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _, _))
759 .Times(3)
760 .WillRepeatedly(DoAll(SetArgReferee<1>("1234567"),
761 SetArgReferee<2>("CCCC"),
762 SetArgReferee<3>("123456789ABC")));
763 EXPECT_CALL(
764 dataIface,
765 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
766 _, _, _))
767 .Times(3)
768 .WillRepeatedly(DoAll(SetArgReferee<1>("7654321"),
769 SetArgReferee<2>("MMMM"),
770 SetArgReferee<3>("CBA987654321")));
771 EXPECT_CALL(
772 dataIface,
773 getHWCalloutFields(
774 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _, _, _))
775 .Times(3)
776 .WillRepeatedly(DoAll(SetArgReferee<1>("7123456"),
777 SetArgReferee<2>("BBBB"),
778 SetArgReferee<3>("C123456789AB")));
779
780 // Call this below with different AdditionalData values that
781 // result in the same callouts.
782 auto checkCallouts = [&entry, &dataIface](const auto& items) {
783 AdditionalData ad{items};
784 SRC src{entry, ad, dataIface};
785
786 ASSERT_TRUE(src.callouts());
787 auto& callouts = src.callouts()->callouts();
788
789 ASSERT_EQ(callouts.size(), 3);
790
791 {
792 EXPECT_EQ(callouts[0]->priority(), 'H');
793 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
794
795 auto& fru = callouts[0]->fruIdentity();
796 EXPECT_EQ(fru->getPN().value(), "1234567");
797 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
798 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
799 }
800 {
801 EXPECT_EQ(callouts[1]->priority(), 'M');
802 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
803
804 auto& fru = callouts[1]->fruIdentity();
805 EXPECT_EQ(fru->getPN().value(), "7654321");
806 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
807 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
808 }
809 {
810 EXPECT_EQ(callouts[2]->priority(), 'L');
811 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
812
813 auto& fru = callouts[2]->fruIdentity();
814 EXPECT_EQ(fru->getPN().value(), "7123456");
815 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
816 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
817 }
818 };
819
820 {
821 // Callouts based on the device path
822 std::vector<std::string> items{
823 "CALLOUT_ERRNO=5",
824 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
825 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"};
826
827 checkCallouts(items);
828 }
829
830 {
831 // Callouts based on the I2C bus and address
832 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
833 "CALLOUT_IIC_ADDR=0x72"};
834 checkCallouts(items);
835 }
836
837 {
838 // Also based on I2C bus and address, but with bus = /dev/i2c-14
839 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=14",
840 "CALLOUT_IIC_ADDR=0x72"};
841 checkCallouts(items);
842 }
843
844 {
845 // Callout not found
846 std::vector<std::string> items{
847 "CALLOUT_ERRNO=5",
848 "CALLOUT_DEVICE_PATH=/sys/devices/platform/ahb/ahb:apb/"
849 "ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"};
850
851 AdditionalData ad{items};
852 SRC src{entry, ad, dataIface};
853
854 EXPECT_FALSE(src.callouts());
855 ASSERT_EQ(src.getDebugData().size(), 1);
856 EXPECT_EQ(src.getDebugData()[0],
857 "Problem looking up I2C callouts on 24 18: "
858 "[json.exception.out_of_range.403] key '24' not found");
859 }
860
861 {
862 // Callout not found
863 std::vector<std::string> items{"CALLOUT_ERRNO=5", "CALLOUT_IIC_BUS=22",
864 "CALLOUT_IIC_ADDR=0x99"};
865 AdditionalData ad{items};
866 SRC src{entry, ad, dataIface};
867
868 EXPECT_FALSE(src.callouts());
869 ASSERT_EQ(src.getDebugData().size(), 1);
870 EXPECT_EQ(src.getDebugData()[0],
871 "Problem looking up I2C callouts on 22 153: "
872 "[json.exception.out_of_range.403] key '22' not found");
873 }
874
875 fs::remove_all(dataPath);
876}