blob: 2fb8770fae6f2ae4e5665a88bdcf5b04122bc571 [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 Spinlerda5b76b2023-06-01 15:56:57 -0500466 entry.src.checkstopFlag = true;
Matt Spinler03984582020-04-09 13:17:58 -0500467 entry.subsystem = 0x42;
Matt Spinler03984582020-04-09 13:17:58 -0500468
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",
Matt Spinler2edce4e2024-01-17 11:13:51 -0600481 "Procedure": "BMC0001"
Matt Spinler03984582020-04-09 13:17:58 -0500482 }
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 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500499 },
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 ]
Matt Spinler03984582020-04-09 13:17:58 -0500513 }
514 ])"_json;
515
516 {
517 // Call out a symbolic FRU and a procedure
518 AdditionalData ad;
519 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500520 std::vector<std::string> names{"systemA"};
521
Matt Spinler1ab66962020-10-29 13:21:44 -0500522 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500523
524 SRC src{entry, ad, dataIface};
525
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500526 EXPECT_TRUE(
527 src.getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured));
528 EXPECT_TRUE(src.getErrorStatusFlag(SRC::ErrorStatusFlags::hwCheckstop));
529
Matt Spinler3fe93e92023-04-14 14:06:59 -0500530 const auto& hexwords = src.hexwordData();
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500531 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured) |
532 static_cast<uint32_t>(SRC::ErrorStatusFlags::hwCheckstop);
Matt Spinler3fe93e92023-04-14 14:06:59 -0500533 EXPECT_EQ(hexwords[5 - 2] & mask, mask);
534
Matt Spinler03984582020-04-09 13:17:58 -0500535 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();
Matt Spinlerea2873d2021-08-18 10:35:40 -0500552 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
Matt Spinler03984582020-04-09 13:17:58 -0500553 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;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500565 std::vector<std::string> names{"systemB"};
566
Matt Spinleraf191c72020-06-04 11:35:13 -0500567 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500568 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500569
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 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500598
599 {
600 // Two hardware callouts
601 AdditionalData ad;
602 NiceMock<MockDataInterface> dataIface;
603 std::vector<std::string> names{"systemC"};
604
Matt Spinler1ab66962020-10-29 13:21:44 -0500605 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500606
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
Matt Spinler2f9225a2020-08-05 12:58:49 -0500613 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600614 .WillOnce(Return(std::vector<std::string>{
615 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500616
Matt Spinler2f9225a2020-08-05 12:58:49 -0500617 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600618 .WillOnce(Return(std::vector<std::string>{
619 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500620
621 EXPECT_CALL(
622 dataIface,
623 getHWCalloutFields(
624 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
625 _))
626 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -0400627 .WillOnce(
628 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
629 SetArgReferee<3>("123456789ABC")));
Matt Spinleraf191c72020-06-04 11:35:13 -0500630
631 EXPECT_CALL(
632 dataIface,
633 getHWCalloutFields(
634 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
635 _))
636 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -0400637 .WillOnce(
638 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
639 SetArgReferee<3>("23456789ABCD")));
Matt Spinleraf191c72020-06-04 11:35:13 -0500640
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 }
Matt Spinler03984582020-04-09 13:17:58 -0500662}
Matt Spinler717de422020-06-04 13:10:14 -0500663
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500664// 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.
667TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
668{
669 message::Entry entry;
670 entry.src.type = 0xBD;
671 entry.src.reasonCode = 0xABCD;
672 entry.subsystem = 0x42;
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500673
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
Matt Spinler1ab66962020-10-29 13:21:44 -0500700 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500701
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
Matt Spinler1ab66962020-10-29 13:21:44 -0500753 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500754
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
Matt Spinler717de422020-06-04 13:10:14 -0500768// Test looking up device path fails in the callout jSON.
769TEST_F(SRCTest, DevicePathCalloutTest)
770{
771 message::Entry entry;
772 entry.src.type = 0xBD;
773 entry.src.reasonCode = 0xABCD;
774 entry.subsystem = 0x42;
Matt Spinler717de422020-06-04 13:10:14 -0500775
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)
Matt Spinler1ab66962020-10-29 13:21:44 -0500817 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500818
Matt Spinler2f9225a2020-08-05 12:58:49 -0500819 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500820 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600821 .WillRepeatedly(Return(std::vector<std::string>{
822 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
Matt Spinler717de422020-06-04 13:10:14 -0500823
Matt Spinler2f9225a2020-08-05 12:58:49 -0500824 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500825 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600826 .WillRepeatedly(Return(std::vector<std::string>{
827 "/xyz/openbmc_project/inventory/chassis/motherboard"}));
Matt Spinler717de422020-06-04 13:10:14 -0500828
Matt Spinler2f9225a2020-08-05 12:58:49 -0500829 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500830 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600831 .WillRepeatedly(Return(std::vector<std::string>{
832 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"}));
Matt Spinler717de422020-06-04 13:10:14 -0500833
Matt Spinler0d92b522021-06-16 13:28:17 -0600834 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500835 .Times(3)
836 .WillRepeatedly(Return("Ufcs-P1-C40"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600837
838 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500839 .Times(3)
840 .WillRepeatedly(Return("Ufcs-P1"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600841
842 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500843 .Times(3)
844 .WillRepeatedly(Return("Ufcs-P1-C15"));
845
Patrick Williams075c7922024-08-16 15:19:49 -0400846 EXPECT_CALL(dataIface,
847 getHWCalloutFields(
848 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0",
849 _, _, _))
Matt Spinler717de422020-06-04 13:10:14 -0500850 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400851 .WillRepeatedly(
852 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
853 SetArgReferee<3>("123456789ABC")));
Matt Spinler717de422020-06-04 13:10:14 -0500854 EXPECT_CALL(
855 dataIface,
856 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
857 _, _, _))
858 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400859 .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 _, _))
Matt Spinler717de422020-06-04 13:10:14 -0500866 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400867 .WillRepeatedly(
868 DoAll(SetArgReferee<1>("7123456"), SetArgReferee<2>("BBBB"),
869 SetArgReferee<3>("C123456789AB")));
Matt Spinler717de422020-06-04 13:10:14 -0500870
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}
Matt Spinler3bdd0112020-08-27 10:24:34 -0500968
969// Test when callouts are passed in via JSON
970TEST_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"
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001012 },
1013 {
1014 "Procedure": "fsi_path",
1015 "Priority": "L"
1016 },
1017 {
1018 "SymbolicFRU": "ambient_temp",
1019 "Priority": "L"
Matt Spinler3bdd0112020-08-27 10:24:34 -05001020 }
1021 ]
1022 )"_json;
1023
1024 message::Entry entry;
1025 entry.src.type = 0xBD;
1026 entry.src.reasonCode = 0xABCD;
1027 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001028
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)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001039 .WillOnce(Return(std::vector<std::string>{
1040 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001041 EXPECT_CALL(
1042 dataIface,
1043 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1044 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -04001045 .WillOnce(
1046 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1047 SetArgReferee<3>("123456789ABC")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001048 }
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)
Patrick Williams075c7922024-08-16 15:19:49 -04001058 .WillOnce(
1059 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
1060 SetArgReferee<3>("23456789ABCD")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001061 }
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
Matt Spinlerafa2c792020-08-27 11:01:39 -05001078 // Check the guarded and deconfigured flags
1079 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1080
Matt Spinler3bdd0112020-08-27 10:24:34 -05001081 const auto& callouts = src.callouts()->callouts();
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001082 ASSERT_EQ(callouts.size(), 8);
Matt Spinler3bdd0112020-08-27 10:24:34 -05001083
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);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001094
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');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001103 }
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
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001159 // 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
Matt Spinler3bdd0112020-08-27 10:24:34 -05001180 // Check that it didn't find any errors
1181 const auto& data = src.getDebugData();
1182 EXPECT_TRUE(data.empty());
1183}
1184
1185TEST_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;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001214
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)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001227 .WillOnce(Return(std::vector<std::string>{
1228 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001229 EXPECT_CALL(
1230 dataIface,
1231 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1232 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -04001233 .WillOnce(
1234 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1235 SetArgReferee<3>("123456789ABC")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001236 }
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}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001283
1284// Test that an inventory path callout can have
1285// a different priority than H.
1286TEST_F(SRCTest, InventoryCalloutTestPriority)
1287{
1288 message::Entry entry;
1289 entry.src.type = 0xBD;
1290 entry.src.reasonCode = 0xABCD;
1291 entry.subsystem = 0x42;
Miguel Gomez53ef1552020-10-14 21:16:32 +00001292
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');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001317}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001318
1319// Test for bmc & platform dump status bits
1320TEST_F(SRCTest, DumpStatusBitsCheck)
1321{
1322 message::Entry entry;
1323 entry.src.type = 0xBD;
1324 entry.src.reasonCode = 0xABCD;
1325 entry.subsystem = 0x42;
Sumit Kumar9d43a722021-08-24 09:46:19 -05001326
1327 AdditionalData ad;
1328 NiceMock<MockDataInterface> dataIface;
1329 std::vector<std::string> dumpType{"bmc/entry", "resource/entry",
1330 "system/entry"};
1331
1332 {
1333 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1334 .WillOnce(Return(std::vector<bool>{true, false, false}));
1335
1336 SRC src{entry, ad, dataIface};
1337 EXPECT_TRUE(src.valid());
1338
1339 const auto& hexwords = src.hexwordData();
1340 EXPECT_EQ(0x00080055, hexwords[0]);
1341 }
1342
1343 {
1344 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1345 .WillOnce(Return(std::vector<bool>{false, true, false}));
1346
1347 SRC src{entry, ad, dataIface};
1348 EXPECT_TRUE(src.valid());
1349
1350 const auto& hexwords = src.hexwordData();
1351 EXPECT_EQ(0x00000255, hexwords[0]);
1352 }
1353
1354 {
1355 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1356 .WillOnce(Return(std::vector<bool>{false, false, true}));
1357
1358 SRC src{entry, ad, dataIface};
1359 EXPECT_TRUE(src.valid());
1360
1361 const auto& hexwords = src.hexwordData();
1362 EXPECT_EQ(0x00000455, hexwords[0]);
1363 }
1364
1365 {
1366 EXPECT_CALL(dataIface, checkDumpStatus(dumpType))
1367 .WillOnce(Return(std::vector<bool>{true, true, true}));
1368
1369 SRC src{entry, ad, dataIface};
1370 EXPECT_TRUE(src.valid());
1371
1372 const auto& hexwords = src.hexwordData();
1373 EXPECT_EQ(0x00080655, hexwords[0]);
1374 }
1375}
Sumit Kumar50bfa692022-01-06 06:48:26 -06001376
1377// Test SRC with additional data - PEL_SUBSYSTEM
1378TEST_F(SRCTest, TestPELSubsystem)
1379{
1380 message::Entry entry;
1381 entry.src.type = 0xBD;
1382 entry.src.reasonCode = 0xABCD;
1383 entry.subsystem = 0x42;
Sumit Kumar50bfa692022-01-06 06:48:26 -06001384
1385 // Values for the SRC words pointed to above
1386 std::vector<std::string> adData{"PEL_SUBSYSTEM=0x20"};
1387 AdditionalData ad{adData};
1388 NiceMock<MockDataInterface> dataIface;
1389
1390 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1391
Sumit Kumar50bfa692022-01-06 06:48:26 -06001392 SRC src{entry, ad, dataIface};
1393
1394 EXPECT_TRUE(src.valid());
1395
1396 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1397}
Vijay Lobo875b6c72021-10-20 17:38:56 -05001398
1399void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1400{
1401 assert(40 + value.size() <= src.size());
1402
1403 for (size_t i = 0; i < value.size(); i++)
1404 {
1405 src[40 + i] = value[i];
1406 }
1407}
1408
1409TEST_F(SRCTest, TestGetProgressCode)
1410{
1411 {
1412 // A real SRC with CC009184
1413 std::vector<uint8_t> src{
1414 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1415 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1416 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1417 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1418 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1419
1420 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1421 }
1422
1423 {
1424 // A real SRC with STANDBY
1425 std::vector<uint8_t> src{
1426 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1427 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1428 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1429 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1430 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1431
1432 EXPECT_EQ(SRC::getProgressCode(src), 0);
1433 }
1434
1435 {
1436 // A real SRC with CC009184, but 1 byte too short
1437 std::vector<uint8_t> src{
1438 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1439 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1440 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1441 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1442 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1443 src.resize(71);
1444 EXPECT_EQ(SRC::getProgressCode(src), 0);
1445 }
1446
1447 {
1448 // A few different ones
1449 const std::map<std::string, uint32_t> progressCodes{
1450 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1451 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1452 {"1234567X", 0}, {"1 ", 0}};
1453
1454 std::vector<uint8_t> src(72, 0x0);
1455
1456 for (const auto& [code, expected] : progressCodes)
1457 {
1458 setAsciiString(src, code);
1459 EXPECT_EQ(SRC::getProgressCode(src), expected);
1460 }
1461
1462 // empty
1463 src.clear();
1464 EXPECT_EQ(SRC::getProgressCode(src), 0);
1465 }
1466}
1467
1468// Test progress is in right SRC hex data field
1469TEST_F(SRCTest, TestProgressCodeField)
1470{
1471 message::Entry entry;
1472 entry.src.type = 0xBD;
1473 entry.src.reasonCode = 0xABCD;
1474 entry.subsystem = 0x42;
1475
1476 AdditionalData ad;
1477 NiceMock<MockDataInterface> dataIface;
Vijay Lobo875b6c72021-10-20 17:38:56 -05001478 EXPECT_CALL(dataIface, getRawProgressSRC())
1479 .WillOnce(Return(std::vector<uint8_t>{
1480 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1481 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1482 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1483 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1484 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1485
1486 SRC src{entry, ad, dataIface};
1487 EXPECT_TRUE(src.valid());
1488
1489 // Verify that the hex vlue is set at the right hexword
1490 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1491}