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/callout.hpp"
17 #include "pel_utils.hpp"
18 
19 #include <gtest/gtest.h>
20 
21 using namespace openpower::pels;
22 using namespace openpower::pels::src;
23 
24 // Unflatten the callout section with all three substructures
25 TEST(CalloutTest, TestUnflattenAllSubstructures)
26 {
27     // The base data.
28     std::vector<uint8_t> data{
29         0xFF, 0x2F, 'H', 8, // size, flags, priority, LC length
30         'U',  '1',  '2', '-', 'P', '1', 0x00, 0x00 // LC
31     };
32 
33     auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
34     auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
35     auto mrus = srcDataFactory(TestSRCType::mruStructure);
36 
37     // Add all 3 substructures
38     data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
39     data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
40     data.insert(data.end(), mrus.begin(), mrus.end());
41 
42     // The final size
43     data[0] = data.size();
44 
45     Stream stream{data};
46     Callout callout{stream};
47 
48     EXPECT_EQ(callout.flattenedSize(), data.size());
49     EXPECT_EQ(callout.priority(), 'H');
50     EXPECT_EQ(callout.locationCode(), "U12-P1");
51 
52     // Spot check the 3 substructures
53     EXPECT_TRUE(callout.fruIdentity());
54     EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
55 
56     EXPECT_TRUE(callout.pceIdentity());
57     EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
58 
59     EXPECT_TRUE(callout.mru());
60     EXPECT_EQ(callout.mru()->mrus().size(), 4);
61     EXPECT_EQ(callout.mru()->mrus().at(3).id, 0x04040404);
62 
63     // Now flatten
64     std::vector<uint8_t> newData;
65     Stream newStream{newData};
66 
67     callout.flatten(newStream);
68     EXPECT_EQ(data, newData);
69 }
70 
71 TEST(CalloutTest, TestUnflattenOneSubstructure)
72 {
73     std::vector<uint8_t> data{
74         0xFF, 0x28, 'H', 0x08, // size, flags, priority, LC length
75         'U',  '1',  '2', '-',  'P', '1', 0x00, 0x00 // LC
76     };
77 
78     auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
79 
80     data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
81 
82     // The final size
83     data[0] = data.size();
84 
85     Stream stream{data};
86     Callout callout{stream};
87 
88     EXPECT_EQ(callout.flattenedSize(), data.size());
89 
90     // Spot check the substructure
91     EXPECT_TRUE(callout.fruIdentity());
92     EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
93 
94     // Not present
95     EXPECT_FALSE(callout.pceIdentity());
96     EXPECT_FALSE(callout.mru());
97 
98     // Now flatten
99     std::vector<uint8_t> newData;
100     Stream newStream{newData};
101 
102     callout.flatten(newStream);
103     EXPECT_EQ(data, newData);
104 }
105 
106 TEST(CalloutTest, TestUnflattenTwoSubstructures)
107 {
108     std::vector<uint8_t> data{
109         0xFF, 0x2B, 'H', 0x08, // size, flags, priority, LC length
110         'U',  '1',  '2', '-',  'P', '1', 0x00, 0x00 // LC
111     };
112 
113     auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
114     auto pceIdentity = srcDataFactory(TestSRCType::pceIdentityStructure);
115 
116     data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
117     data.insert(data.end(), pceIdentity.begin(), pceIdentity.end());
118 
119     // The final size
120     data[0] = data.size();
121 
122     Stream stream{data};
123     Callout callout{stream};
124 
125     EXPECT_EQ(callout.flattenedSize(), data.size());
126 
127     // Spot check the 2 substructures
128     EXPECT_TRUE(callout.fruIdentity());
129     EXPECT_EQ(callout.fruIdentity()->getSN(), "123456789ABC");
130 
131     EXPECT_TRUE(callout.pceIdentity());
132     EXPECT_EQ(callout.pceIdentity()->enclosureName(), "PCENAME12");
133 
134     // Not present
135     EXPECT_FALSE(callout.mru());
136 
137     // Now flatten
138     std::vector<uint8_t> newData;
139     Stream newStream{newData};
140 
141     callout.flatten(newStream);
142     EXPECT_EQ(data, newData);
143 }
144 
145 TEST(CalloutTest, TestNoLocationCode)
146 {
147     std::vector<uint8_t> data{
148         0xFF, 0x2B, 'H', 0x00 // size, flags, priority, LC length
149     };
150 
151     auto fruIdentity = srcDataFactory(TestSRCType::fruIdentityStructure);
152     data.insert(data.end(), fruIdentity.begin(), fruIdentity.end());
153 
154     // The final size
155     data[0] = data.size();
156 
157     Stream stream{data};
158     Callout callout{stream};
159 
160     EXPECT_TRUE(callout.locationCode().empty());
161 }
162 
163 // Create a callout object by passing in the hardware fields to add
164 TEST(CalloutTest, TestHardwareCallout)
165 {
166     constexpr size_t fruIdentitySize = 28;
167 
168     {
169         Callout callout{CalloutPriority::high, "U99-42.5-P1-C2-E1", "1234567",
170                         "ABCD", "123456789ABC"};
171 
172         // size/flags/pri/locsize fields +
173         // rounded up location code length +
174         // FRUIdentity size
175         size_t size = 4 + 20 + fruIdentitySize;
176 
177         EXPECT_EQ(callout.flags(),
178                   Callout::calloutType | Callout::fruIdentIncluded);
179 
180         EXPECT_EQ(callout.flattenedSize(), size);
181         EXPECT_EQ(callout.priority(), 'H');
182         EXPECT_EQ(callout.locationCode(), "U99-42.5-P1-C2-E1");
183         EXPECT_EQ(callout.locationCodeSize(), 20);
184 
185         auto& fru = callout.fruIdentity();
186         EXPECT_EQ(fru->getPN().value(), "1234567");
187         EXPECT_EQ(fru->getCCIN().value(), "ABCD");
188         EXPECT_EQ(fru->getSN().value(), "123456789ABC");
189     }
190 
191     {
192         // A 3B location code, plus null = 4
193         Callout callout{CalloutPriority::high, "123", "1234567", "ABCD",
194                         "123456789ABC"};
195 
196         size_t size = 4 + 4 + fruIdentitySize;
197         EXPECT_EQ(callout.locationCodeSize(), 4);
198         EXPECT_EQ(callout.flattenedSize(), size);
199         EXPECT_EQ(callout.locationCode(), "123");
200     }
201 
202     {
203         // A 4B location code, plus null = 5, then pad to 8
204         Callout callout{CalloutPriority::high, "1234", "1234567", "ABCD",
205                         "123456789ABC"};
206 
207         size_t size = 4 + 8 + fruIdentitySize;
208         EXPECT_EQ(callout.locationCodeSize(), 8);
209         EXPECT_EQ(callout.flattenedSize(), size);
210         EXPECT_EQ(callout.locationCode(), "1234");
211     }
212 
213     {
214         // A truncated location code (80 is max size, including null)
215         std::string locCode(81, 'L');
216         Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD",
217                         "123456789ABC"};
218 
219         size_t size = 4 + 80 + fruIdentitySize;
220         EXPECT_EQ(callout.locationCodeSize(), 80);
221         EXPECT_EQ(callout.flattenedSize(), size);
222 
223         // take off 1 to get to 80, and another for the null
224         locCode = locCode.substr(0, locCode.size() - 2);
225         EXPECT_EQ(callout.locationCode(), locCode);
226     }
227 
228     {
229         // A truncated location code by 1 because of the null
230         std::string locCode(80, 'L');
231         Callout callout{CalloutPriority::high, locCode, "1234567", "ABCD",
232                         "123456789ABC"};
233 
234         size_t size = 4 + 80 + fruIdentitySize;
235         EXPECT_EQ(callout.locationCodeSize(), 80);
236         EXPECT_EQ(callout.flattenedSize(), size);
237 
238         locCode.pop_back();
239         EXPECT_EQ(callout.locationCode(), locCode);
240     }
241 
242     {
243         // Max size location code
244         std::string locCode(79, 'L');
245         Callout callout{CalloutPriority::low, locCode, "1234567", "ABCD",
246                         "123456789ABC"};
247 
248         size_t size = 4 + 80 + fruIdentitySize;
249         EXPECT_EQ(callout.locationCodeSize(), 80);
250         EXPECT_EQ(callout.flattenedSize(), size);
251 
252         EXPECT_EQ(callout.locationCode(), locCode);
253 
254         // How about we flatten/unflatten this last one
255         std::vector<uint8_t> data;
256         Stream stream{data};
257 
258         callout.flatten(stream);
259 
260         {
261             Stream newStream{data};
262             Callout newCallout{newStream};
263 
264             EXPECT_EQ(newCallout.flags(),
265                       Callout::calloutType | Callout::fruIdentIncluded);
266 
267             EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize());
268             EXPECT_EQ(newCallout.priority(), callout.priority());
269             EXPECT_EQ(newCallout.locationCode(), callout.locationCode());
270             EXPECT_EQ(newCallout.locationCodeSize(),
271                       callout.locationCodeSize());
272 
273             auto& fru = newCallout.fruIdentity();
274             EXPECT_EQ(fru->getPN().value(), "1234567");
275             EXPECT_EQ(fru->getCCIN().value(), "ABCD");
276             EXPECT_EQ(fru->getSN().value(), "123456789ABC");
277         }
278     }
279 
280     {
281         // With MRUs
282         std::vector<MRU::MRUCallout> mruList{{'H', 1}, {'H', 2}};
283 
284         Callout callout{CalloutPriority::high, "U99-P5", "1234567", "ABCD",
285                         "123456789ABC",        mruList};
286 
287         EXPECT_EQ(callout.flags(), Callout::calloutType |
288                                        Callout::fruIdentIncluded |
289                                        Callout::mruIncluded);
290 
291         EXPECT_EQ(callout.priority(), 'H');
292         EXPECT_EQ(callout.locationCode(), "U99-P5");
293         EXPECT_EQ(callout.locationCodeSize(), 8);
294 
295         auto& fru = callout.fruIdentity();
296         EXPECT_EQ(fru->getPN().value(), "1234567");
297         EXPECT_EQ(fru->getCCIN().value(), "ABCD");
298         EXPECT_EQ(fru->getSN().value(), "123456789ABC");
299 
300         auto& mruSection = callout.mru();
301         EXPECT_EQ(mruSection->mrus().size(), 2);
302         EXPECT_EQ(mruSection->mrus().at(0).priority, 'H');
303         EXPECT_EQ(mruSection->mrus().at(0).id, 1);
304         EXPECT_EQ(mruSection->mrus().at(1).priority, 'H');
305         EXPECT_EQ(mruSection->mrus().at(1).id, 2);
306     }
307 }
308 
309 // Create a callout object by passing in the maintenance procedure to add.
310 TEST(CalloutTest, TestProcedureCallout)
311 {
312     Callout callout{CalloutPriority::medium, "bmc_code"};
313 
314     // size/flags/pri/locsize fields + FRUIdentity size
315     // No location code.
316     size_t size = 4 + 12;
317 
318     EXPECT_EQ(callout.flags(),
319               Callout::calloutType | Callout::fruIdentIncluded);
320 
321     EXPECT_EQ(callout.flattenedSize(), size);
322     EXPECT_EQ(callout.priority(), 'M');
323     EXPECT_EQ(callout.locationCode(), "");
324     EXPECT_EQ(callout.locationCodeSize(), 0);
325 
326     auto& fru = callout.fruIdentity();
327     EXPECT_EQ(fru->getMaintProc().value(), "BMC0001");
328 
329     // flatten/unflatten
330     std::vector<uint8_t> data;
331     Stream stream{data};
332 
333     callout.flatten(stream);
334 
335     Stream newStream{data};
336     Callout newCallout{newStream};
337 
338     EXPECT_EQ(newCallout.flags(),
339               Callout::calloutType | Callout::fruIdentIncluded);
340 
341     EXPECT_EQ(newCallout.flattenedSize(), callout.flattenedSize());
342     EXPECT_EQ(newCallout.priority(), callout.priority());
343     EXPECT_EQ(newCallout.locationCode(), callout.locationCode());
344     EXPECT_EQ(newCallout.locationCodeSize(), callout.locationCodeSize());
345 
346     auto& newFRU = newCallout.fruIdentity();
347     EXPECT_EQ(newFRU->getMaintProc().value(), fru->getMaintProc().value());
348 
349     // Use raw procedure value
350 
351     Callout rawCallout{CalloutPriority::medium, "BMCXXXX",
352                        CalloutValueType::raw};
353     auto& rawFRU = rawCallout.fruIdentity();
354     EXPECT_EQ(rawFRU->getMaintProc().value(), "BMCXXXX");
355 }
356 
357 // Create a callout object by passing in the symbolic FRU to add.
358 TEST(CalloutTest, TestSymbolicFRUCallout)
359 {
360     // symbolic FRU with a location code
361     {
362         Callout callout{CalloutPriority::high, "service_docs", "P1-C3", false};
363 
364         // size/flags/pri/locsize fields + plus loc + FRUIdentity size
365         size_t size = 4 + 8 + 12;
366 
367         EXPECT_EQ(callout.flags(),
368                   Callout::calloutType | Callout::fruIdentIncluded);
369 
370         EXPECT_EQ(callout.flattenedSize(), size);
371         EXPECT_EQ(callout.priority(), 'H');
372         EXPECT_EQ(callout.locationCode(), "P1-C3");
373         EXPECT_EQ(callout.locationCodeSize(), 8);
374 
375         auto& fru = callout.fruIdentity();
376 
377         EXPECT_EQ(fru->failingComponentType(), FRUIdentity::symbolicFRU);
378         EXPECT_EQ(fru->getPN().value(), "SVCDOCS");
379     }
380 
381     // symbolic FRU without a location code
382     {
383         Callout callout{CalloutPriority::high, "service_docs", "", false};
384 
385         // size/flags/pri/locsize fields + plus loc + FRUIdentity size
386         size_t size = 4 + 0 + 12;
387 
388         EXPECT_EQ(callout.flags(),
389                   Callout::calloutType | Callout::fruIdentIncluded);
390 
391         EXPECT_EQ(callout.flattenedSize(), size);
392         EXPECT_EQ(callout.priority(), 'H');
393         EXPECT_EQ(callout.locationCode(), "");
394         EXPECT_EQ(callout.locationCodeSize(), 0);
395 
396         auto& fru = callout.fruIdentity();
397 
398         EXPECT_EQ(fru->failingComponentType(), FRUIdentity::symbolicFRU);
399         EXPECT_EQ(fru->getPN().value(), "SVCDOCS");
400     }
401 
402     // symbolic FRU with a trusted location code
403     {
404         Callout callout{CalloutPriority::high, "service_docs", "P1-C3", true};
405 
406         // size/flags/pri/locsize fields + plus loc + FRUIdentity size
407         size_t size = 4 + 8 + 12;
408 
409         EXPECT_EQ(callout.flags(),
410                   Callout::calloutType | Callout::fruIdentIncluded);
411 
412         EXPECT_EQ(callout.flattenedSize(), size);
413         EXPECT_EQ(callout.priority(), 'H');
414         EXPECT_EQ(callout.locationCode(), "P1-C3");
415         EXPECT_EQ(callout.locationCodeSize(), 8);
416 
417         auto& fru = callout.fruIdentity();
418         EXPECT_EQ(fru->failingComponentType(),
419                   FRUIdentity::symbolicFRUTrustedLocCode);
420         EXPECT_EQ(fru->getPN().value(), "SVCDOCS");
421     }
422 
423     // symbolic FRU with raw FRU value
424     {
425         {
426             Callout callout{CalloutPriority::high, "SYMBFRU",
427                             CalloutValueType::raw, "", false};
428 
429             auto& fru = callout.fruIdentity();
430 
431             EXPECT_EQ(fru->getPN().value(), "SYMBFRU");
432         }
433     }
434 }
435