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 "pel_utils.hpp"
17
18 #include "extensions/openpower-pels/private_header.hpp"
19 #include "extensions/openpower-pels/user_header.hpp"
20
21 #include <fstream>
22
23 #include <gtest/gtest.h>
24
25 namespace fs = std::filesystem;
26 using namespace openpower::pels;
27
28 std::filesystem::path CleanLogID::pelIDFile{};
29 std::filesystem::path CleanPELFiles::pelIDFile{};
30 std::filesystem::path CleanPELFiles::repoPath{};
31 std::filesystem::path CleanPELFiles::registryPath{};
32
33 const std::vector<uint8_t> privateHeaderSection{
34 // section header
35 0x50, 0x48, // ID 'PH'
36 0x00, 0x30, // Size
37 0x01, 0x02, // version, subtype
38 0x03, 0x04, // comp ID
39
40 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, // create timestamp
41 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A, 0x00, // commit timestamp
42 0x4F, // creatorID 'O'
43 0x00, // logtype
44 0x00, // reserved
45 0x02, // section count
46 0x90, 0x91, 0x92, 0x93, // OpenBMC log ID
47 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0, // creator version
48 0x50, 0x51, 0x52, 0x53, // plid
49 0x80, 0x81, 0x82, 0x83}; // PEL ID
50
51 const std::vector<uint8_t> userHeaderSection{
52 // section header
53 0x55, 0x48, // ID 'UH'
54 0x00, 0x18, // Size
55 0x01, 0x0A, // version, subtype
56 0x0B, 0x0C, // comp ID
57
58 0x10, 0x04, // subsystem, scope
59 0x20, 0x00, // severity, type
60 0x00, 0x00, 0x00, 0x00, // reserved
61 0x03, 0x04, // problem domain, vector
62 0x80, 0xC0, // action flags
63 0x00, 0x00, 0x00, 0x00 // reserved
64 };
65
66 const std::vector<uint8_t> srcSectionNoCallouts{
67
68 // Header
69 'P', 'S', 0x00, 0x50, 0x01, 0x01, 0x02, 0x02,
70
71 0x02, 0x00, 0x00, // version, flags, reserved
72 0x09, 0x00, 0x00, // hex word count, reserved2B
73 0x00, 0x48, // SRC structure size
74
75 // Hex words 2 - 9
76 0x02, 0x02, 0x02, 0x55, 0x03, 0x03, 0x03, 0x10, 0x04, 0x04, 0x04, 0x04,
77 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
78 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
79 // ASCII string
80 'B', 'D', '8', 'D', '5', '6', '7', '8', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
81 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
82 ' ', ' '};
83
84 const std::vector<uint8_t> failingMTMSSection{
85 // Header
86 0x4D, 0x54, 0x00, 0x1C, 0x01, 0x00, 0x20, 0x00,
87
88 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2',
89 '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'};
90
91 const std::vector<uint8_t> UserDataSection{
92 // Header
93 0x55, 0x44, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00,
94
95 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
96
97 const std::vector<uint8_t> ExtUserHeaderSection{
98 // Header
99 'E', 'H', 0x00, 0x60, 0x01, 0x00, 0x03, 0x04,
100
101 // MTMS
102 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', '1', '2', '3', '4', '5', '6', '7',
103 '8', '9', 'A', 'B', 'C',
104
105 // Server FW version
106 'S', 'E', 'R', 'V', 'E', 'R', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0',
107 '\0',
108
109 // Subsystem FW Version
110 'B', 'M', 'C', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '\0', '\0', '\0',
111 '\0', '\0',
112
113 0x00, 0x00, 0x00, 0x00, // Reserved
114 0x20, 0x25, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // Ref time
115 0x00, 0x00, 0x00, // Reserved
116
117 // SymptomID length and symptom ID
118 20, 'B', 'D', '8', 'D', '4', '2', '0', '0', '_', '1', '2', '3', '4', '5',
119 '6', '7', '8', '\0', '\0', '\0'};
120
121 const std::vector<uint8_t> srcFRUIdentityCallout{
122 'I', 'D', 0x1C, 0x1D, // type, size, flags
123 '1', '2', '3', '4', // PN
124 '5', '6', '7', 0x00, 'A', 'A', 'A', 'A', // CCIN
125 '1', '2', '3', '4', '5', '6', '7', '8', // SN
126 '9', 'A', 'B', 'C'};
127
128 const std::vector<uint8_t> srcPCEIdentityCallout{
129 'P', 'E', 0x24, 0x00, // type, size, flags
130 'T', 'T', 'T', 'T', '-', 'M', 'M', 'M', // MTM
131 '1', '2', '3', '4', '5', '6', '7', // SN
132 '8', '9', 'A', 'B', 'C', 'P', 'C', 'E', // Name + null padded
133 'N', 'A', 'M', 'E', '1', '2', 0x00, 0x00, 0x00};
134
135 const std::vector<uint8_t> srcMRUCallout{
136 'M', 'R', 0x28, 0x04, // ID, size, flags
137 0x00, 0x00, 0x00, 0x00, // Reserved
138 0x00, 0x00, 0x00, 'H', // priority 0
139 0x01, 0x01, 0x01, 0x01, // MRU ID 0
140 0x00, 0x00, 0x00, 'M', // priority 1
141 0x02, 0x02, 0x02, 0x02, // MRU ID 1
142 0x00, 0x00, 0x00, 'L', // priority 2
143 0x03, 0x03, 0x03, 0x03, // MRU ID 2
144 0x00, 0x00, 0x00, 'H', // priority 3
145 0x04, 0x04, 0x04, 0x04, // MRU ID 3
146 };
147
148 const std::vector<uint8_t> extendedUserDataSection{
149 // Header
150 0x45, 0x44, 0x00, 0x18, 0x01, 0x02, 0x20, 0x00,
151
152 // Creator ID 'O', and then data
153 0x4F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
154 0x05, 0x0A, 0x0B, 0x0C};
155
156 constexpr size_t sectionCountOffset = 27;
157 constexpr size_t createTimestampPHOffset = 8;
158 constexpr size_t commitTimestampPHOffset = 16;
159 constexpr size_t creatorPHOffset = 24;
160 constexpr size_t obmcIDPHOffset = 28;
161 constexpr size_t plidPHOffset = 40;
162 constexpr size_t pelIDPHOffset = 44;
163 constexpr size_t sevUHOffset = 10;
164 constexpr size_t actionFlagsUHOffset = 18;
165
pelDataFactory(TestPELType type)166 std::vector<uint8_t> pelDataFactory(TestPELType type)
167 {
168 std::vector<uint8_t> data;
169
170 switch (type)
171 {
172 case TestPELType::pelSimple:
173 data.insert(data.end(), privateHeaderSection.begin(),
174 privateHeaderSection.end());
175 data.insert(data.end(), userHeaderSection.begin(),
176 userHeaderSection.end());
177 data.insert(data.end(), srcSectionNoCallouts.begin(),
178 srcSectionNoCallouts.end());
179 data.insert(data.end(), failingMTMSSection.begin(),
180 failingMTMSSection.end());
181 data.insert(data.end(), UserDataSection.begin(),
182 UserDataSection.end());
183 data.insert(data.end(), ExtUserHeaderSection.begin(),
184 ExtUserHeaderSection.end());
185 data.insert(data.end(), extendedUserDataSection.begin(),
186 extendedUserDataSection.end());
187 data.at(sectionCountOffset) = 7;
188 break;
189 case TestPELType::privateHeaderSection:
190 data.insert(data.end(), privateHeaderSection.begin(),
191 privateHeaderSection.end());
192 break;
193 case TestPELType::userHeaderSection:
194 data.insert(data.end(), userHeaderSection.begin(),
195 userHeaderSection.end());
196 break;
197 case TestPELType::primarySRCSection:
198 data.insert(data.end(), srcSectionNoCallouts.begin(),
199 srcSectionNoCallouts.end());
200 break;
201 case TestPELType::primarySRCSection2Callouts:
202 {
203 // Start with the no-callouts SRC, and add the callouts section
204 // from above.
205 auto src = srcSectionNoCallouts;
206 auto callouts =
207 srcDataFactory(TestSRCType::calloutSection2Callouts);
208
209 src.insert(src.end(), callouts.begin(), callouts.end());
210
211 // Set the flag that says there are callouts
212 // One byte after the 8B header
213 src[8 + 1] |= 0x01;
214
215 // Set the new sizes
216 uint16_t size = src.size();
217 Stream stream{src};
218
219 stream.offset(2); // In the header
220 stream << size;
221
222 // In the SRC - the size field doesn't include the header
223 size -= 8;
224 stream.offset(8 + 6);
225 stream << size;
226
227 data.insert(data.end(), src.begin(), src.end());
228 break;
229 }
230 case TestPELType::failingMTMSSection:
231 data.insert(data.end(), failingMTMSSection.begin(),
232 failingMTMSSection.end());
233 break;
234 case TestPELType::extendedUserDataSection:
235 data.insert(data.end(), extendedUserDataSection.begin(),
236 extendedUserDataSection.end());
237 break;
238 }
239 return data;
240 }
241
pelFactory(uint32_t id,char creatorID,uint8_t severity,uint16_t actionFlags,size_t size)242 std::vector<uint8_t> pelFactory(uint32_t id, char creatorID, uint8_t severity,
243 uint16_t actionFlags, size_t size)
244 {
245 std::vector<uint8_t> data;
246 size_t offset = 0;
247
248 auto now = std::chrono::system_clock::now();
249 auto timestamp = getBCDTime(now);
250
251 // Start with the default Private Header, and modify it
252 data.insert(data.end(), privateHeaderSection.begin(),
253 privateHeaderSection.end());
254 data.at(creatorPHOffset) = creatorID;
255
256 // Modify the multibyte fields in it
257 Stream stream{data};
258 stream.offset(createTimestampPHOffset);
259 stream << timestamp;
260 stream.offset(commitTimestampPHOffset);
261 stream << timestamp;
262 stream.offset(plidPHOffset);
263 stream << id;
264 stream.offset(pelIDPHOffset);
265 stream << id;
266 stream.offset(obmcIDPHOffset);
267 stream << id + 500;
268
269 offset = data.size();
270
271 // User Header
272 data.insert(data.end(), userHeaderSection.begin(), userHeaderSection.end());
273 data.at(offset + sevUHOffset) = severity;
274 data.at(offset + actionFlagsUHOffset) = actionFlags >> 8;
275 data.at(offset + actionFlagsUHOffset + 1) = actionFlags;
276
277 // Use the default SRC, failing MTMS, and ext user Header sections
278 auto src = pelDataFactory(TestPELType::primarySRCSection2Callouts);
279
280 data.insert(data.end(), src.begin(), src.end());
281 data.insert(data.end(), failingMTMSSection.begin(),
282 failingMTMSSection.end());
283 data.insert(data.end(), ExtUserHeaderSection.begin(),
284 ExtUserHeaderSection.end());
285
286 data.at(sectionCountOffset) = 5;
287
288 // Require the size to be enough for all the above sections.
289 assert(size >= data.size());
290 assert(size <= 16384);
291
292 // Add a UserData section to get the size we need.
293 auto udSection = UserDataSection;
294 udSection.resize(size - data.size());
295
296 if (!udSection.empty())
297 {
298 // At least has to be 8B for the header
299 assert(udSection.size() >= 8);
300
301 // UD sections must be 4B aligned
302 assert(udSection.size() % 4 == 0);
303
304 // Set the new size in the section heder
305 Stream udStream{udSection};
306 udStream.offset(2);
307 udStream << static_cast<uint16_t>(udSection.size());
308
309 data.insert(data.end(), udSection.begin(), udSection.end());
310 data[sectionCountOffset]++;
311 }
312
313 assert(size == data.size());
314 return data;
315 }
316
srcDataFactory(TestSRCType type)317 std::vector<uint8_t> srcDataFactory(TestSRCType type)
318 {
319 switch (type)
320 {
321 case TestSRCType::fruIdentityStructure:
322 return srcFRUIdentityCallout;
323
324 case TestSRCType::pceIdentityStructure:
325 return srcPCEIdentityCallout;
326
327 case TestSRCType::mruStructure:
328 return srcMRUCallout;
329
330 case TestSRCType::calloutStructureA:
331 {
332 // Add just the FRU identity substructure to the base structure
333 std::vector<uint8_t> data{
334 0xFF, 0x28, 'H', 4, // size, flags, priority, LC length
335 'U', '4', '2', 0x00 // LC
336 };
337
338 data.insert(data.end(), srcFRUIdentityCallout.begin(),
339 srcFRUIdentityCallout.end());
340
341 // The final size
342 data[0] = data.size();
343 return data;
344 }
345 case TestSRCType::calloutStructureB:
346 {
347 // Add all 3 substructures to the base structure
348
349 std::vector<uint8_t> data{
350 0xFF, 0x2F, 'L', 8, // size, flags, priority, LC length
351 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC
352 };
353 data.insert(data.end(), srcFRUIdentityCallout.begin(),
354 srcFRUIdentityCallout.end());
355 data.insert(data.end(), srcPCEIdentityCallout.begin(),
356 srcPCEIdentityCallout.end());
357 data.insert(data.end(), srcMRUCallout.begin(), srcMRUCallout.end());
358
359 // The final size
360 data[0] = data.size();
361 return data;
362 }
363 case TestSRCType::calloutSection2Callouts:
364 {
365 std::vector<uint8_t> data{0xC0, 0x00, 0x00,
366 0x00}; // ID, flags, length in words
367
368 // Add 2 callouts
369 auto callout = srcDataFactory(TestSRCType::calloutStructureA);
370 data.insert(data.end(), callout.begin(), callout.end());
371
372 callout = srcDataFactory(TestSRCType::calloutStructureB);
373 data.insert(data.end(), callout.begin(), callout.end());
374
375 // Set the actual word length value at offset 2
376 Stream stream{data};
377 uint16_t wordLength = data.size() / 4;
378 stream.offset(2);
379 stream << wordLength;
380 stream.offset(0);
381
382 return data;
383 }
384 }
385 return {};
386 }
387
readPELFile(const fs::path & path)388 std::unique_ptr<std::vector<uint8_t>> readPELFile(const fs::path& path)
389 {
390 std::ifstream file{path};
391
392 auto pel = std::make_unique<std::vector<uint8_t>>(
393 std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
394 return pel;
395 }
396