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 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 case TestPELType::extendedUserDataSection: 234 data.insert(data.end(), extendedUserDataSection.begin(), 235 extendedUserDataSection.end()); 236 } 237 return data; 238 } 239 240 std::vector<uint8_t> pelFactory(uint32_t id, char creatorID, uint8_t severity, 241 uint16_t actionFlags, size_t size) 242 { 243 std::vector<uint8_t> data; 244 size_t offset = 0; 245 246 auto now = std::chrono::system_clock::now(); 247 auto timestamp = getBCDTime(now); 248 249 // Start with the default Private Header, and modify it 250 data.insert(data.end(), privateHeaderSection.begin(), 251 privateHeaderSection.end()); 252 data.at(creatorPHOffset) = creatorID; 253 254 // Modify the multibyte fields in it 255 Stream stream{data}; 256 stream.offset(createTimestampPHOffset); 257 stream << timestamp; 258 stream.offset(commitTimestampPHOffset); 259 stream << timestamp; 260 stream.offset(plidPHOffset); 261 stream << id; 262 stream.offset(pelIDPHOffset); 263 stream << id; 264 stream.offset(obmcIDPHOffset); 265 stream << id + 500; 266 267 offset = data.size(); 268 269 // User Header 270 data.insert(data.end(), userHeaderSection.begin(), userHeaderSection.end()); 271 data.at(offset + sevUHOffset) = severity; 272 data.at(offset + actionFlagsUHOffset) = actionFlags >> 8; 273 data.at(offset + actionFlagsUHOffset + 1) = actionFlags; 274 275 // Use the default SRC, failing MTMS, and ext user Header sections 276 auto src = pelDataFactory(TestPELType::primarySRCSection2Callouts); 277 278 data.insert(data.end(), src.begin(), src.end()); 279 data.insert(data.end(), failingMTMSSection.begin(), 280 failingMTMSSection.end()); 281 data.insert(data.end(), ExtUserHeaderSection.begin(), 282 ExtUserHeaderSection.end()); 283 284 data.at(sectionCountOffset) = 5; 285 286 // Require the size to be enough for all the above sections. 287 assert(size >= data.size()); 288 assert(size <= 16384); 289 290 // Add a UserData section to get the size we need. 291 auto udSection = UserDataSection; 292 udSection.resize(size - data.size()); 293 294 if (!udSection.empty()) 295 { 296 // At least has to be 8B for the header 297 assert(udSection.size() >= 8); 298 299 // UD sections must be 4B aligned 300 assert(udSection.size() % 4 == 0); 301 302 // Set the new size in the section heder 303 Stream udStream{udSection}; 304 udStream.offset(2); 305 udStream << static_cast<uint16_t>(udSection.size()); 306 307 data.insert(data.end(), udSection.begin(), udSection.end()); 308 data[sectionCountOffset]++; 309 } 310 311 assert(size == data.size()); 312 return data; 313 } 314 315 std::vector<uint8_t> srcDataFactory(TestSRCType type) 316 { 317 switch (type) 318 { 319 case TestSRCType::fruIdentityStructure: 320 return srcFRUIdentityCallout; 321 322 case TestSRCType::pceIdentityStructure: 323 return srcPCEIdentityCallout; 324 325 case TestSRCType::mruStructure: 326 return srcMRUCallout; 327 328 case TestSRCType::calloutStructureA: 329 { 330 // Add just the FRU identity substructure to the base structure 331 std::vector<uint8_t> data{ 332 0xFF, 0x28, 'H', 4, // size, flags, priority, LC length 333 'U', '4', '2', 0x00 // LC 334 }; 335 336 data.insert(data.end(), srcFRUIdentityCallout.begin(), 337 srcFRUIdentityCallout.end()); 338 339 // The final size 340 data[0] = data.size(); 341 return data; 342 } 343 case TestSRCType::calloutStructureB: 344 { 345 // Add all 3 substructures to the base structure 346 347 std::vector<uint8_t> data{ 348 0xFF, 0x2F, 'L', 8, // size, flags, priority, LC length 349 'U', '1', '2', '-', 'P', '1', 0x00, 0x00 // LC 350 }; 351 data.insert(data.end(), srcFRUIdentityCallout.begin(), 352 srcFRUIdentityCallout.end()); 353 data.insert(data.end(), srcPCEIdentityCallout.begin(), 354 srcPCEIdentityCallout.end()); 355 data.insert(data.end(), srcMRUCallout.begin(), srcMRUCallout.end()); 356 357 // The final size 358 data[0] = data.size(); 359 return data; 360 } 361 case TestSRCType::calloutSection2Callouts: 362 { 363 std::vector<uint8_t> data{0xC0, 0x00, 0x00, 364 0x00}; // ID, flags, length in words 365 366 // Add 2 callouts 367 auto callout = srcDataFactory(TestSRCType::calloutStructureA); 368 data.insert(data.end(), callout.begin(), callout.end()); 369 370 callout = srcDataFactory(TestSRCType::calloutStructureB); 371 data.insert(data.end(), callout.begin(), callout.end()); 372 373 // Set the actual word length value at offset 2 374 Stream stream{data}; 375 uint16_t wordLength = data.size() / 4; 376 stream.offset(2); 377 stream << wordLength; 378 stream.offset(0); 379 380 return data; 381 } 382 } 383 return {}; 384 } 385 386 std::unique_ptr<std::vector<uint8_t>> readPELFile(const fs::path& path) 387 { 388 std::ifstream file{path}; 389 390 auto pel = std::make_unique<std::vector<uint8_t>>( 391 std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); 392 return pel; 393 } 394