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 break; 234 case TestPELType::extendedUserDataSection: 235 data.insert(data.end(), extendedUserDataSection.begin(), 236 extendedUserDataSection.end()); 237 break; 238 } 239 return data; 240 } 241 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 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 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