1 /**
2  * Copyright © 2019 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "extensions/openpower-pels/src.hpp"
17 #include "mocks.hpp"
18 #include "pel_utils.hpp"
19 
20 #include <fstream>
21 
22 #include <gtest/gtest.h>
23 
24 using namespace openpower::pels;
25 using ::testing::NiceMock;
26 using ::testing::Return;
27 namespace fs = std::filesystem;
28 
29 const auto testRegistry = R"(
30 {
31 "PELs":
32 [
33     {
34         "Name": "xyz.openbmc_project.Error.Test",
35         "Subsystem": "bmc_firmware",
36         "SRC":
37         {
38             "ReasonCode": "0xABCD",
39             "Words6To9":
40             {
41                 "6":
42                 {
43                     "Description": "Component ID",
44                     "AdditionalDataPropSource": "COMPID"
45                 },
46                 "7":
47                 {
48                     "Description": "Failure count",
49                     "AdditionalDataPropSource": "FREQUENCY"
50                 },
51                 "8":
52                 {
53                     "Description": "Time period",
54                     "AdditionalDataPropSource": "DURATION"
55                 },
56                 "9":
57                 {
58                     "Description": "Error code",
59                     "AdditionalDataPropSource": "ERRORCODE"
60                 }
61             }
62         },
63         "Documentation":
64         {
65             "Description": "A Component Fault",
66             "Message": "Comp %1 failed %2 times over %3 secs with ErrorCode %4",
67             "MessageArgSources":
68             [
69                 "SRCWord6", "SRCWord7", "SRCWord8", "SRCWord9"
70             ]
71         }
72     }
73 ]
74 }
75 )";
76 
77 class SRCTest : public ::testing::Test
78 {
79   protected:
80     static void SetUpTestCase()
81     {
82         char path[] = "/tmp/srctestXXXXXX";
83         regDir = mkdtemp(path);
84     }
85 
86     static void TearDownTestCase()
87     {
88         fs::remove_all(regDir);
89     }
90 
91     static std::string writeData(const char* data)
92     {
93         fs::path path = regDir / "registry.json";
94         std::ofstream stream{path};
95         stream << data;
96         return path;
97     }
98 
99     static fs::path regDir;
100 };
101 
102 fs::path SRCTest::regDir{};
103 
104 TEST_F(SRCTest, UnflattenFlattenTestNoCallouts)
105 {
106     auto data = pelDataFactory(TestPELType::primarySRCSection);
107 
108     Stream stream{data};
109     SRC src{stream};
110 
111     EXPECT_TRUE(src.valid());
112 
113     EXPECT_EQ(src.header().id, 0x5053);
114     EXPECT_EQ(src.header().size, 0x50);
115     EXPECT_EQ(src.header().version, 0x01);
116     EXPECT_EQ(src.header().subType, 0x01);
117     EXPECT_EQ(src.header().componentID, 0x0202);
118 
119     EXPECT_EQ(src.version(), 0x02);
120     EXPECT_EQ(src.flags(), 0x00);
121     EXPECT_EQ(src.hexWordCount(), 9);
122     EXPECT_EQ(src.size(), 0x48);
123 
124     const auto& hexwords = src.hexwordData();
125     EXPECT_EQ(0x02020255, hexwords[0]);
126     EXPECT_EQ(0x03030310, hexwords[1]);
127     EXPECT_EQ(0x04040404, hexwords[2]);
128     EXPECT_EQ(0x05050505, hexwords[3]);
129     EXPECT_EQ(0x06060606, hexwords[4]);
130     EXPECT_EQ(0x07070707, hexwords[5]);
131     EXPECT_EQ(0x08080808, hexwords[6]);
132     EXPECT_EQ(0x09090909, hexwords[7]);
133 
134     EXPECT_EQ(src.asciiString(), "BD8D5678                        ");
135     EXPECT_FALSE(src.callouts());
136 
137     // Flatten
138     std::vector<uint8_t> newData;
139     Stream newStream{newData};
140 
141     src.flatten(newStream);
142     EXPECT_EQ(data, newData);
143 }
144 
145 TEST_F(SRCTest, UnflattenFlattenTest2Callouts)
146 {
147     auto data = pelDataFactory(TestPELType::primarySRCSection2Callouts);
148 
149     Stream stream{data};
150     SRC src{stream};
151 
152     EXPECT_TRUE(src.valid());
153     EXPECT_EQ(src.flags(), 0x01); // Additional sections within the SRC.
154 
155     // Spot check the SRC fields, but they're the same as above
156     EXPECT_EQ(src.asciiString(), "BD8D5678                        ");
157 
158     // There should be 2 callouts
159     const auto& calloutsSection = src.callouts();
160     ASSERT_TRUE(calloutsSection);
161     const auto& callouts = calloutsSection->callouts();
162     EXPECT_EQ(callouts.size(), 2);
163 
164     // spot check that each callout has the right substructures
165     EXPECT_TRUE(callouts.front()->fruIdentity());
166     EXPECT_FALSE(callouts.front()->pceIdentity());
167     EXPECT_FALSE(callouts.front()->mru());
168 
169     EXPECT_TRUE(callouts.back()->fruIdentity());
170     EXPECT_TRUE(callouts.back()->pceIdentity());
171     EXPECT_TRUE(callouts.back()->mru());
172 
173     // Flatten
174     std::vector<uint8_t> newData;
175     Stream newStream{newData};
176 
177     src.flatten(newStream);
178     EXPECT_EQ(data, newData);
179 }
180 
181 // Create an SRC from the message registry
182 TEST_F(SRCTest, CreateTestNoCallouts)
183 {
184     message::Entry entry;
185     entry.src.type = 0xBD;
186     entry.src.reasonCode = 0xABCD;
187     entry.subsystem = 0x42;
188     entry.src.powerFault = true;
189     entry.src.hexwordADFields = {{5, "TEST1"}, // Not a user defined word
190                                  {6, "TEST1"},
191                                  {7, "TEST2"},
192                                  {8, "TEST3"},
193                                  {9, "TEST4"}};
194 
195     // Values for the SRC words pointed to above
196     std::vector<std::string> adData{"TEST1=0x12345678", "TEST2=12345678",
197                                     "TEST3=0XDEF", "TEST4=Z"};
198     AdditionalData ad{adData};
199     NiceMock<MockDataInterface> dataIface;
200 
201     EXPECT_CALL(dataIface, getMotherboardCCIN).WillOnce(Return("ABCD"));
202 
203     SRC src{entry, ad, dataIface};
204 
205     EXPECT_TRUE(src.valid());
206     EXPECT_TRUE(src.isPowerFaultEvent());
207     EXPECT_EQ(src.size(), baseSRCSize);
208 
209     const auto& hexwords = src.hexwordData();
210 
211     // The spec always refers to SRC words 2 - 9, and as the hexwordData()
212     // array index starts at 0 use the math in the [] below to make it easier
213     // to tell what is being accessed.
214     EXPECT_EQ(hexwords[2 - 2] & 0xF0000000, 0);    // Partition dump status
215     EXPECT_EQ(hexwords[2 - 2] & 0x00F00000, 0);    // Partition boot type
216     EXPECT_EQ(hexwords[2 - 2] & 0x000000FF, 0x55); // SRC format
217     EXPECT_EQ(hexwords[3 - 2] & 0x000000FF, 0x10); // BMC position
218     EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0xABCD0000); // Motherboard CCIN
219 
220     // Validate more fields here as the code starts filling them in.
221 
222     // Ensure hex word 5 wasn't allowed to be set to TEST1's contents
223     EXPECT_EQ(hexwords[5 - 2], 0);
224 
225     // The user defined hex word fields specifed in the additional data.
226     EXPECT_EQ(hexwords[6 - 2], 0x12345678); // TEST1
227     EXPECT_EQ(hexwords[7 - 2], 12345678);   // TEST2
228     EXPECT_EQ(hexwords[8 - 2], 0xdef);      // TEST3
229     EXPECT_EQ(hexwords[9 - 2], 0);          // TEST4, but can't convert a 'Z'
230 
231     EXPECT_EQ(src.asciiString(), "BD42ABCD                        ");
232 
233     // No callouts
234     EXPECT_FALSE(src.callouts());
235 
236     // May as well spot check the flatten/unflatten
237     std::vector<uint8_t> data;
238     Stream stream{data};
239     src.flatten(stream);
240 
241     stream.offset(0);
242     SRC newSRC{stream};
243 
244     EXPECT_TRUE(newSRC.valid());
245     EXPECT_EQ(newSRC.isPowerFaultEvent(), src.isPowerFaultEvent());
246     EXPECT_EQ(newSRC.asciiString(), src.asciiString());
247     EXPECT_FALSE(newSRC.callouts());
248 }
249 
250 // Test when the CCIN string isn't a 4 character number
251 TEST_F(SRCTest, BadCCINTest)
252 {
253     message::Entry entry;
254     entry.src.type = 0xBD;
255     entry.src.reasonCode = 0xABCD;
256     entry.subsystem = 0x42;
257     entry.src.powerFault = false;
258 
259     std::vector<std::string> adData{};
260     AdditionalData ad{adData};
261     NiceMock<MockDataInterface> dataIface;
262 
263     // First it isn't a number, then it is too long,
264     // then it is empty.
265     EXPECT_CALL(dataIface, getMotherboardCCIN)
266         .WillOnce(Return("X"))
267         .WillOnce(Return("12345"))
268         .WillOnce(Return(""));
269 
270     // The CCIN in the first half should still be 0 each time.
271     {
272         SRC src{entry, ad, dataIface};
273         EXPECT_TRUE(src.valid());
274         const auto& hexwords = src.hexwordData();
275         EXPECT_EQ(hexwords[3 - 2] & 0xFFFF0000, 0x00000000);
276     }
277 
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 // Test the getErrorDetails function
294 TEST_F(SRCTest, MessageSubstitutionTest)
295 {
296     auto path = SRCTest::writeData(testRegistry);
297     message::Registry registry{path};
298     auto entry = registry.lookup("0xABCD", message::LookupType::reasonCode);
299 
300     std::vector<std::string> adData{"COMPID=0x1", "FREQUENCY=0x4",
301                                     "DURATION=30", "ERRORCODE=0x01ABCDEF"};
302     AdditionalData ad{adData};
303     NiceMock<MockDataInterface> dataIface;
304 
305     SRC src{*entry, ad, dataIface};
306     EXPECT_TRUE(src.valid());
307 
308     auto errorDetails = src.getErrorDetails(registry, DetailLevel::message);
309     ASSERT_TRUE(errorDetails);
310     EXPECT_EQ(
311         errorDetails.value(),
312         "Comp 0x1 failed 0x4 times over 0x1E secs with ErrorCode 0x1ABCDEF");
313 }
314