blob: 2c49a7b7a86655527f27f107325d05854ff07049 [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
Patrick Williamse5940632024-11-22 20:47:58 -0500201 std::map<std::string, std::string> adData{
202 {"TEST1", "0x12345678"},
203 {"TEST2", "12345678"},
204 {"TEST3", "0XDEF"},
205 {"TEST4", "Z"}};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500206 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600207 NiceMock<MockDataInterface> dataIface;
208
209 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
210
211 SRC src{entry, ad, dataIface};
Matt Spinlerbd716f02019-10-15 10:54:11 -0500212
213 EXPECT_TRUE(src.valid());
Mike Cappsa2d7b772022-03-07 15:47:48 -0500214 EXPECT_FALSE(src.isPowerFaultEvent());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500215 EXPECT_EQ(src.size(), baseSRCSize);
216
217 const auto& hexwords = src.hexwordData();
218
219 // The spec always refers to SRC words 2 - 9, and as the hexwordData()
220 // array index starts at 0 use the math in the [] below to make it easier
221 // to tell what is being accessed.
222 EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0); // Partition dump status
223 EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0); // Partition boot type
224 EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
225 EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
Matt Spinler075e5ba2020-02-21 15:46:00 -0600226 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
Matt Spinlerbd716f02019-10-15 10:54:11 -0500227
228 // Validate more fields here as the code starts filling them in.
229
230 // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
Matt Spinler3fe93e92023-04-14 14:06:59 -0500231 // And that none of the error status flags are set
Matt Spinlerbd716f02019-10-15 10:54:11 -0500232 EXPECT_EQ(hexwords[5 - 2], 0);
233
234 // The user defined hex word fields specifed in the additional data.
235 EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
236 EXPECT_EQ(hexwords[7 - 2], 12345678); // TEST2
237 EXPECT_EQ(hexwords[8 - 2], 0xdef); // TEST3
238 EXPECT_EQ(hexwords[9 - 2], 0); // TEST4, but can't convert a 'Z'
239
240 EXPECT_EQ(src.asciiString(), "BD42ABCD ");
241
242 // No callouts
243 EXPECT_FALSE(src.callouts());
244
245 // May as well spot check the flatten/unflatten
246 std::vector<uint8_t> data;
247 Stream stream{data};
248 src.flatten(stream);
249
250 stream.offset(0);
251 SRC newSRC{stream};
252
253 EXPECT_TRUE(newSRC.valid());
Matt Spinlerbd716f02019-10-15 10:54:11 -0500254 EXPECT_EQ(newSRC.asciiString(), src.asciiString());
255 EXPECT_FALSE(newSRC.callouts());
256}
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800257
Matt Spinler075e5ba2020-02-21 15:46:00 -0600258// Test when the CCIN string isn't a 4 character number
259TEST_F(SRCTest, BadCCINTest)
260{
261 message::Entry entry;
262 entry.src.type = 0xBD;
263 entry.src.reasonCode = 0xABCD;
264 entry.subsystem = 0x42;
Matt Spinler075e5ba2020-02-21 15:46:00 -0600265
Patrick Williamse5940632024-11-22 20:47:58 -0500266 std::map<std::string, std::string> adData{};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600267 AdditionalData ad{adData};
268 NiceMock<MockDataInterface> dataIface;
269
270 // First it isn't a number, then it is too long,
271 // then it is empty.
272 EXPECT_CALL(dataIface, getMotherboardCCIN)
273 .WillOnce(Return("X"))
274 .WillOnce(Return("12345"))
275 .WillOnce(Return(""));
276
277 // The CCIN in the first half should still be 0 each time.
278 {
279 SRC src{entry, ad, dataIface};
280 EXPECT_TRUE(src.valid());
281 const auto& hexwords = src.hexwordData();
282 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
283 }
284
285 {
286 SRC src{entry, ad, dataIface};
287 EXPECT_TRUE(src.valid());
288 const auto& hexwords = src.hexwordData();
289 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
290 }
291
292 {
293 SRC src{entry, ad, dataIface};
294 EXPECT_TRUE(src.valid());
295 const auto& hexwords = src.hexwordData();
296 EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
297 }
298}
299
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800300// Test the getErrorDetails function
301TEST_F(SRCTest, MessageSubstitutionTest)
302{
303 auto path = SRCTest::writeData(testRegistry);
304 message::Registry registry{path};
305 auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
306
Patrick Williamse5940632024-11-22 20:47:58 -0500307 std::map<std::string, std::string> adData{
308 {"COMPID", "0x1"},
309 {"FREQUENCY", "0x4"},
310 {"DURATION", "30"},
311 {"ERRORCODE", "0x01ABCDEF"}};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800312 AdditionalData ad{adData};
Matt Spinler075e5ba2020-02-21 15:46:00 -0600313 NiceMock<MockDataInterface> dataIface;
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800314
Matt Spinler075e5ba2020-02-21 15:46:00 -0600315 SRC src{*entry, ad, dataIface};
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800316 EXPECT_TRUE(src.valid());
317
318 auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
319 ASSERT_TRUE(errorDetails);
Zane Shelley39936e32021-11-13 16:19:34 -0600320 EXPECT_EQ(errorDetails.value(),
321 "Comp 0x00000001 failed 0x00000004 times over 0x0000001E secs "
322 "with ErrorCode 0x01ABCDEF");
Harisuddin Mohamed Isa6fd0c1e2020-02-06 14:25:57 +0800323}
Matt Spinlered046852020-03-13 13:58:15 -0500324// Test that an inventory path callout string is
325// converted into the appropriate FRU callout.
326TEST_F(SRCTest, InventoryCalloutTest)
327{
328 message::Entry entry;
329 entry.src.type = 0xBD;
330 entry.src.reasonCode = 0xABCD;
331 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500332
Patrick Williamse5940632024-11-22 20:47:58 -0500333 std::map<std::string, std::string> adData{
334 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
Matt Spinlered046852020-03-13 13:58:15 -0500335 AdditionalData ad{adData};
336 NiceMock<MockDataInterface> dataIface;
337
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500338 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
339 .WillOnce(Return("UTMS-P1"));
340
341 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500342 .Times(1)
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500343 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
344 SetArgReferee<3>("123456789ABC")));
Matt Spinlered046852020-03-13 13:58:15 -0500345
346 SRC src{entry, ad, dataIface};
347 EXPECT_TRUE(src.valid());
348
349 ASSERT_TRUE(src.callouts());
350
351 EXPECT_EQ(src.callouts()->callouts().size(), 1);
352
353 auto& callout = src.callouts()->callouts().front();
354
355 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
Matt Spinler717de422020-06-04 13:10:14 -0500356 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500357
358 auto& fru = callout->fruIdentity();
359
360 EXPECT_EQ(fru->getPN().value(), "1234567");
361 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
362 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
363
364 // flatten and unflatten
365 std::vector<uint8_t> data;
366 Stream stream{data};
367 src.flatten(stream);
368
369 stream.offset(0);
370 SRC newSRC{stream};
371 EXPECT_TRUE(newSRC.valid());
372 ASSERT_TRUE(src.callouts());
373 EXPECT_EQ(src.callouts()->callouts().size(), 1);
374}
375
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500376// Test that when the location code can't be obtained that
Matt Spinler479b6922021-08-17 16:34:59 -0500377// no callout is added.
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500378TEST_F(SRCTest, InventoryCalloutNoLocCodeTest)
379{
380 message::Entry entry;
381 entry.src.type = 0xBD;
382 entry.src.reasonCode = 0xABCD;
383 entry.subsystem = 0x42;
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500384
Patrick Williamse5940632024-11-22 20:47:58 -0500385 std::map<std::string, std::string> adData{
386 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500387 AdditionalData ad{adData};
388 NiceMock<MockDataInterface> dataIface;
389
390 auto func = []() {
391 throw sdbusplus::exception::SdBusError(5, "Error");
392 return std::string{};
393 };
394
395 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
396 .Times(1)
397 .WillOnce(InvokeWithoutArgs(func));
398
399 EXPECT_CALL(dataIface, getHWCalloutFields(_, _, _, _)).Times(0);
400
401 SRC src{entry, ad, dataIface};
402 EXPECT_TRUE(src.valid());
403
Matt Spinler479b6922021-08-17 16:34:59 -0500404 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500405
406 // flatten and unflatten
407 std::vector<uint8_t> data;
408 Stream stream{data};
409 src.flatten(stream);
410
411 stream.offset(0);
412 SRC newSRC{stream};
413 EXPECT_TRUE(newSRC.valid());
Matt Spinler479b6922021-08-17 16:34:59 -0500414 ASSERT_FALSE(src.callouts());
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500415}
416
417// Test that when the VPD can't be obtained that
418// a callout is still created.
Matt Spinlered046852020-03-13 13:58:15 -0500419TEST_F(SRCTest, InventoryCalloutNoVPDTest)
420{
421 message::Entry entry;
422 entry.src.type = 0xBD;
423 entry.src.reasonCode = 0xABCD;
424 entry.subsystem = 0x42;
Matt Spinlered046852020-03-13 13:58:15 -0500425
Patrick Williamse5940632024-11-22 20:47:58 -0500426 std::map<std::string, std::string> adData{
427 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
Matt Spinlered046852020-03-13 13:58:15 -0500428 AdditionalData ad{adData};
429 NiceMock<MockDataInterface> dataIface;
430
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500431 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
432 .Times(1)
433 .WillOnce(Return("UTMS-P10"));
434
Matt Spinlered046852020-03-13 13:58:15 -0500435 auto func = []() { throw sdbusplus::exception::SdBusError(5, "Error"); };
436
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500437 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
Matt Spinlered046852020-03-13 13:58:15 -0500438 .Times(1)
439 .WillOnce(InvokeWithoutArgs(func));
440
441 SRC src{entry, ad, dataIface};
442 EXPECT_TRUE(src.valid());
Matt Spinlered046852020-03-13 13:58:15 -0500443 ASSERT_TRUE(src.callouts());
Matt Spinlered046852020-03-13 13:58:15 -0500444 EXPECT_EQ(src.callouts()->callouts().size(), 1);
445
446 auto& callout = src.callouts()->callouts().front();
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500447 EXPECT_EQ(callout->locationCode(), "UTMS-P10");
Matt Spinler717de422020-06-04 13:10:14 -0500448 EXPECT_EQ(callout->priority(), 'H');
Matt Spinlered046852020-03-13 13:58:15 -0500449
450 auto& fru = callout->fruIdentity();
451
Matt Spinler9b90e2a2020-04-14 10:59:04 -0500452 EXPECT_EQ(fru->getPN(), "");
453 EXPECT_EQ(fru->getCCIN(), "");
454 EXPECT_EQ(fru->getSN(), "");
455 EXPECT_FALSE(fru->getMaintProc());
456
Matt Spinlered046852020-03-13 13:58:15 -0500457 // flatten and unflatten
458 std::vector<uint8_t> data;
459 Stream stream{data};
460 src.flatten(stream);
461
462 stream.offset(0);
463 SRC newSRC{stream};
464 EXPECT_TRUE(newSRC.valid());
465 ASSERT_TRUE(src.callouts());
466 EXPECT_EQ(src.callouts()->callouts().size(), 1);
467}
Matt Spinler03984582020-04-09 13:17:58 -0500468
469TEST_F(SRCTest, RegistryCalloutTest)
470{
471 message::Entry entry;
472 entry.src.type = 0xBD;
473 entry.src.reasonCode = 0xABCD;
Matt Spinler3fe93e92023-04-14 14:06:59 -0500474 entry.src.deconfigFlag = true;
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500475 entry.src.checkstopFlag = true;
Matt Spinler03984582020-04-09 13:17:58 -0500476 entry.subsystem = 0x42;
Matt Spinler03984582020-04-09 13:17:58 -0500477
478 entry.callouts = R"(
479 [
480 {
481 "System": "systemA",
482 "CalloutList":
483 [
484 {
485 "Priority": "high",
486 "SymbolicFRU": "service_docs"
487 },
488 {
489 "Priority": "medium",
Matt Spinler2edce4e2024-01-17 11:13:51 -0600490 "Procedure": "BMC0001"
Matt Spinler03984582020-04-09 13:17:58 -0500491 }
492 ]
493 },
494 {
495 "System": "systemB",
496 "CalloutList":
497 [
498 {
499 "Priority": "high",
500 "LocCode": "P0-C8",
501 "SymbolicFRUTrusted": "service_docs"
502 },
503 {
504 "Priority": "medium",
505 "SymbolicFRUTrusted": "service_docs"
506 }
507 ]
Matt Spinleraf191c72020-06-04 11:35:13 -0500508 },
509 {
510 "System": "systemC",
511 "CalloutList":
512 [
513 {
514 "Priority": "high",
515 "LocCode": "P0-C8"
516 },
517 {
518 "Priority": "medium",
519 "LocCode": "P0-C9"
520 }
521 ]
Matt Spinler03984582020-04-09 13:17:58 -0500522 }
523 ])"_json;
524
525 {
526 // Call out a symbolic FRU and a procedure
527 AdditionalData ad;
528 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500529 std::vector<std::string> names{"systemA"};
530
Matt Spinler1ab66962020-10-29 13:21:44 -0500531 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500532
533 SRC src{entry, ad, dataIface};
534
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500535 EXPECT_TRUE(
536 src.getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured));
537 EXPECT_TRUE(src.getErrorStatusFlag(SRC::ErrorStatusFlags::hwCheckstop));
538
Matt Spinler3fe93e92023-04-14 14:06:59 -0500539 const auto& hexwords = src.hexwordData();
Matt Spinlerda5b76b2023-06-01 15:56:57 -0500540 auto mask = static_cast<uint32_t>(SRC::ErrorStatusFlags::deconfigured) |
541 static_cast<uint32_t>(SRC::ErrorStatusFlags::hwCheckstop);
Matt Spinler3fe93e92023-04-14 14:06:59 -0500542 EXPECT_EQ(hexwords[5 - 2] & mask, mask);
543
Matt Spinler03984582020-04-09 13:17:58 -0500544 auto& callouts = src.callouts()->callouts();
545 ASSERT_EQ(callouts.size(), 2);
546
547 EXPECT_EQ(callouts[0]->locationCodeSize(), 0);
548 EXPECT_EQ(callouts[0]->priority(), 'H');
549
550 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
551 EXPECT_EQ(callouts[1]->priority(), 'M');
552
553 auto& fru1 = callouts[0]->fruIdentity();
554 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
555 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
556 EXPECT_FALSE(fru1->getMaintProc());
557 EXPECT_FALSE(fru1->getSN());
558 EXPECT_FALSE(fru1->getCCIN());
559
560 auto& fru2 = callouts[1]->fruIdentity();
Matt Spinlerea2873d2021-08-18 10:35:40 -0500561 EXPECT_EQ(fru2->getMaintProc().value(), "BMC0001");
Matt Spinler03984582020-04-09 13:17:58 -0500562 EXPECT_EQ(fru2->failingComponentType(),
563 src::FRUIdentity::maintenanceProc);
564 EXPECT_FALSE(fru2->getPN());
565 EXPECT_FALSE(fru2->getSN());
566 EXPECT_FALSE(fru2->getCCIN());
567 }
568
569 {
570 // Call out a trusted symbolic FRU with a location code, and
571 // another one without.
572 AdditionalData ad;
573 NiceMock<MockDataInterface> dataIface;
Matt Spinler6ea4d5f2020-05-20 13:31:07 -0500574 std::vector<std::string> names{"systemB"};
575
Matt Spinleraf191c72020-06-04 11:35:13 -0500576 EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
Matt Spinler1ab66962020-10-29 13:21:44 -0500577 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinler03984582020-04-09 13:17:58 -0500578
579 SRC src{entry, ad, dataIface};
580
581 auto& callouts = src.callouts()->callouts();
582 EXPECT_EQ(callouts.size(), 2);
583
584 EXPECT_EQ(callouts[0]->locationCode(), "P0-C8");
585 EXPECT_EQ(callouts[0]->priority(), 'H');
586
587 EXPECT_EQ(callouts[1]->locationCodeSize(), 0);
588 EXPECT_EQ(callouts[1]->priority(), 'M');
589
590 auto& fru1 = callouts[0]->fruIdentity();
591 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
592 EXPECT_EQ(fru1->failingComponentType(),
593 src::FRUIdentity::symbolicFRUTrustedLocCode);
594 EXPECT_FALSE(fru1->getMaintProc());
595 EXPECT_FALSE(fru1->getSN());
596 EXPECT_FALSE(fru1->getCCIN());
597
598 // It asked for a trusted symbolic FRU, but no location code
599 // was provided so it is switched back to a normal one
600 auto& fru2 = callouts[1]->fruIdentity();
601 EXPECT_EQ(fru2->getPN().value(), "SVCDOCS");
602 EXPECT_EQ(fru2->failingComponentType(), src::FRUIdentity::symbolicFRU);
603 EXPECT_FALSE(fru2->getMaintProc());
604 EXPECT_FALSE(fru2->getSN());
605 EXPECT_FALSE(fru2->getCCIN());
606 }
Matt Spinleraf191c72020-06-04 11:35:13 -0500607
608 {
609 // Two hardware callouts
610 AdditionalData ad;
611 NiceMock<MockDataInterface> dataIface;
612 std::vector<std::string> names{"systemC"};
613
Matt Spinler1ab66962020-10-29 13:21:44 -0500614 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinleraf191c72020-06-04 11:35:13 -0500615
616 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
617 .WillOnce(Return("UXXX-P0-C8"));
618
619 EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
620 .WillOnce(Return("UXXX-P0-C9"));
621
Matt Spinler2f9225a2020-08-05 12:58:49 -0500622 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600623 .WillOnce(Return(std::vector<std::string>{
624 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500625
Matt Spinler2f9225a2020-08-05 12:58:49 -0500626 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0, false))
Matt Spinlerbad056b2023-01-25 14:16:57 -0600627 .WillOnce(Return(std::vector<std::string>{
628 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"}));
Matt Spinleraf191c72020-06-04 11:35:13 -0500629
630 EXPECT_CALL(
631 dataIface,
632 getHWCalloutFields(
633 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
634 _))
635 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -0400636 .WillOnce(
637 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
638 SetArgReferee<3>("123456789ABC")));
Matt Spinleraf191c72020-06-04 11:35:13 -0500639
640 EXPECT_CALL(
641 dataIface,
642 getHWCalloutFields(
643 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
644 _))
645 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -0400646 .WillOnce(
647 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
648 SetArgReferee<3>("23456789ABCD")));
Matt Spinleraf191c72020-06-04 11:35:13 -0500649
650 SRC src{entry, ad, dataIface};
651
652 auto& callouts = src.callouts()->callouts();
653 EXPECT_EQ(callouts.size(), 2);
654
655 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
656 EXPECT_EQ(callouts[0]->priority(), 'H');
657
658 auto& fru1 = callouts[0]->fruIdentity();
659 EXPECT_EQ(fru1->getPN().value(), "1234567");
660 EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
661 EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
662
663 EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
664 EXPECT_EQ(callouts[1]->priority(), 'M');
665
666 auto& fru2 = callouts[1]->fruIdentity();
667 EXPECT_EQ(fru2->getPN().value(), "2345678");
668 EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
669 EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
670 }
Matt Spinler03984582020-04-09 13:17:58 -0500671}
Matt Spinler717de422020-06-04 13:10:14 -0500672
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500673// Test that a symbolic FRU with a trusted location code callout
674// from the registry can get its location from the
675// CALLOUT_INVENTORY_PATH AdditionalData entry.
676TEST_F(SRCTest, SymbolicFRUWithInvPathTest)
677{
678 message::Entry entry;
679 entry.src.type = 0xBD;
680 entry.src.reasonCode = 0xABCD;
681 entry.subsystem = 0x42;
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500682
683 entry.callouts = R"(
684 [{
685 "CalloutList":
686 [
687 {
688 "Priority": "high",
689 "SymbolicFRUTrusted": "service_docs",
690 "UseInventoryLocCode": true
691 },
692 {
693 "Priority": "medium",
694 "LocCode": "P0-C8",
695 "SymbolicFRUTrusted": "pwrsply"
696 }
697 ]
698 }])"_json;
699
700 {
701 // The location code for the first symbolic FRU callout will
702 // come from this inventory path since UseInventoryLocCode is set.
703 // In this case there will be no normal FRU callout for the motherboard.
Patrick Williamse5940632024-11-22 20:47:58 -0500704 std::map<std::string, std::string> adData{
705 {"CALLOUT_INVENTORY_PATH", "motherboard"}};
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500706 AdditionalData ad{adData};
707 NiceMock<MockDataInterface> dataIface;
708 std::vector<std::string> names{"systemA"};
709
Matt Spinler1ab66962020-10-29 13:21:44 -0500710 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500711
712 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
713 .Times(1)
714 .WillOnce(Return("Ufcs-P10"));
715
716 EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
717 .WillOnce(Return("Ufcs-P0-C8"));
718
719 SRC src{entry, ad, dataIface};
720
721 auto& callouts = src.callouts()->callouts();
722 EXPECT_EQ(callouts.size(), 2);
723
724 // The location code for the first symbolic FRU callout with a
725 // trusted location code comes from the motherboard.
726 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P10");
727 EXPECT_EQ(callouts[0]->priority(), 'H');
728 auto& fru1 = callouts[0]->fruIdentity();
729 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
730 EXPECT_EQ(fru1->failingComponentType(),
731 src::FRUIdentity::symbolicFRUTrustedLocCode);
732
733 // The second trusted symbolic FRU callouts uses the location
734 // code in the registry as usual.
735 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P0-C8");
736 EXPECT_EQ(callouts[1]->priority(), 'M');
737 auto& fru2 = callouts[1]->fruIdentity();
738 EXPECT_EQ(fru2->getPN().value(), "PWRSPLY");
739 EXPECT_EQ(fru2->failingComponentType(),
740 src::FRUIdentity::symbolicFRUTrustedLocCode);
741 }
742
743 {
744 // This time say we want to use the location code from
745 // the inventory, but don't pass it in and the callout should
746 // end up a regular symbolic FRU
747 entry.callouts = R"(
748 [{
749 "CalloutList":
750 [
751 {
752 "Priority": "high",
753 "SymbolicFRUTrusted": "service_docs",
754 "UseInventoryLocCode": true
755 }
756 ]
757 }])"_json;
758
759 AdditionalData ad;
760 NiceMock<MockDataInterface> dataIface;
761 std::vector<std::string> names{"systemA"};
762
Matt Spinler1ab66962020-10-29 13:21:44 -0500763 EXPECT_CALL(dataIface, getSystemNames).WillOnce(Return(names));
Matt Spinlerf00f9d02020-10-23 09:14:22 -0500764
765 SRC src{entry, ad, dataIface};
766
767 auto& callouts = src.callouts()->callouts();
768 EXPECT_EQ(callouts.size(), 1);
769
770 EXPECT_EQ(callouts[0]->locationCode(), "");
771 EXPECT_EQ(callouts[0]->priority(), 'H');
772 auto& fru1 = callouts[0]->fruIdentity();
773 EXPECT_EQ(fru1->getPN().value(), "SVCDOCS");
774 EXPECT_EQ(fru1->failingComponentType(), src::FRUIdentity::symbolicFRU);
775 }
776}
777
Matt Spinler717de422020-06-04 13:10:14 -0500778// Test looking up device path fails in the callout jSON.
779TEST_F(SRCTest, DevicePathCalloutTest)
780{
781 message::Entry entry;
782 entry.src.type = 0xBD;
783 entry.src.reasonCode = 0xABCD;
784 entry.subsystem = 0x42;
Matt Spinler717de422020-06-04 13:10:14 -0500785
786 const auto calloutJSON = R"(
787 {
788 "I2C":
789 {
790 "14":
791 {
792 "114":
793 {
794 "Callouts":[
795 {
796 "Name": "/chassis/motherboard/cpu0",
797 "LocationCode": "P1-C40",
798 "Priority": "H"
799 },
800 {
801 "Name": "/chassis/motherboard",
802 "LocationCode": "P1",
803 "Priority": "M"
804 },
805 {
806 "Name": "/chassis/motherboard/bmc",
807 "LocationCode": "P1-C15",
808 "Priority": "L"
809 }
810 ],
811 "Dest": "proc 0 target"
812 }
813 }
814 }
815 })";
816
817 auto dataPath = getPELReadOnlyDataPath();
818 std::ofstream file{dataPath / "systemA_dev_callouts.json"};
819 file << calloutJSON;
820 file.close();
821
822 NiceMock<MockDataInterface> dataIface;
823 std::vector<std::string> names{"systemA"};
824
825 EXPECT_CALL(dataIface, getSystemNames)
826 .Times(5)
Matt Spinler1ab66962020-10-29 13:21:44 -0500827 .WillRepeatedly(Return(names));
Matt Spinler717de422020-06-04 13:10:14 -0500828
Matt Spinler2f9225a2020-08-05 12:58:49 -0500829 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C40", 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/cpu0"}));
Matt Spinler717de422020-06-04 13:10:14 -0500833
Matt Spinler2f9225a2020-08-05 12:58:49 -0500834 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500835 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600836 .WillRepeatedly(Return(std::vector<std::string>{
837 "/xyz/openbmc_project/inventory/chassis/motherboard"}));
Matt Spinler717de422020-06-04 13:10:14 -0500838
Matt Spinler2f9225a2020-08-05 12:58:49 -0500839 EXPECT_CALL(dataIface, getInventoryFromLocCode("P1-C15", 0, false))
Matt Spinler717de422020-06-04 13:10:14 -0500840 .Times(3)
Matt Spinlerbad056b2023-01-25 14:16:57 -0600841 .WillRepeatedly(Return(std::vector<std::string>{
842 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc"}));
Matt Spinler717de422020-06-04 13:10:14 -0500843
Matt Spinler0d92b522021-06-16 13:28:17 -0600844 EXPECT_CALL(dataIface, expandLocationCode("P1-C40", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500845 .Times(3)
846 .WillRepeatedly(Return("Ufcs-P1-C40"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600847
848 EXPECT_CALL(dataIface, expandLocationCode("P1", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500849 .Times(3)
850 .WillRepeatedly(Return("Ufcs-P1"));
Matt Spinler0d92b522021-06-16 13:28:17 -0600851
852 EXPECT_CALL(dataIface, expandLocationCode("P1-C15", 0))
Matt Spinler717de422020-06-04 13:10:14 -0500853 .Times(3)
854 .WillRepeatedly(Return("Ufcs-P1-C15"));
855
Patrick Williams075c7922024-08-16 15:19:49 -0400856 EXPECT_CALL(dataIface,
857 getHWCalloutFields(
858 "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0",
859 _, _, _))
Matt Spinler717de422020-06-04 13:10:14 -0500860 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400861 .WillRepeatedly(
862 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
863 SetArgReferee<3>("123456789ABC")));
Matt Spinler717de422020-06-04 13:10:14 -0500864 EXPECT_CALL(
865 dataIface,
866 getHWCalloutFields("/xyz/openbmc_project/inventory/chassis/motherboard",
867 _, _, _))
868 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400869 .WillRepeatedly(
870 DoAll(SetArgReferee<1>("7654321"), SetArgReferee<2>("MMMM"),
871 SetArgReferee<3>("CBA987654321")));
872 EXPECT_CALL(dataIface,
873 getHWCalloutFields(
874 "/xyz/openbmc_project/inventory/chassis/motherboard/bmc", _,
875 _, _))
Matt Spinler717de422020-06-04 13:10:14 -0500876 .Times(3)
Patrick Williams075c7922024-08-16 15:19:49 -0400877 .WillRepeatedly(
878 DoAll(SetArgReferee<1>("7123456"), SetArgReferee<2>("BBBB"),
879 SetArgReferee<3>("C123456789AB")));
Matt Spinler717de422020-06-04 13:10:14 -0500880
881 // Call this below with different AdditionalData values that
882 // result in the same callouts.
883 auto checkCallouts = [&entry, &dataIface](const auto& items) {
884 AdditionalData ad{items};
885 SRC src{entry, ad, dataIface};
886
887 ASSERT_TRUE(src.callouts());
888 auto& callouts = src.callouts()->callouts();
889
890 ASSERT_EQ(callouts.size(), 3);
891
892 {
893 EXPECT_EQ(callouts[0]->priority(), 'H');
894 EXPECT_EQ(callouts[0]->locationCode(), "Ufcs-P1-C40");
895
896 auto& fru = callouts[0]->fruIdentity();
897 EXPECT_EQ(fru->getPN().value(), "1234567");
898 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
899 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
900 }
901 {
902 EXPECT_EQ(callouts[1]->priority(), 'M');
903 EXPECT_EQ(callouts[1]->locationCode(), "Ufcs-P1");
904
905 auto& fru = callouts[1]->fruIdentity();
906 EXPECT_EQ(fru->getPN().value(), "7654321");
907 EXPECT_EQ(fru->getCCIN().value(), "MMMM");
908 EXPECT_EQ(fru->getSN().value(), "CBA987654321");
909 }
910 {
911 EXPECT_EQ(callouts[2]->priority(), 'L');
912 EXPECT_EQ(callouts[2]->locationCode(), "Ufcs-P1-C15");
913
914 auto& fru = callouts[2]->fruIdentity();
915 EXPECT_EQ(fru->getPN().value(), "7123456");
916 EXPECT_EQ(fru->getCCIN().value(), "BBBB");
917 EXPECT_EQ(fru->getSN().value(), "C123456789AB");
918 }
919 };
920
921 {
922 // Callouts based on the device path
Patrick Williamse5940632024-11-22 20:47:58 -0500923 std::map<std::string, std::string> items{
924 {"CALLOUT_ERRNO", "5"},
925 {"CALLOUT_DEVICE_PATH",
926 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-14/14-0072"}};
Matt Spinler717de422020-06-04 13:10:14 -0500927
928 checkCallouts(items);
929 }
930
931 {
932 // Callouts based on the I2C bus and address
Patrick Williamse5940632024-11-22 20:47:58 -0500933 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
934 {"CALLOUT_IIC_BUS", "14"},
935 {"CALLOUT_IIC_ADDR", "0x72"}};
Matt Spinler717de422020-06-04 13:10:14 -0500936 checkCallouts(items);
937 }
938
939 {
940 // Also based on I2C bus and address, but with bus = /dev/i2c-14
Patrick Williamse5940632024-11-22 20:47:58 -0500941 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
942 {"CALLOUT_IIC_BUS", "14"},
943 {"CALLOUT_IIC_ADDR", "0x72"}};
Matt Spinler717de422020-06-04 13:10:14 -0500944 checkCallouts(items);
945 }
946
947 {
948 // Callout not found
Patrick Williamse5940632024-11-22 20:47:58 -0500949 std::map<std::string, std::string> items{
950 {"CALLOUT_ERRNO", "5"},
951 {"CALLOUT_DEVICE_PATH",
952 "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a340.i2c-bus/i2c-24/24-0012"}};
Matt Spinler717de422020-06-04 13:10:14 -0500953
954 AdditionalData ad{items};
955 SRC src{entry, ad, dataIface};
956
957 EXPECT_FALSE(src.callouts());
958 ASSERT_EQ(src.getDebugData().size(), 1);
959 EXPECT_EQ(src.getDebugData()[0],
960 "Problem looking up I2C callouts on 24 18: "
961 "[json.exception.out_of_range.403] key '24' not found");
962 }
963
964 {
965 // Callout not found
Patrick Williamse5940632024-11-22 20:47:58 -0500966 std::map<std::string, std::string> items{{"CALLOUT_ERRNO", "5"},
967 {"CALLOUT_IIC_BUS", "22"},
968 {"CALLOUT_IIC_ADDR", "0x99"}};
Matt Spinler717de422020-06-04 13:10:14 -0500969 AdditionalData ad{items};
970 SRC src{entry, ad, dataIface};
971
972 EXPECT_FALSE(src.callouts());
973 ASSERT_EQ(src.getDebugData().size(), 1);
974 EXPECT_EQ(src.getDebugData()[0],
975 "Problem looking up I2C callouts on 22 153: "
976 "[json.exception.out_of_range.403] key '22' not found");
977 }
978
979 fs::remove_all(dataPath);
980}
Matt Spinler3bdd0112020-08-27 10:24:34 -0500981
982// Test when callouts are passed in via JSON
983TEST_F(SRCTest, JsonCalloutsTest)
984{
985 const auto jsonCallouts = R"(
986 [
987 {
988 "LocationCode": "P0-C1",
989 "Priority": "H",
990 "MRUs": [
991 {
992 "ID": 42,
993 "Priority": "H"
994 },
995 {
996 "ID": 43,
997 "Priority": "M"
998 }
999 ]
1000 },
1001 {
1002 "InventoryPath": "/inv/system/chassis/motherboard/cpu0",
1003 "Priority": "M",
1004 "Guarded": true,
1005 "Deconfigured": true
1006 },
1007 {
1008 "Procedure": "PROCEDU",
1009 "Priority": "A"
1010 },
1011 {
1012 "SymbolicFRU": "TRUSTED",
1013 "Priority": "B",
1014 "TrustedLocationCode": true,
1015 "LocationCode": "P1-C23"
1016 },
1017 {
1018 "SymbolicFRU": "FRUTST1",
1019 "Priority": "C",
1020 "LocationCode": "P1-C24"
1021 },
1022 {
1023 "SymbolicFRU": "FRUTST2LONG",
1024 "Priority": "L"
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001025 },
1026 {
1027 "Procedure": "fsi_path",
1028 "Priority": "L"
1029 },
1030 {
1031 "SymbolicFRU": "ambient_temp",
1032 "Priority": "L"
Matt Spinler3bdd0112020-08-27 10:24:34 -05001033 }
1034 ]
1035 )"_json;
1036
1037 message::Entry entry;
1038 entry.src.type = 0xBD;
1039 entry.src.reasonCode = 0xABCD;
1040 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001041
1042 AdditionalData ad;
1043 NiceMock<MockDataInterface> dataIface;
1044
1045 // Callout 0 mock calls
1046 {
1047 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1048 .Times(1)
1049 .WillOnce(Return("UXXX-P0-C1"));
1050 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1051 .Times(1)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001052 .WillOnce(Return(std::vector<std::string>{
1053 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001054 EXPECT_CALL(
1055 dataIface,
1056 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1057 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -04001058 .WillOnce(
1059 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1060 SetArgReferee<3>("123456789ABC")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001061 }
1062 // Callout 1 mock calls
1063 {
1064 EXPECT_CALL(dataIface,
1065 getLocationCode("/inv/system/chassis/motherboard/cpu0"))
1066 .WillOnce(Return("UYYY-P5"));
1067 EXPECT_CALL(
1068 dataIface,
1069 getHWCalloutFields("/inv/system/chassis/motherboard/cpu0", _, _, _))
1070 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -04001071 .WillOnce(
1072 DoAll(SetArgReferee<1>("2345678"), SetArgReferee<2>("DDDD"),
1073 SetArgReferee<3>("23456789ABCD")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001074 }
1075 // Callout 3 mock calls
1076 {
1077 EXPECT_CALL(dataIface, expandLocationCode("P1-C23", 0))
1078 .Times(1)
1079 .WillOnce(Return("UXXX-P1-C23"));
1080 }
1081 // Callout 4 mock calls
1082 {
1083 EXPECT_CALL(dataIface, expandLocationCode("P1-C24", 0))
1084 .Times(1)
1085 .WillOnce(Return("UXXX-P1-C24"));
1086 }
1087
1088 SRC src{entry, ad, jsonCallouts, dataIface};
1089 ASSERT_TRUE(src.callouts());
1090
Matt Spinlerafa2c792020-08-27 11:01:39 -05001091 // Check the guarded and deconfigured flags
1092 EXPECT_TRUE(src.hexwordData()[3] & 0x03000000);
1093
Matt Spinler3bdd0112020-08-27 10:24:34 -05001094 const auto& callouts = src.callouts()->callouts();
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001095 ASSERT_EQ(callouts.size(), 8);
Matt Spinler3bdd0112020-08-27 10:24:34 -05001096
1097 // Check callout 0
1098 {
1099 EXPECT_EQ(callouts[0]->priority(), 'H');
1100 EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C1");
1101
1102 auto& fru = callouts[0]->fruIdentity();
1103 EXPECT_EQ(fru->getPN().value(), "1234567");
1104 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1105 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1106 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
Matt Spinlerb8cb60f2020-08-27 10:55:55 -05001107
1108 auto& mruCallouts = callouts[0]->mru();
1109 ASSERT_TRUE(mruCallouts);
1110 auto& mrus = mruCallouts->mrus();
1111 ASSERT_EQ(mrus.size(), 2);
1112 EXPECT_EQ(mrus[0].id, 42);
1113 EXPECT_EQ(mrus[0].priority, 'H');
1114 EXPECT_EQ(mrus[1].id, 43);
1115 EXPECT_EQ(mrus[1].priority, 'M');
Matt Spinler3bdd0112020-08-27 10:24:34 -05001116 }
1117
1118 // Check callout 1
1119 {
1120 EXPECT_EQ(callouts[1]->priority(), 'M');
1121 EXPECT_EQ(callouts[1]->locationCode(), "UYYY-P5");
1122
1123 auto& fru = callouts[1]->fruIdentity();
1124 EXPECT_EQ(fru->getPN().value(), "2345678");
1125 EXPECT_EQ(fru->getCCIN().value(), "DDDD");
1126 EXPECT_EQ(fru->getSN().value(), "23456789ABCD");
1127 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1128 }
1129
1130 // Check callout 2
1131 {
1132 EXPECT_EQ(callouts[2]->priority(), 'A');
1133 EXPECT_EQ(callouts[2]->locationCode(), "");
1134
1135 auto& fru = callouts[2]->fruIdentity();
1136 EXPECT_EQ(fru->getMaintProc().value(), "PROCEDU");
1137 EXPECT_EQ(fru->failingComponentType(),
1138 src::FRUIdentity::maintenanceProc);
1139 }
1140
1141 // Check callout 3
1142 {
1143 EXPECT_EQ(callouts[3]->priority(), 'B');
1144 EXPECT_EQ(callouts[3]->locationCode(), "UXXX-P1-C23");
1145
1146 auto& fru = callouts[3]->fruIdentity();
1147 EXPECT_EQ(fru->getPN().value(), "TRUSTED");
1148 EXPECT_EQ(fru->failingComponentType(),
1149 src::FRUIdentity::symbolicFRUTrustedLocCode);
1150 }
1151
1152 // Check callout 4
1153 {
1154 EXPECT_EQ(callouts[4]->priority(), 'C');
1155 EXPECT_EQ(callouts[4]->locationCode(), "UXXX-P1-C24");
1156
1157 auto& fru = callouts[4]->fruIdentity();
1158 EXPECT_EQ(fru->getPN().value(), "FRUTST1");
1159 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1160 }
1161
1162 // Check callout 5
1163 {
1164 EXPECT_EQ(callouts[5]->priority(), 'L');
1165 EXPECT_EQ(callouts[5]->locationCode(), "");
1166
1167 auto& fru = callouts[5]->fruIdentity();
1168 EXPECT_EQ(fru->getPN().value(), "FRUTST2");
1169 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1170 }
1171
Matt Spinler3c7ec6d2022-05-06 08:50:20 -05001172 // Check callout 6
1173 {
1174 EXPECT_EQ(callouts[6]->priority(), 'L');
1175 EXPECT_EQ(callouts[6]->locationCode(), "");
1176
1177 auto& fru = callouts[6]->fruIdentity();
1178 EXPECT_EQ(fru->getMaintProc().value(), "BMC0004");
1179 EXPECT_EQ(fru->failingComponentType(),
1180 src::FRUIdentity::maintenanceProc);
1181 }
1182
1183 // Check callout 7
1184 {
1185 EXPECT_EQ(callouts[7]->priority(), 'L');
1186 EXPECT_EQ(callouts[7]->locationCode(), "");
1187
1188 auto& fru = callouts[7]->fruIdentity();
1189 EXPECT_EQ(fru->getPN().value(), "AMBTEMP");
1190 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::symbolicFRU);
1191 }
1192
Matt Spinler3bdd0112020-08-27 10:24:34 -05001193 // Check that it didn't find any errors
1194 const auto& data = src.getDebugData();
1195 EXPECT_TRUE(data.empty());
1196}
1197
1198TEST_F(SRCTest, JsonBadCalloutsTest)
1199{
1200 // The first call will have a Throw in a mock call.
1201 // The second will have a different Throw in a mock call.
1202 // The others have issues with the Priority field.
1203 const auto jsonCallouts = R"(
1204 [
1205 {
1206 "LocationCode": "P0-C1",
1207 "Priority": "H"
1208 },
1209 {
1210 "LocationCode": "P0-C2",
1211 "Priority": "H"
1212 },
1213 {
1214 "LocationCode": "P0-C3"
1215 },
1216 {
1217 "LocationCode": "P0-C4",
1218 "Priority": "X"
1219 }
1220 ]
1221 )"_json;
1222
1223 message::Entry entry;
1224 entry.src.type = 0xBD;
1225 entry.src.reasonCode = 0xABCD;
1226 entry.subsystem = 0x42;
Matt Spinler3bdd0112020-08-27 10:24:34 -05001227
1228 AdditionalData ad;
1229 NiceMock<MockDataInterface> dataIface;
1230
1231 // Callout 0 mock calls
1232 // Expand location code will fail, so the unexpanded location
1233 // code should show up in the callout instead.
1234 {
1235 EXPECT_CALL(dataIface, expandLocationCode("P0-C1", 0))
1236 .WillOnce(Throw(std::runtime_error("Fail")));
1237
1238 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C1", 0, false))
1239 .Times(1)
Matt Spinlerbad056b2023-01-25 14:16:57 -06001240 .WillOnce(Return(std::vector<std::string>{
1241 "/inv/system/chassis/motherboard/bmc"}));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001242 EXPECT_CALL(
1243 dataIface,
1244 getHWCalloutFields("/inv/system/chassis/motherboard/bmc", _, _, _))
1245 .Times(1)
Patrick Williams075c7922024-08-16 15:19:49 -04001246 .WillOnce(
1247 DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1248 SetArgReferee<3>("123456789ABC")));
Matt Spinler3bdd0112020-08-27 10:24:34 -05001249 }
1250
1251 // Callout 1 mock calls
1252 // getInventoryFromLocCode will fail
1253 {
1254 EXPECT_CALL(dataIface, expandLocationCode("P0-C2", 0))
1255 .Times(1)
1256 .WillOnce(Return("UXXX-P0-C2"));
1257
1258 EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C2", 0, false))
1259 .Times(1)
1260 .WillOnce(Throw(std::runtime_error("Fail")));
1261 }
1262
1263 SRC src{entry, ad, jsonCallouts, dataIface};
1264
1265 ASSERT_TRUE(src.callouts());
1266
1267 const auto& callouts = src.callouts()->callouts();
1268
1269 // Only the first callout was successful
1270 ASSERT_EQ(callouts.size(), 1);
1271
1272 {
1273 EXPECT_EQ(callouts[0]->priority(), 'H');
1274 EXPECT_EQ(callouts[0]->locationCode(), "P0-C1");
1275
1276 auto& fru = callouts[0]->fruIdentity();
1277 EXPECT_EQ(fru->getPN().value(), "1234567");
1278 EXPECT_EQ(fru->getCCIN().value(), "CCCC");
1279 EXPECT_EQ(fru->getSN().value(), "123456789ABC");
1280 EXPECT_EQ(fru->failingComponentType(), src::FRUIdentity::hardwareFRU);
1281 }
1282
1283 const auto& data = src.getDebugData();
1284 ASSERT_EQ(data.size(), 4);
1285 EXPECT_STREQ(data[0].c_str(), "Unable to expand location code P0-C1: Fail");
1286 EXPECT_STREQ(data[1].c_str(),
1287 "Failed extracting callout data from JSON: Unable to "
1288 "get inventory path from location code: P0-C2: Fail");
1289 EXPECT_STREQ(data[2].c_str(),
1290 "Failed extracting callout data from JSON: "
1291 "[json.exception.out_of_range.403] key 'Priority' not found");
1292 EXPECT_STREQ(data[3].c_str(),
1293 "Failed extracting callout data from JSON: Invalid "
1294 "priority 'X' found in JSON callout");
1295}
Miguel Gomez53ef1552020-10-14 21:16:32 +00001296
1297// Test that an inventory path callout can have
1298// a different priority than H.
1299TEST_F(SRCTest, InventoryCalloutTestPriority)
1300{
1301 message::Entry entry;
1302 entry.src.type = 0xBD;
1303 entry.src.reasonCode = 0xABCD;
1304 entry.subsystem = 0x42;
Miguel Gomez53ef1552020-10-14 21:16:32 +00001305
Patrick Williamse5940632024-11-22 20:47:58 -05001306 std::map<std::string, std::string> adData{
1307 {"CALLOUT_INVENTORY_PATH", "motherboard"}, {"CALLOUT_PRIORITY", "M"}};
Miguel Gomez53ef1552020-10-14 21:16:32 +00001308 AdditionalData ad{adData};
1309 NiceMock<MockDataInterface> dataIface;
1310
1311 EXPECT_CALL(dataIface, getLocationCode("motherboard"))
1312 .WillOnce(Return("UTMS-P1"));
1313
1314 EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
1315 .Times(1)
1316 .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
1317 SetArgReferee<3>("123456789ABC")));
1318
1319 SRC src{entry, ad, dataIface};
1320 EXPECT_TRUE(src.valid());
1321
1322 ASSERT_TRUE(src.callouts());
1323
1324 EXPECT_EQ(src.callouts()->callouts().size(), 1);
1325
1326 auto& callout = src.callouts()->callouts().front();
1327
1328 EXPECT_EQ(callout->locationCode(), "UTMS-P1");
1329 EXPECT_EQ(callout->priority(), 'M');
Harisuddin Mohamed Isa1a1b0df2020-11-23 16:34:36 +08001330}
Sumit Kumar9d43a722021-08-24 09:46:19 -05001331
Sumit Kumar50bfa692022-01-06 06:48:26 -06001332// Test SRC with additional data - PEL_SUBSYSTEM
1333TEST_F(SRCTest, TestPELSubsystem)
1334{
1335 message::Entry entry;
1336 entry.src.type = 0xBD;
1337 entry.src.reasonCode = 0xABCD;
1338 entry.subsystem = 0x42;
Sumit Kumar50bfa692022-01-06 06:48:26 -06001339
1340 // Values for the SRC words pointed to above
Patrick Williamse5940632024-11-22 20:47:58 -05001341 std::map<std::string, std::string> adData{{"PEL_SUBSYSTEM", "0x20"}};
Sumit Kumar50bfa692022-01-06 06:48:26 -06001342 AdditionalData ad{adData};
1343 NiceMock<MockDataInterface> dataIface;
1344
1345 EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
1346
Sumit Kumar50bfa692022-01-06 06:48:26 -06001347 SRC src{entry, ad, dataIface};
1348
1349 EXPECT_TRUE(src.valid());
1350
1351 EXPECT_EQ(src.asciiString(), "BD20ABCD ");
1352}
Vijay Lobo875b6c72021-10-20 17:38:56 -05001353
1354void setAsciiString(std::vector<uint8_t>& src, const std::string& value)
1355{
1356 assert(40 + value.size() <= src.size());
1357
1358 for (size_t i = 0; i < value.size(); i++)
1359 {
1360 src[40 + i] = value[i];
1361 }
1362}
1363
1364TEST_F(SRCTest, TestGetProgressCode)
1365{
1366 {
1367 // A real SRC with CC009184
1368 std::vector<uint8_t> src{
1369 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1370 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1371 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1372 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1373 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1374
1375 EXPECT_EQ(SRC::getProgressCode(src), 0xCC009184);
1376 }
1377
1378 {
1379 // A real SRC with STANDBY
1380 std::vector<uint8_t> src{
1381 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0,
1382 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 65, 78, 68,
1384 66, 89, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1385 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1386
1387 EXPECT_EQ(SRC::getProgressCode(src), 0);
1388 }
1389
1390 {
1391 // A real SRC with CC009184, but 1 byte too short
1392 std::vector<uint8_t> src{
1393 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1394 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1395 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1396 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1397 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
1398 src.resize(71);
1399 EXPECT_EQ(SRC::getProgressCode(src), 0);
1400 }
1401
1402 {
1403 // A few different ones
1404 const std::map<std::string, uint32_t> progressCodes{
1405 {"12345678", 0x12345678}, {"ABCDEF00", 0xABCDEF00},
1406 {"abcdef00", 0xABCDEF00}, {"X1234567", 0},
1407 {"1234567X", 0}, {"1 ", 0}};
1408
1409 std::vector<uint8_t> src(72, 0x0);
1410
1411 for (const auto& [code, expected] : progressCodes)
1412 {
1413 setAsciiString(src, code);
1414 EXPECT_EQ(SRC::getProgressCode(src), expected);
1415 }
1416
1417 // empty
1418 src.clear();
1419 EXPECT_EQ(SRC::getProgressCode(src), 0);
1420 }
1421}
1422
1423// Test progress is in right SRC hex data field
1424TEST_F(SRCTest, TestProgressCodeField)
1425{
1426 message::Entry entry;
1427 entry.src.type = 0xBD;
1428 entry.src.reasonCode = 0xABCD;
1429 entry.subsystem = 0x42;
1430
1431 AdditionalData ad;
1432 NiceMock<MockDataInterface> dataIface;
Vijay Lobo875b6c72021-10-20 17:38:56 -05001433 EXPECT_CALL(dataIface, getRawProgressSRC())
1434 .WillOnce(Return(std::vector<uint8_t>{
1435 2, 8, 0, 9, 0, 0, 0, 72, 0, 0, 0, 224, 0, 0, 0,
1436 0, 204, 0, 145, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1437 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 48, 48, 57,
1438 49, 56, 52, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1439 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}));
1440
1441 SRC src{entry, ad, dataIface};
1442 EXPECT_TRUE(src.valid());
1443
1444 // Verify that the hex vlue is set at the right hexword
1445 EXPECT_EQ(src.hexwordData()[2], 0xCC009184);
1446}