1 /* ffsparser.cpp
2
3 Copyright (c) 2018, Nikolaj Schlej. All rights reserved.
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 */
12
13 #include "ffsparser.h"
14
15 #include <map>
16 #include <algorithm>
17 #include <iostream>
18
19 #include "descriptor.h"
20 #include "ffs.h"
21 #include "gbe.h"
22 #include "me.h"
23 #include "fit.h"
24 #include "nvram.h"
25 #include "peimage.h"
26 #include "parsingdata.h"
27 #include "types.h"
28 #include "utility.h"
29
30 #include "nvramparser.h"
31 #include "meparser.h"
32 #include "uinttypes.h"
33
34 #ifndef QT_CORE_LIB
35 namespace Qt {
36 enum GlobalColor {
37 red = 7,
38 green = 8,
39 cyan = 10,
40 yellow = 12,
41 };
42 }
43 #endif
44
45 // Region info
46 struct REGION_INFO {
47 UINT32 offset;
48 UINT32 length;
49 UINT8 type;
50 UByteArray data;
operator <(const REGION_INFO & lhs,const REGION_INFO & rhs)51 friend bool operator< (const REGION_INFO & lhs, const REGION_INFO & rhs){ return lhs.offset < rhs.offset; }
52 };
53
54 // BPDT partition info
55 struct BPDT_PARTITION_INFO {
56 BPDT_ENTRY ptEntry;
57 UINT8 type;
58 UModelIndex index;
operator <(const BPDT_PARTITION_INFO & lhs,const BPDT_PARTITION_INFO & rhs)59 friend bool operator< (const BPDT_PARTITION_INFO & lhs, const BPDT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; }
60 };
61
62 // CPD partition info
63 struct CPD_PARTITION_INFO {
64 CPD_ENTRY ptEntry;
65 UINT8 type;
66 bool hasMetaData;
67 UModelIndex index;
operator <(const CPD_PARTITION_INFO & lhs,const CPD_PARTITION_INFO & rhs)68 friend bool operator< (const CPD_PARTITION_INFO & lhs, const CPD_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset.Offset < rhs.ptEntry.Offset.Offset; }
69 };
70
71 // Constructor
FfsParser(TreeModel * treeModel)72 FfsParser::FfsParser(TreeModel* treeModel) : model(treeModel),
73 imageBase(0), addressDiff(0x100000000ULL),
74 bgAcmFound(false), bgKeyManifestFound(false), bgBootPolicyFound(false), bgProtectedRegionsBase(0) {
75 nvramParser = new NvramParser(treeModel, this);
76 meParser = new MeParser(treeModel, this);
77 }
78
79 // Destructor
~FfsParser()80 FfsParser::~FfsParser() {
81 delete nvramParser;
82 delete meParser;
83 }
84
85 // Obtain parser messages
getMessages() const86 std::vector<std::pair<UString, UModelIndex> > FfsParser::getMessages() const {
87 std::vector<std::pair<UString, UModelIndex> > meVector = meParser->getMessages();
88 std::vector<std::pair<UString, UModelIndex> > nvramVector = nvramParser->getMessages();
89 std::vector<std::pair<UString, UModelIndex> > resultVector = messagesVector;
90 resultVector.insert(resultVector.end(), meVector.begin(), meVector.end());
91 resultVector.insert(resultVector.end(), nvramVector.begin(), nvramVector.end());
92 return resultVector;
93 }
94
95 // Firmware image parsing functions
parse(const UByteArray & buffer)96 USTATUS FfsParser::parse(const UByteArray & buffer)
97 {
98 UModelIndex root;
99
100 // Reset global parser state
101 openedImage = buffer;
102 imageBase = 0;
103 addressDiff = 0x100000000ULL;
104 bgAcmFound = false;
105 bgKeyManifestFound = false;
106 bgBootPolicyFound = false;
107 bgProtectedRegionsBase = 0;
108 lastVtf = UModelIndex();
109 fitTable.clear();
110 securityInfo = "";
111 bgAcmFound = false;
112 bgKeyManifestFound = false;
113 bgBootPolicyFound = false;
114 bgKmHash = UByteArray();
115 bgBpHash = UByteArray();
116 bgBpDigest = UByteArray();
117 bgProtectedRanges.clear();
118 bgDxeCoreIndex = UModelIndex();
119
120 // Parse input buffer
121 USTATUS result = performFirstPass(buffer, root);
122 if (result == U_SUCCESS) {
123 if (lastVtf.isValid()) {
124 result = performSecondPass(root);
125 }
126 else {
127 msg(usprintf("%s: not a single Volume Top File is found, the image may be corrupted", __FUNCTION__));
128 }
129 }
130
131 addInfoRecursive(root);
132 return result;
133 }
134
performFirstPass(const UByteArray & buffer,UModelIndex & index)135 USTATUS FfsParser::performFirstPass(const UByteArray & buffer, UModelIndex & index)
136 {
137 // Sanity check
138 if (buffer.isEmpty()) {
139 return EFI_INVALID_PARAMETER;
140 }
141
142 USTATUS result;
143
144 // Try parsing as UEFI Capsule
145 result = parseCapsule(buffer, 0, UModelIndex(), index);;
146 if (result != U_ITEM_NOT_FOUND) {
147 return result;
148 }
149
150 // Try parsing as Intel image
151 result = parseIntelImage(buffer, 0, UModelIndex(), index);
152 if (result != U_ITEM_NOT_FOUND) {
153 return result;
154 }
155
156 // Parse as generic image
157 return parseGenericImage(buffer, 0, UModelIndex(), index);
158 }
159
parseGenericImage(const UByteArray & buffer,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)160 USTATUS FfsParser::parseGenericImage(const UByteArray & buffer, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
161 {
162 // Parse as generic UEFI image
163 UString name("UEFI image");
164 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", buffer.size(), buffer.size());
165
166 // Add tree item
167 index = model->addItem(localOffset, Types::Image, Subtypes::UefiImage, name, UString(), info, UByteArray(), buffer, UByteArray(), Fixed, parent);
168
169 // Parse the image as raw area
170 bgProtectedRegionsBase = imageBase = model->base(parent) + localOffset;
171 return parseRawArea(index);
172 }
173
parseCapsule(const UByteArray & capsule,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)174 USTATUS FfsParser::parseCapsule(const UByteArray & capsule, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
175 {
176 // Check buffer size to be more than or equal to size of EFI_CAPSULE_HEADER
177 if ((UINT32)capsule.size() < sizeof(EFI_CAPSULE_HEADER)) {
178 return U_ITEM_NOT_FOUND;
179 }
180
181 UINT32 capsuleHeaderSize = 0;
182 // Check buffer for being normal EFI capsule header
183 if (capsule.startsWith(EFI_CAPSULE_GUID)
184 || capsule.startsWith(EFI_FMP_CAPSULE_GUID)
185 || capsule.startsWith(INTEL_CAPSULE_GUID)
186 || capsule.startsWith(LENOVO_CAPSULE_GUID)
187 || capsule.startsWith(LENOVO2_CAPSULE_GUID)) {
188 // Get info
189 const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)capsule.constData();
190
191 // Check sanity of HeaderSize and CapsuleImageSize values
192 if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)capsule.size()
193 || capsuleHeader->HeaderSize > capsuleHeader->CapsuleImageSize) {
194 msg(usprintf("%s: UEFI capsule header size of %Xh (%u) bytes is invalid", __FUNCTION__,
195 capsuleHeader->HeaderSize,
196 capsuleHeader->HeaderSize));
197 return U_INVALID_CAPSULE;
198 }
199 if (capsuleHeader->CapsuleImageSize > (UINT32)capsule.size()) {
200 msg(usprintf("%s: UEFI capsule image size of %Xh (%u) bytes is invalid", __FUNCTION__,
201 capsuleHeader->CapsuleImageSize,
202 capsuleHeader->CapsuleImageSize));
203 return U_INVALID_CAPSULE;
204 }
205
206 capsuleHeaderSize = capsuleHeader->HeaderSize;
207 UByteArray header = capsule.left(capsuleHeaderSize);
208 UByteArray body = capsule.mid(capsuleHeaderSize);
209 UString name("UEFI capsule");
210 UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleGuid, false) +
211 usprintf("\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh",
212 capsule.size(), capsule.size(),
213 capsuleHeaderSize, capsuleHeaderSize,
214 capsuleHeader->CapsuleImageSize - capsuleHeaderSize, capsuleHeader->CapsuleImageSize - capsuleHeaderSize,
215 capsuleHeader->Flags);
216
217 // Add tree item
218 index = model->addItem(localOffset, Types::Capsule, Subtypes::UefiCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent);
219 }
220 // Check buffer for being Toshiba capsule header
221 else if (capsule.startsWith(TOSHIBA_CAPSULE_GUID)) {
222 // Get info
223 const TOSHIBA_CAPSULE_HEADER* capsuleHeader = (const TOSHIBA_CAPSULE_HEADER*)capsule.constData();
224
225 // Check sanity of HeaderSize and FullSize values
226 if (capsuleHeader->HeaderSize == 0 || capsuleHeader->HeaderSize > (UINT32)capsule.size()
227 || capsuleHeader->HeaderSize > capsuleHeader->FullSize) {
228 msg(usprintf("%s: Toshiba capsule header size of %Xh (%u) bytes is invalid", __FUNCTION__,
229 capsuleHeader->HeaderSize, capsuleHeader->HeaderSize));
230 return U_INVALID_CAPSULE;
231 }
232 if (capsuleHeader->FullSize > (UINT32)capsule.size()) {
233 msg(usprintf("%s: Toshiba capsule full size of %Xh (%u) bytes is invalid", __FUNCTION__,
234 capsuleHeader->FullSize, capsuleHeader->FullSize));
235 return U_INVALID_CAPSULE;
236 }
237
238 capsuleHeaderSize = capsuleHeader->HeaderSize;
239 UByteArray header = capsule.left(capsuleHeaderSize);
240 UByteArray body = capsule.mid(capsuleHeaderSize);
241 UString name("Toshiba capsule");
242 UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleGuid, false) +
243 usprintf("\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh",
244 capsule.size(), capsule.size(),
245 capsuleHeaderSize, capsuleHeaderSize,
246 capsuleHeader->FullSize - capsuleHeaderSize, capsuleHeader->FullSize - capsuleHeaderSize,
247 capsuleHeader->Flags);
248
249 // Add tree item
250 index = model->addItem(localOffset, Types::Capsule, Subtypes::ToshibaCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent);
251 }
252 // Check buffer for being extended Aptio capsule header
253 else if (capsule.startsWith(APTIO_SIGNED_CAPSULE_GUID)
254 || capsule.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) {
255 bool signedCapsule = capsule.startsWith(APTIO_SIGNED_CAPSULE_GUID);
256
257 if ((UINT32)capsule.size() <= sizeof(APTIO_CAPSULE_HEADER)) {
258 msg(usprintf("%s: AMI capsule image file is smaller than minimum size of 20h (32) bytes", __FUNCTION__));
259 return U_INVALID_CAPSULE;
260 }
261
262 // Get info
263 const APTIO_CAPSULE_HEADER* capsuleHeader = (const APTIO_CAPSULE_HEADER*)capsule.constData();
264
265 // Check sanity of RomImageOffset and CapsuleImageSize values
266 if (capsuleHeader->RomImageOffset == 0 || capsuleHeader->RomImageOffset > (UINT32)capsule.size()
267 || capsuleHeader->RomImageOffset > capsuleHeader->CapsuleHeader.CapsuleImageSize) {
268 msg(usprintf("%s: AMI capsule image offset of %Xh (%u) bytes is invalid", __FUNCTION__,
269 capsuleHeader->RomImageOffset, capsuleHeader->RomImageOffset));
270 return U_INVALID_CAPSULE;
271 }
272 if (capsuleHeader->CapsuleHeader.CapsuleImageSize > (UINT32)capsule.size()) {
273 msg(usprintf("%s: AMI capsule image size of %Xh (%u) bytes is invalid", __FUNCTION__,
274 capsuleHeader->CapsuleHeader.CapsuleImageSize,
275 capsuleHeader->CapsuleHeader.CapsuleImageSize));
276 return U_INVALID_CAPSULE;
277 }
278
279 capsuleHeaderSize = capsuleHeader->RomImageOffset;
280 UByteArray header = capsule.left(capsuleHeaderSize);
281 UByteArray body = capsule.mid(capsuleHeaderSize);
282 UString name("AMI Aptio capsule");
283 UString info = UString("Capsule GUID: ") + guidToUString(capsuleHeader->CapsuleHeader.CapsuleGuid, false) +
284 usprintf("\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %Xh (%u)\nImage size: %Xh (%u)\nFlags: %08Xh",
285 capsule.size(), capsule.size(),
286 capsuleHeaderSize, capsuleHeaderSize,
287 capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize, capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize,
288 capsuleHeader->CapsuleHeader.Flags);
289
290 // Add tree item
291 index = model->addItem(localOffset, Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, UString(), info, header, body, UByteArray(), Fixed, parent);
292
293 // Show message about possible Aptio signature break
294 if (signedCapsule) {
295 msg(usprintf("%s: Aptio capsule signature may become invalid after image modifications", __FUNCTION__), index);
296 }
297 }
298
299 // Capsule present
300 if (capsuleHeaderSize > 0) {
301 UByteArray image = capsule.mid(capsuleHeaderSize);
302 UModelIndex imageIndex;
303
304 // Try parsing as Intel image
305 USTATUS result = parseIntelImage(image, capsuleHeaderSize, index, imageIndex);
306 if (result != U_ITEM_NOT_FOUND) {
307 return result;
308 }
309
310 // Parse as generic image
311 return parseGenericImage(image, capsuleHeaderSize, index, imageIndex);
312 }
313
314 return U_ITEM_NOT_FOUND;
315 }
316
parseIntelImage(const UByteArray & intelImage,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)317 USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
318 {
319 // Check for buffer size to be greater or equal to descriptor region size
320 if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) {
321 msg(usprintf("%s: input file is smaller than minimum descriptor size of %Xh (%u) bytes", __FUNCTION__, FLASH_DESCRIPTOR_SIZE, FLASH_DESCRIPTOR_SIZE));
322 return U_ITEM_NOT_FOUND;
323 }
324
325 // Store the beginning of descriptor as descriptor base address
326 const FLASH_DESCRIPTOR_HEADER* descriptor = (const FLASH_DESCRIPTOR_HEADER*)intelImage.constData();
327
328 // Check descriptor signature
329 if (descriptor->Signature != FLASH_DESCRIPTOR_SIGNATURE) {
330 return U_ITEM_NOT_FOUND;
331 }
332
333 // Parse descriptor map
334 const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)((UINT8*)descriptor + sizeof(FLASH_DESCRIPTOR_HEADER));
335 const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)((UINT8*)descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE);
336
337 // Check sanity of base values
338 if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE
339 || descriptorMap->MasterBase == descriptorMap->RegionBase
340 || descriptorMap->MasterBase == descriptorMap->ComponentBase) {
341 msg(usprintf("%s: invalid descriptor master base %02Xh", __FUNCTION__, descriptorMap->MasterBase));
342 return U_INVALID_FLASH_DESCRIPTOR;
343 }
344 if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE
345 || descriptorMap->RegionBase == descriptorMap->ComponentBase) {
346 msg(usprintf("%s: invalid descriptor region base %02Xh", __FUNCTION__, descriptorMap->RegionBase));
347 return U_INVALID_FLASH_DESCRIPTOR;
348 }
349 if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) {
350 msg(usprintf("%s: invalid descriptor component base %02Xh", __FUNCTION__, descriptorMap->ComponentBase));
351 return U_INVALID_FLASH_DESCRIPTOR;
352 }
353
354 const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((UINT8*)descriptor, descriptorMap->RegionBase);
355 const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8((UINT8*)descriptor, descriptorMap->ComponentBase);
356
357 UINT8 descriptorVersion = 2;
358 // Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency
359 if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ)
360 descriptorVersion = 1;
361
362 // Regions
363 std::vector<REGION_INFO> regions;
364
365 // ME region
366 REGION_INFO me;
367 me.type = Subtypes::MeRegion;
368 me.offset = 0;
369 me.length = 0;
370 if (regionSection->MeLimit) {
371 me.offset = calculateRegionOffset(regionSection->MeBase);
372 me.length = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit);
373 me.data = intelImage.mid(me.offset, me.length);
374 regions.push_back(me);
375 }
376
377 // BIOS region
378 if (regionSection->BiosLimit) {
379 REGION_INFO bios;
380 bios.type = Subtypes::BiosRegion;
381 bios.offset = calculateRegionOffset(regionSection->BiosBase);
382 bios.length = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit);
383
384 // Check for Gigabyte specific descriptor map
385 if (bios.length == (UINT32)intelImage.size()) {
386 if (!me.offset) {
387 msg(usprintf("%s: can't determine BIOS region start from Gigabyte-specific descriptor", __FUNCTION__));
388 return U_INVALID_FLASH_DESCRIPTOR;
389 }
390 // Use ME region end as BIOS region offset
391 bios.offset = me.offset + me.length;
392 bios.length = (UINT32)intelImage.size() - bios.offset;
393 bios.data = intelImage.mid(bios.offset, bios.length);
394 }
395 // Normal descriptor map
396 else {
397 bios.data = intelImage.mid(bios.offset, bios.length);
398 }
399
400 regions.push_back(bios);
401 }
402 else {
403 msg(usprintf("%s: descriptor parsing failed, BIOS region not found in descriptor", __FUNCTION__));
404 return U_INVALID_FLASH_DESCRIPTOR;
405 }
406
407 // Add all other regions
408 for (UINT8 i = Subtypes::GbeRegion; i <= Subtypes::PttRegion; i++) {
409 if (descriptorVersion == 1 && i == Subtypes::MicrocodeRegion)
410 break; // Do not parse Microcode and other following regions for legacy descriptors
411
412 const UINT16* RegionBase = ((const UINT16*)regionSection) + 2 * i;
413 const UINT16* RegionLimit = ((const UINT16*)regionSection) + 2 * i + 1;
414 if (*RegionLimit && !(*RegionBase == 0xFFFF && *RegionLimit == 0xFFFF)) {
415 REGION_INFO region;
416 region.type = i;
417 region.offset = calculateRegionOffset(*RegionBase);
418 region.length = calculateRegionSize(*RegionBase, *RegionLimit);
419 if (region.length != 0) {
420 region.data = intelImage.mid(region.offset, region.length);
421 regions.push_back(region);
422 }
423 }
424 }
425
426 // Sort regions in ascending order
427 std::sort(regions.begin(), regions.end());
428
429 // Check for intersections and paddings between regions
430 REGION_INFO region;
431 // Check intersection with the descriptor
432 if (regions.front().offset < FLASH_DESCRIPTOR_SIZE) {
433 msg(usprintf("%s: ", __FUNCTION__) + itemSubtypeToUString(Types::Region, regions.front().type)
434 + UString(" region has intersection with flash descriptor"),
435 index);
436 return U_INVALID_FLASH_DESCRIPTOR;
437 }
438 // Check for padding between descriptor and the first region
439 else if (regions.front().offset > FLASH_DESCRIPTOR_SIZE) {
440 region.offset = FLASH_DESCRIPTOR_SIZE;
441 region.length = regions.front().offset - FLASH_DESCRIPTOR_SIZE;
442 region.data = intelImage.mid(region.offset, region.length);
443 region.type = getPaddingType(region.data);
444 regions.insert(regions.begin(), region);
445 }
446 // Check for intersections/paddings between regions
447 for (size_t i = 1; i < regions.size(); i++) {
448 UINT32 previousRegionEnd = regions[i-1].offset + regions[i-1].length;
449 // Check that current region is fully present in the image
450 if ((UINT64)regions[i].offset + (UINT64)regions[i].length > (UINT64)intelImage.size()) {
451 msg(usprintf("%s: ", __FUNCTION__) + itemSubtypeToUString(Types::Region, regions[i].type)
452 + UString(" region is located outside of the opened image. If your system uses dual-chip storage, please append another part to the opened image"),
453 index);
454 return U_TRUNCATED_IMAGE;
455 }
456
457 // Check for intersection with previous region
458 if (regions[i].offset < previousRegionEnd) {
459 msg(usprintf("%s: ", __FUNCTION__) + itemSubtypeToUString(Types::Region, regions[i].type)
460 + UString(" region has intersection with ") + itemSubtypeToUString(Types::Region, regions[i - 1].type) +UString(" region"),
461 index);
462 return U_INVALID_FLASH_DESCRIPTOR;
463 }
464 // Check for padding between current and previous regions
465 else if (regions[i].offset > previousRegionEnd) {
466 region.offset = previousRegionEnd;
467 region.length = regions[i].offset - previousRegionEnd;
468 region.data = intelImage.mid(region.offset, region.length);
469 region.type = getPaddingType(region.data);
470 std::vector<REGION_INFO>::iterator iter = regions.begin();
471 std::advance(iter, i);
472 regions.insert(iter, region);
473 }
474 }
475 // Check for padding after the last region
476 if ((UINT64)regions.back().offset + (UINT64)regions.back().length < (UINT64)intelImage.size()) {
477 region.offset = regions.back().offset + regions.back().length;
478 region.length = (UINT32)(intelImage.size() - region.offset);
479 region.data = intelImage.mid(region.offset, region.length);
480 region.type = getPaddingType(region.data);
481 regions.push_back(region);
482 }
483
484 // Region map is consistent
485
486 // Intel image
487 UString name("Intel image");
488 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nFlash chips: %u\nRegions: %u\nMasters: %u\nPCH straps: %u\nPROC straps: %u",
489 intelImage.size(), intelImage.size(),
490 descriptorMap->NumberOfFlashChips + 1, //
491 descriptorMap->NumberOfRegions + 1, // Zero-based numbers in storage
492 descriptorMap->NumberOfMasters + 1, //
493 descriptorMap->NumberOfPchStraps,
494 descriptorMap->NumberOfProcStraps);
495
496 // Set image base
497 imageBase = model->base(parent) + localOffset;
498
499 // Add Intel image tree item
500 index = model->addItem(localOffset, Types::Image, Subtypes::IntelImage, name, UString(), info, UByteArray(), intelImage, UByteArray(), Fixed, parent);
501
502 // Descriptor
503 // Get descriptor info
504 UByteArray body = intelImage.left(FLASH_DESCRIPTOR_SIZE);
505 name = UString("Descriptor region");
506 info = usprintf("ReservedVector:\n%02X %02X %02X %02X %02X %02X %02X %02X\n"
507 "%02X %02X %02X %02X %02X %02X %02X %02X\nFull size: %Xh (%u)",
508 descriptor->ReservedVector[0], descriptor->ReservedVector[1], descriptor->ReservedVector[2], descriptor->ReservedVector[3],
509 descriptor->ReservedVector[4], descriptor->ReservedVector[5], descriptor->ReservedVector[6], descriptor->ReservedVector[7],
510 descriptor->ReservedVector[8], descriptor->ReservedVector[9], descriptor->ReservedVector[10], descriptor->ReservedVector[11],
511 descriptor->ReservedVector[12], descriptor->ReservedVector[13], descriptor->ReservedVector[14], descriptor->ReservedVector[15],
512 FLASH_DESCRIPTOR_SIZE, FLASH_DESCRIPTOR_SIZE);
513
514 // Add offsets of actual regions
515 for (size_t i = 0; i < regions.size(); i++) {
516 if (regions[i].type != Subtypes::ZeroPadding && regions[i].type != Subtypes::OnePadding && regions[i].type != Subtypes::DataPadding)
517 info += UString("\n") + itemSubtypeToUString(Types::Region, regions[i].type)
518 + usprintf(" region offset: %Xh", regions[i].offset + localOffset);
519 }
520
521 // Region access settings
522 if (descriptorVersion == 1) {
523 const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8((UINT8*)descriptor, descriptorMap->MasterBase);
524 info += UString("\nRegion access settings:");
525 info += usprintf("\nBIOS: %02Xh %02Xh ME: %02Xh %02Xh\nGbE: %02Xh %02Xh",
526 masterSection->BiosRead,
527 masterSection->BiosWrite,
528 masterSection->MeRead,
529 masterSection->MeWrite,
530 masterSection->GbeRead,
531 masterSection->GbeWrite);
532
533 // BIOS access table
534 info += UString("\nBIOS access table:")
535 + UString("\n Read Write")
536 + usprintf("\nDesc %s %s", masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ",
537 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ");
538 info += UString("\nBIOS Yes Yes")
539 + usprintf("\nME %s %s", masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ",
540 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ");
541 info += usprintf("\nGbE %s %s", masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ",
542 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ");
543 info += usprintf("\nPDR %s %s", masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ",
544 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ");
545 }
546 else if (descriptorVersion == 2) {
547 const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8((UINT8*)descriptor, descriptorMap->MasterBase);
548 info += UString("\nRegion access settings:");
549 info += usprintf("\nBIOS: %03Xh %03Xh ME: %03Xh %03Xh\nGbE: %03Xh %03Xh EC: %03Xh %03Xh",
550 masterSection->BiosRead,
551 masterSection->BiosWrite,
552 masterSection->MeRead,
553 masterSection->MeWrite,
554 masterSection->GbeRead,
555 masterSection->GbeWrite,
556 masterSection->EcRead,
557 masterSection->EcWrite);
558
559 // BIOS access table
560 info += UString("\nBIOS access table:")
561 + UString("\n Read Write")
562 + usprintf("\nDesc %s %s",
563 masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ",
564 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ");
565 info += UString("\nBIOS Yes Yes")
566 + usprintf("\nME %s %s",
567 masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ",
568 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ");
569 info += usprintf("\nGbE %s %s",
570 masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ",
571 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ");
572 info += usprintf("\nPDR %s %s",
573 masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ",
574 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ");
575 info += usprintf("\nEC %s %s",
576 masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No ",
577 masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No ");
578
579 // Prepend descriptor version if present
580 if (descriptorMap->DescriptorVersion != FLASH_DESCRIPTOR_VERSION_INVALID) {
581 const FLASH_DESCRIPTOR_VERSION* version = (const FLASH_DESCRIPTOR_VERSION*)&descriptorMap->DescriptorVersion;
582 UString versionStr = usprintf("Flash descriptor version: %d.%d", version->Major, version->Minor);
583 if (version->Major != FLASH_DESCRIPTOR_VERSION_MAJOR || version->Minor != FLASH_DESCRIPTOR_VERSION_MINOR) {
584 versionStr += ", unknown";
585 msg(usprintf("%s: unknown flash descriptor version %d.%d", __FUNCTION__, version->Major, version->Minor));
586 }
587 info = versionStr + "\n" + info;
588 }
589 }
590
591 // VSCC table
592 const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)((UINT8*)descriptor + ((UINT16)upperMap->VsccTableBase << 4));
593 info += UString("\nFlash chips in VSCC table:");
594 UINT8 vsscTableSize = upperMap->VsccTableSize * sizeof(UINT32) / sizeof(VSCC_TABLE_ENTRY);
595 for (UINT8 i = 0; i < vsscTableSize; i++) {
596 UString jedecId = jedecIdToUString(vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1);
597 info += usprintf("\n%02X%02X%02X (", vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1)
598 + jedecId
599 + UString(")");
600 if (jedecId == UString("Unknown")) {
601 msg(usprintf("%s: SPI flash with unknown JEDEC ID %02X%02X%02X found in VSCC table", __FUNCTION__,
602 vsccTableEntry->VendorId, vsccTableEntry->DeviceId0, vsccTableEntry->DeviceId1), index);
603 }
604 vsccTableEntry++;
605 }
606
607 // Add descriptor tree item
608 UModelIndex regionIndex = model->addItem(localOffset, Types::Region, Subtypes::DescriptorRegion, name, UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
609
610
611 // Parse regions
612 USTATUS result = U_SUCCESS;
613 USTATUS parseResult = U_SUCCESS;
614 for (size_t i = 0; i < regions.size(); i++) {
615 region = regions[i];
616 switch (region.type) {
617 case Subtypes::BiosRegion:
618 result = parseBiosRegion(region.data, region.offset, index, regionIndex);
619 break;
620 case Subtypes::MeRegion:
621 result = parseMeRegion(region.data, region.offset, index, regionIndex);
622 break;
623 case Subtypes::GbeRegion:
624 result = parseGbeRegion(region.data, region.offset, index, regionIndex);
625 break;
626 case Subtypes::PdrRegion:
627 result = parsePdrRegion(region.data, region.offset, index, regionIndex);
628 break;
629 case Subtypes::DevExp1Region:
630 result = parseDevExp1Region(region.data, region.offset, index, regionIndex);
631 break;
632 case Subtypes::Bios2Region:
633 case Subtypes::MicrocodeRegion:
634 case Subtypes::EcRegion:
635 case Subtypes::DevExp2Region:
636 case Subtypes::IeRegion:
637 case Subtypes::Tgbe1Region:
638 case Subtypes::Tgbe2Region:
639 case Subtypes::Reserved1Region:
640 case Subtypes::Reserved2Region:
641 case Subtypes::PttRegion:
642 result = parseGenericRegion(region.type, region.data, region.offset, index, regionIndex);
643 break;
644 case Subtypes::ZeroPadding:
645 case Subtypes::OnePadding:
646 case Subtypes::DataPadding: {
647 // Add padding between regions
648 UByteArray padding = intelImage.mid(region.offset, region.length);
649
650 // Get info
651 name = UString("Padding");
652 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")",
653 padding.size(), padding.size());
654
655 // Add tree item
656 regionIndex = model->addItem(region.offset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
657 result = U_SUCCESS;
658 } break;
659 default:
660 msg(usprintf("%s: region of unknown type found", __FUNCTION__), index);
661 result = U_INVALID_FLASH_DESCRIPTOR;
662 }
663 // Store the first failed result as a final result
664 if (!parseResult && result) {
665 parseResult = result;
666 }
667 }
668
669 return parseResult;
670 }
671
parseGbeRegion(const UByteArray & gbe,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)672 USTATUS FfsParser::parseGbeRegion(const UByteArray & gbe, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
673 {
674 // Check sanity
675 if (gbe.isEmpty())
676 return U_EMPTY_REGION;
677 if ((UINT32)gbe.size() < GBE_VERSION_OFFSET + sizeof(GBE_VERSION))
678 return U_INVALID_REGION;
679
680 // Get info
681 UString name("GbE region");
682 const GBE_MAC_ADDRESS* mac = (const GBE_MAC_ADDRESS*)gbe.constData();
683 const GBE_VERSION* version = (const GBE_VERSION*)(gbe.constData() + GBE_VERSION_OFFSET);
684 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nMAC: %02X:%02X:%02X:%02X:%02X:%02X\nVersion: %u.%u",
685 gbe.size(), gbe.size(),
686 mac->vendor[0], mac->vendor[1], mac->vendor[2],
687 mac->device[0], mac->device[1], mac->device[2],
688 version->major,
689 version->minor);
690
691 // Add tree item
692 index = model->addItem(localOffset, Types::Region, Subtypes::GbeRegion, name, UString(), info, UByteArray(), gbe, UByteArray(), Fixed, parent);
693
694 return U_SUCCESS;
695 }
696
parseMeRegion(const UByteArray & me,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)697 USTATUS FfsParser::parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
698 {
699 // Check sanity
700 if (me.isEmpty())
701 return U_EMPTY_REGION;
702
703 // Get info
704 UString name("ME region");
705 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", me.size(), me.size());
706
707 // Parse region
708 bool versionFound = true;
709 bool emptyRegion = false;
710 // Check for empty region
711 if (me.size() == me.count('\xFF') || me.size() == me.count('\x00')) {
712 // Further parsing not needed
713 emptyRegion = true;
714 info += ("\nState: empty");
715 }
716 else {
717 // Search for new signature
718 INT32 versionOffset = (INT32)me.indexOf(ME_VERSION_SIGNATURE2);
719 if (versionOffset < 0){ // New signature not found
720 // Search for old signature
721 versionOffset = (INT32)me.indexOf(ME_VERSION_SIGNATURE);
722 if (versionOffset < 0){
723 info += ("\nVersion: unknown");
724 versionFound = false;
725 }
726 }
727
728 // Check sanity
729 if ((UINT32)me.size() < (UINT32)versionOffset + sizeof(ME_VERSION))
730 return U_INVALID_REGION;
731
732 // Add version information
733 if (versionFound) {
734 const ME_VERSION* version = (const ME_VERSION*)(me.constData() + versionOffset);
735 info += usprintf("\nVersion: %u.%u.%u.%u",
736 version->Major,
737 version->Minor,
738 version->Bugfix,
739 version->Build);
740 }
741 }
742
743 // Add tree item
744 index = model->addItem(localOffset, Types::Region, Subtypes::MeRegion, name, UString(), info, UByteArray(), me, UByteArray(), Fixed, parent);
745
746 // Show messages
747 if (emptyRegion) {
748 msg(usprintf("%s: ME region is empty", __FUNCTION__), index);
749 }
750 else if (!versionFound) {
751 msg(usprintf("%s: ME version is unknown, it can be damaged", __FUNCTION__), index);
752 }
753 else {
754 meParser->parseMeRegionBody(index);
755 }
756
757 return U_SUCCESS;
758 }
759
parsePdrRegion(const UByteArray & pdr,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)760 USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
761 {
762 // Check sanity
763 if (pdr.isEmpty())
764 return U_EMPTY_REGION;
765
766 // Get info
767 UString name("PDR region");
768 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", pdr.size(), pdr.size());
769
770 // Add tree item
771 index = model->addItem(localOffset, Types::Region, Subtypes::PdrRegion, name, UString(), info, UByteArray(), pdr, UByteArray(), Fixed, parent);
772
773 // Parse PDR region as BIOS space
774 USTATUS result = parseRawArea(index);
775 if (result && result != U_VOLUMES_NOT_FOUND && result != U_INVALID_VOLUME && result != U_STORES_NOT_FOUND)
776 return result;
777
778 return U_SUCCESS;
779 }
780
parseDevExp1Region(const UByteArray & devExp1,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)781 USTATUS FfsParser::parseDevExp1Region(const UByteArray & devExp1, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
782 {
783 // Check sanity
784 if (devExp1.isEmpty())
785 return U_EMPTY_REGION;
786
787 // Get info
788 UString name("DevExp1 region");
789 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", devExp1.size(), devExp1.size());
790
791 bool emptyRegion = false;
792 // Check for empty region
793 if (devExp1.size() == devExp1.count('\xFF') || devExp1.size() == devExp1.count('\x00')) {
794 // Further parsing not needed
795 emptyRegion = true;
796 info += ("\nState: empty");
797 }
798
799 // Add tree item
800 index = model->addItem(localOffset, Types::Region, Subtypes::DevExp1Region, name, UString(), info, UByteArray(), devExp1, UByteArray(), Fixed, parent);
801
802 if (!emptyRegion) {
803 meParser->parseMeRegionBody(index);
804 }
805 return U_SUCCESS;
806 }
807
parseGenericRegion(const UINT8 subtype,const UByteArray & region,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)808 USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
809 {
810 // Check sanity
811 if (region.isEmpty())
812 return U_EMPTY_REGION;
813
814 // Get info
815 UString name = itemSubtypeToUString(Types::Region, subtype) + UString(" region");
816 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", region.size(), region.size());
817
818 // Add tree item
819 index = model->addItem(localOffset, Types::Region, subtype, name, UString(), info, UByteArray(), region, UByteArray(), Fixed, parent);
820
821 return U_SUCCESS;
822 }
823
parseBiosRegion(const UByteArray & bios,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)824 USTATUS FfsParser::parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
825 {
826 // Sanity check
827 if (bios.isEmpty())
828 return U_EMPTY_REGION;
829
830 // Get info
831 UString name("BIOS region");
832 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", bios.size(), bios.size());
833
834 // Add tree item
835 index = model->addItem(localOffset, Types::Region, Subtypes::BiosRegion, name, UString(), info, UByteArray(), bios, UByteArray(), Fixed, parent);
836
837 return parseRawArea(index);
838 }
839
parseRawArea(const UModelIndex & index)840 USTATUS FfsParser::parseRawArea(const UModelIndex & index)
841 {
842 // Sanity check
843 if (!index.isValid())
844 return U_INVALID_PARAMETER;
845
846 // Get item data
847 UByteArray data = model->body(index);
848 UINT32 headerSize = (UINT32)model->header(index).size();
849
850 USTATUS result;
851 UString name;
852 UString info;
853
854 // Search for the first item
855 UINT8 prevItemType = 0;
856 UINT32 prevItemOffset = 0;
857 UINT32 prevItemSize = 0;
858 UINT32 prevItemAltSize = 0;
859
860 result = findNextRawAreaItem(index, 0, prevItemType, prevItemOffset, prevItemSize, prevItemAltSize);
861 if (result) {
862 // No need to parse further
863 return U_SUCCESS;
864 }
865
866 // Set base of protected regions to be the first volume
867 if (model->type(index) == Types::Region
868 && model->subtype(index) == Subtypes::BiosRegion) {
869 bgProtectedRegionsBase = (UINT64)model->base(index) + prevItemOffset;
870 }
871
872 // First item is not at the beginning of this raw area
873 if (prevItemOffset > 0) {
874 // Get info
875 UByteArray padding = data.left(prevItemOffset);
876 name = UString("Padding");
877 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
878
879 // Add tree item
880 model->addItem(headerSize, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
881 }
882
883 // Search for and parse all items
884 UINT8 itemType = prevItemType;
885 UINT32 itemOffset = prevItemOffset;
886 UINT32 itemSize = prevItemSize;
887 UINT32 itemAltSize = prevItemAltSize;
888
889 while (!result) {
890 // Padding between items
891 if (itemOffset > prevItemOffset + prevItemSize) {
892 UINT32 paddingOffset = prevItemOffset + prevItemSize;
893 UINT32 paddingSize = itemOffset - paddingOffset;
894 UByteArray padding = data.mid(paddingOffset, paddingSize);
895
896 // Get info
897 name = UString("Padding");
898 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
899
900 // Add tree item
901 model->addItem(headerSize + paddingOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
902 }
903
904 // Check that item is fully present in input
905 if (itemSize > (UINT32)data.size() || itemOffset + itemSize > (UINT32)data.size()) {
906 // Mark the rest as padding and finish parsing
907 UByteArray padding = data.mid(itemOffset);
908
909 // Get info
910 name = UString("Padding");
911 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
912
913 // Add tree item
914 UModelIndex paddingIndex = model->addItem(headerSize + itemOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
915 msg(usprintf("%s: one of volumes inside overlaps the end of data", __FUNCTION__), paddingIndex);
916
917 // Update variables
918 prevItemOffset = itemOffset;
919 prevItemSize = (UINT32)padding.size();
920 break;
921 }
922
923 // Parse current volume's header
924 if (itemType == Types::Volume) {
925 UModelIndex volumeIndex;
926 UByteArray volume = data.mid(itemOffset, itemSize);
927 result = parseVolumeHeader(volume, headerSize + itemOffset, index, volumeIndex);
928 if (result) {
929 msg(usprintf("%s: volume header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
930 } else {
931 // Show messages
932 if (itemSize != itemAltSize)
933 msg(usprintf("%s: volume size stored in header %Xh differs from calculated using block map %Xh", __FUNCTION__,
934 itemSize, itemAltSize),
935 volumeIndex);
936 }
937 }
938 else if (itemType == Types::Microcode) {
939 UModelIndex microcodeIndex;
940 UByteArray microcode = data.mid(itemOffset, itemSize);
941 result = parseIntelMicrocodeHeader(microcode, headerSize + itemOffset, index, microcodeIndex);
942 if (result) {
943 msg(usprintf("%s: microcode header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
944 }
945 }
946 else if (itemType == Types::BpdtStore) {
947 UByteArray bpdtStore = data.mid(itemOffset, itemSize);
948
949 // Get info
950 name = UString("BPDT region");
951 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", bpdtStore.size(), bpdtStore.size());
952
953 // Add tree item
954 UModelIndex bpdtIndex = model->addItem(headerSize + itemOffset, Types::BpdtStore, 0, name, UString(), info, UByteArray(), bpdtStore, UByteArray(), Fixed, index);
955
956 // Parse BPDT region
957 UModelIndex bpdtPtIndex;
958 result = parseBpdtRegion(bpdtStore, 0, 0, bpdtIndex, bpdtPtIndex);
959 if (result) {
960 msg(usprintf("%s: BPDT store parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
961 }
962 }
963 else {
964 return U_UNKNOWN_ITEM_TYPE;
965 }
966
967 // Go to next item
968 prevItemOffset = itemOffset;
969 prevItemSize = itemSize;
970 prevItemType = itemType;
971 result = findNextRawAreaItem(index, itemOffset + prevItemSize, itemType, itemOffset, itemSize, itemAltSize);
972
973 // Silence value not used after assignment warning
974 (void)prevItemType;
975 }
976
977 // Padding at the end of RAW area
978 itemOffset = prevItemOffset + prevItemSize;
979 if ((UINT32)data.size() > itemOffset) {
980 UByteArray padding = data.mid(itemOffset);
981
982 // Get info
983 name = UString("Padding");
984 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
985
986 // Add tree item
987 model->addItem(headerSize + itemOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
988 }
989
990 // Parse bodies
991 for (int i = 0; i < model->rowCount(index); i++) {
992 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
993 UModelIndex current = index.child(i, 0);
994 #else
995 UModelIndex current = index.model()->index(i, 0, index);
996 #endif
997
998 switch (model->type(current)) {
999 case Types::Volume:
1000 parseVolumeBody(current);
1001 break;
1002 case Types::Microcode:
1003 // Parsing already done
1004 break;
1005 case Types::BpdtStore:
1006 // Parsing already done
1007 break;
1008 case Types::BpdtPartition:
1009 // Parsing already done
1010 break;
1011 case Types::Padding:
1012 // No parsing required
1013 break;
1014 default:
1015 return U_UNKNOWN_ITEM_TYPE;
1016 }
1017 }
1018
1019 return U_SUCCESS;
1020 }
1021
parseVolumeHeader(const UByteArray & volume,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1022 USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1023 {
1024 // Sanity check
1025 if (volume.isEmpty())
1026 return U_INVALID_PARAMETER;
1027
1028 // Check that there is space for the volume header
1029 if ((UINT32)volume.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) {
1030 msg(usprintf("%s: input volume size %" PRIXQ "h (%" PRIuQ ") is smaller than volume header size 40h (64)", __FUNCTION__, volume.size(), volume.size()));
1031 return U_INVALID_VOLUME;
1032 }
1033
1034 // Populate volume header
1035 const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData());
1036
1037 // Check sanity of HeaderLength value
1038 if ((UINT32)ALIGN8(volumeHeader->HeaderLength) > (UINT32)volume.size()) {
1039 msg(usprintf("%s: volume header overlaps the end of data", __FUNCTION__));
1040 return U_INVALID_VOLUME;
1041 }
1042 // Check sanity of ExtHeaderOffset value
1043 if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset
1044 && (UINT32)ALIGN8(volumeHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER)) > (UINT32)volume.size()) {
1045 msg(usprintf("%s: extended volume header overlaps the end of data", __FUNCTION__));
1046 return U_INVALID_VOLUME;
1047 }
1048
1049 // Calculate volume header size
1050 UINT32 headerSize;
1051 EFI_GUID extendedHeaderGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0 }};
1052 bool hasExtendedHeader = false;
1053 if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
1054 hasExtendedHeader = true;
1055 const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset);
1056 headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize;
1057 extendedHeaderGuid = extendedHeader->FvName;
1058 }
1059 else {
1060 headerSize = volumeHeader->HeaderLength;
1061 }
1062
1063 // Extended header end can be unaligned
1064 headerSize = ALIGN8(headerSize);
1065
1066 // Check for volume structure to be known
1067 bool isUnknown = true;
1068 bool isNvramVolume = false;
1069 bool isMicrocodeVolume = false;
1070 UINT8 ffsVersion = 0;
1071
1072 // Check for FFS v2 volume
1073 UByteArray guid = UByteArray((const char*)&volumeHeader->FileSystemGuid, sizeof(EFI_GUID));
1074 if (std::find(FFSv2Volumes.begin(), FFSv2Volumes.end(), guid) != FFSv2Volumes.end()) {
1075 isUnknown = false;
1076 ffsVersion = 2;
1077 }
1078 // Check for FFS v3 volume
1079 else if (std::find(FFSv3Volumes.begin(), FFSv3Volumes.end(), guid) != FFSv3Volumes.end()) {
1080 isUnknown = false;
1081 ffsVersion = 3;
1082 }
1083 // Check for VSS NVRAM volume
1084 else if (guid == NVRAM_MAIN_STORE_VOLUME_GUID || guid == NVRAM_ADDITIONAL_STORE_VOLUME_GUID) {
1085 isUnknown = false;
1086 isNvramVolume = true;
1087 }
1088 // Check for Microcode volume
1089 else if (guid == EFI_APPLE_MICROCODE_VOLUME_GUID) {
1090 isUnknown = false;
1091 isMicrocodeVolume = true;
1092 headerSize = EFI_APPLE_MICROCODE_VOLUME_HEADER_SIZE;
1093 }
1094
1095 // Check volume revision and alignment
1096 bool msgAlignmentBitsSet = false;
1097 bool msgUnaligned = false;
1098 bool msgUnknownRevision = false;
1099 UINT32 alignment = 0x10000; // Default volume alignment is 64K
1100 if (volumeHeader->Revision == 1) {
1101 // Acquire alignment capability bit
1102 bool alignmentCap = (volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP) != 0;
1103 if (!alignmentCap) {
1104 if (volumeHeader->Attributes & 0xFFFF0000)
1105 msgAlignmentBitsSet = true;
1106 }
1107 // Do not check for volume alignment on revision 1 volumes
1108 // No one gives a single damn about setting it correctly
1109 }
1110 else if (volumeHeader->Revision == 2) {
1111 // Acquire alignment
1112 alignment = (UINT32)(1UL << ((volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16));
1113 // Check alignment
1114 if (!isUnknown && !model->compressed(parent) && ((model->base(parent) + localOffset - imageBase) % alignment))
1115 msgUnaligned = true;
1116 }
1117 else {
1118 msgUnknownRevision = true;
1119 }
1120
1121 // Check attributes
1122 // Determine value of empty byte
1123 UINT8 emptyByte = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00';
1124
1125 // Check for AppleCRC32 and UsedSpace in ZeroVector
1126 bool hasAppleCrc32 = false;
1127 UINT32 volumeSize = (UINT32)volume.size();
1128 UINT32 appleCrc32 = *(UINT32*)(volume.constData() + 8);
1129 UINT32 usedSpace = *(UINT32*)(volume.constData() + 12);
1130 if (appleCrc32 != 0) {
1131 // Calculate CRC32 of the volume body
1132 UINT32 crc = (UINT32)crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength);
1133 if (crc == appleCrc32) {
1134 hasAppleCrc32 = true;
1135 }
1136 }
1137
1138 // Check header checksum by recalculating it
1139 bool msgInvalidChecksum = false;
1140 UByteArray tempHeader((const char*)volumeHeader, volumeHeader->HeaderLength);
1141 ((EFI_FIRMWARE_VOLUME_HEADER*)tempHeader.data())->Checksum = 0;
1142 UINT16 calculated = calculateChecksum16((const UINT16*)tempHeader.constData(), volumeHeader->HeaderLength);
1143 if (volumeHeader->Checksum != calculated)
1144 msgInvalidChecksum = true;
1145
1146 // Get info
1147 UByteArray header = volume.left(headerSize);
1148 UByteArray body = volume.mid(headerSize);
1149 UString name = guidToUString(volumeHeader->FileSystemGuid);
1150 UString info = usprintf("ZeroVector:\n%02X %02X %02X %02X %02X %02X %02X %02X\n"
1151 "%02X %02X %02X %02X %02X %02X %02X %02X\nSignature: _FVH\nFileSystem GUID: ",
1152 volumeHeader->ZeroVector[0], volumeHeader->ZeroVector[1], volumeHeader->ZeroVector[2], volumeHeader->ZeroVector[3],
1153 volumeHeader->ZeroVector[4], volumeHeader->ZeroVector[5], volumeHeader->ZeroVector[6], volumeHeader->ZeroVector[7],
1154 volumeHeader->ZeroVector[8], volumeHeader->ZeroVector[9], volumeHeader->ZeroVector[10], volumeHeader->ZeroVector[11],
1155 volumeHeader->ZeroVector[12], volumeHeader->ZeroVector[13], volumeHeader->ZeroVector[14], volumeHeader->ZeroVector[15])
1156 + guidToUString(volumeHeader->FileSystemGuid, false) \
1157 + usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nRevision: %u\nAttributes: %08Xh\nErase polarity: %u\nChecksum: %04Xh",
1158 volumeSize, volumeSize,
1159 headerSize, headerSize,
1160 volumeSize - headerSize, volumeSize - headerSize,
1161 volumeHeader->Revision,
1162 volumeHeader->Attributes,
1163 (emptyByte ? 1 : 0),
1164 volumeHeader->Checksum) +
1165 (msgInvalidChecksum ? usprintf(", invalid, should be %04Xh", calculated) : UString(", valid"));
1166
1167 // Extended header present
1168 if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
1169 const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset);
1170 info += usprintf("\nExtended header size: %Xh (%u)\nVolume GUID: ",
1171 extendedHeader->ExtHeaderSize, extendedHeader->ExtHeaderSize) + guidToUString(extendedHeader->FvName, false);
1172 name = guidToUString(extendedHeader->FvName); // Replace FFS GUID with volume GUID
1173 }
1174
1175 // Add text
1176 UString text;
1177 if (hasAppleCrc32)
1178 text += UString("AppleCRC32 ");
1179
1180 // Add tree item
1181 UINT8 subtype = Subtypes::UnknownVolume;
1182 if (!isUnknown) {
1183 if (ffsVersion == 2)
1184 subtype = Subtypes::Ffs2Volume;
1185 else if (ffsVersion == 3)
1186 subtype = Subtypes::Ffs3Volume;
1187 else if (isNvramVolume)
1188 subtype = Subtypes::NvramVolume;
1189 else if (isMicrocodeVolume)
1190 subtype = Subtypes::MicrocodeVolume;
1191 }
1192 index = model->addItem(localOffset, Types::Volume, subtype, name, text, info, header, body, UByteArray(), Movable, parent);
1193
1194 // Set parsing data for created volume
1195 VOLUME_PARSING_DATA pdata;
1196 pdata.emptyByte = emptyByte;
1197 pdata.ffsVersion = ffsVersion;
1198 pdata.hasExtendedHeader = hasExtendedHeader ? TRUE : FALSE;
1199 pdata.extendedHeaderGuid = extendedHeaderGuid;
1200 pdata.alignment = alignment;
1201 pdata.revision = volumeHeader->Revision;
1202 pdata.hasAppleCrc32 = hasAppleCrc32;
1203 pdata.hasValidUsedSpace = FALSE; // Will be updated later, if needed
1204 pdata.usedSpace = usedSpace;
1205 pdata.isWeakAligned = (volumeHeader->Revision > 1 && (volumeHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT));
1206 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
1207
1208 // Show messages
1209 if (isUnknown)
1210 msg(usprintf("%s: unknown file system ", __FUNCTION__) + guidToUString(volumeHeader->FileSystemGuid), index);
1211 if (msgInvalidChecksum)
1212 msg(usprintf("%s: volume header checksum is invalid", __FUNCTION__), index);
1213 if (msgAlignmentBitsSet)
1214 msg(usprintf("%s: alignment bits set on volume without alignment capability", __FUNCTION__), index);
1215 if (msgUnaligned)
1216 msg(usprintf("%s: unaligned volume", __FUNCTION__), index);
1217 if (msgUnknownRevision)
1218 msg(usprintf("%s: unknown volume revision %u", __FUNCTION__, volumeHeader->Revision), index);
1219
1220 return U_SUCCESS;
1221 }
1222
microcodeHeaderValid(const INTEL_MICROCODE_HEADER * ucodeHeader)1223 BOOLEAN FfsParser::microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader)
1224 {
1225 // Check main reserved bytes to be zero
1226 bool reservedBytesValid = true;
1227 for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) {
1228 if (ucodeHeader->Reserved[i] != 0x00) {
1229 reservedBytesValid = false;
1230 break;
1231 }
1232 }
1233 if (!reservedBytesValid) {
1234 return FALSE;
1235 }
1236
1237 // Check CpuFlags reserved bytes to be zero
1238 for (UINT32 i = 0; i < sizeof(ucodeHeader->ProcessorFlagsReserved); i++) {
1239 if (ucodeHeader->ProcessorFlagsReserved[i] != 0x00) {
1240 reservedBytesValid = false;
1241 break;
1242 }
1243 }
1244 if (!reservedBytesValid) {
1245 return FALSE;
1246 }
1247
1248 // Check data size to be multiple of 4 and less than 0x1000000
1249 if (ucodeHeader->DataSize % 4 != 0 ||
1250 ucodeHeader->DataSize > 0xFFFFFF) {
1251 return FALSE;
1252 }
1253
1254 // Check TotalSize to be greater or equal than DataSize and less than 0x1000000
1255 if (ucodeHeader->TotalSize < ucodeHeader->DataSize ||
1256 ucodeHeader->TotalSize > 0xFFFFFF) {
1257 return FALSE;
1258 }
1259
1260 // Check date to be sane
1261 // Check day to be in 0x01-0x09, 0x10-0x19, 0x20-0x29, 0x30-0x31
1262 if (ucodeHeader->DateDay < 0x01 ||
1263 (ucodeHeader->DateDay > 0x09 && ucodeHeader->DateDay < 0x10) ||
1264 (ucodeHeader->DateDay > 0x19 && ucodeHeader->DateDay < 0x20) ||
1265 (ucodeHeader->DateDay > 0x29 && ucodeHeader->DateDay < 0x30) ||
1266 ucodeHeader->DateDay > 0x31) {
1267 return FALSE;
1268 }
1269 // Check month to be in 0x01-0x09, 0x10-0x12
1270 if (ucodeHeader->DateMonth < 0x01 ||
1271 (ucodeHeader->DateMonth > 0x09 && ucodeHeader->DateMonth < 0x10) ||
1272 ucodeHeader->DateMonth > 0x12) {
1273 return FALSE;
1274 }
1275 // Check year to be in 0x1990-0x1999, 0x2000-0x2009, 0x2010-0x2019, 0x2020-0x2029, 0x2030-0x2030, 0x2040-0x2049
1276 if (ucodeHeader->DateYear < 0x1990 ||
1277 (ucodeHeader->DateYear > 0x1999 && ucodeHeader->DateYear < 0x2000) ||
1278 (ucodeHeader->DateYear > 0x2009 && ucodeHeader->DateYear < 0x2010) ||
1279 (ucodeHeader->DateYear > 0x2019 && ucodeHeader->DateYear < 0x2020) ||
1280 (ucodeHeader->DateYear > 0x2029 && ucodeHeader->DateYear < 0x2030) ||
1281 (ucodeHeader->DateYear > 0x2039 && ucodeHeader->DateYear < 0x2040) ||
1282 ucodeHeader->DateYear > 0x2049) {
1283 return FALSE;
1284 }
1285 // Check HeaderVersion to be 1.
1286 if (ucodeHeader->HeaderVersion != 1) {
1287 return FALSE;
1288 }
1289 // Check LoaderRevision to be 1.
1290 if (ucodeHeader->LoaderRevision != 1) {
1291 return FALSE;
1292 }
1293
1294 return TRUE;
1295 }
1296
findNextRawAreaItem(const UModelIndex & index,const UINT32 localOffset,UINT8 & nextItemType,UINT32 & nextItemOffset,UINT32 & nextItemSize,UINT32 & nextItemAlternativeSize)1297 USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize)
1298 {
1299 UByteArray data = model->body(index);
1300 UINT32 dataSize = (UINT32)data.size();
1301
1302 if (dataSize < sizeof(UINT32))
1303 return U_STORES_NOT_FOUND;
1304
1305 UINT32 offset = localOffset;
1306 for (; offset < dataSize - sizeof(UINT32); offset++) {
1307 const UINT32* currentPos = (const UINT32*)(data.constData() + offset);
1308 const UINT32 restSize = dataSize - offset;
1309 if (readUnaligned(currentPos) == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode
1310 // Check data size
1311 if (restSize < sizeof(INTEL_MICROCODE_HEADER)) {
1312 continue;
1313 }
1314
1315 // Check microcode header candidate
1316 const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos;
1317 if (FALSE == microcodeHeaderValid(ucodeHeader)) {
1318 continue;
1319 }
1320
1321 // Check size candidate
1322 if (ucodeHeader->TotalSize == 0)
1323 continue;
1324
1325 // All checks passed, microcode found
1326 nextItemType = Types::Microcode;
1327 nextItemSize = ucodeHeader->TotalSize;
1328 nextItemAlternativeSize = ucodeHeader->TotalSize;
1329 nextItemOffset = offset;
1330 break;
1331 }
1332 else if (readUnaligned(currentPos) == EFI_FV_SIGNATURE) {
1333 if (offset < EFI_FV_SIGNATURE_OFFSET)
1334 continue;
1335
1336 const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(data.constData() + offset - EFI_FV_SIGNATURE_OFFSET);
1337 if (volumeHeader->FvLength < sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY) || volumeHeader->FvLength >= 0xFFFFFFFFUL) {
1338 continue;
1339 }
1340 if (volumeHeader->Revision != 1 && volumeHeader->Revision != 2) {
1341 continue;
1342 }
1343
1344 // Calculate alternative volume size using its BlockMap
1345 nextItemAlternativeSize = 0;
1346 const EFI_FV_BLOCK_MAP_ENTRY* entry = (const EFI_FV_BLOCK_MAP_ENTRY*)(data.constData() + offset - EFI_FV_SIGNATURE_OFFSET + sizeof(EFI_FIRMWARE_VOLUME_HEADER));
1347 while (entry->NumBlocks != 0 && entry->Length != 0) {
1348 if ((void*)entry >= data.constData() + data.size()) {
1349 continue;
1350 }
1351
1352 nextItemAlternativeSize += entry->NumBlocks * entry->Length;
1353 entry += 1;
1354 }
1355
1356 // All checks passed, volume found
1357 nextItemType = Types::Volume;
1358 nextItemSize = (UINT32)volumeHeader->FvLength;
1359 nextItemOffset = offset - EFI_FV_SIGNATURE_OFFSET;
1360 break;
1361 }
1362 else if (readUnaligned(currentPos) == BPDT_GREEN_SIGNATURE || readUnaligned(currentPos) == BPDT_YELLOW_SIGNATURE) {
1363 // Check data size
1364 if (restSize < sizeof(BPDT_HEADER))
1365 continue;
1366
1367 const BPDT_HEADER *bpdtHeader = (const BPDT_HEADER *)currentPos;
1368 // Check version
1369 if (bpdtHeader->HeaderVersion != BPDT_HEADER_VERSION_1) // IFWI 2.0 only for now
1370 continue;
1371
1372 UINT32 ptBodySize = bpdtHeader->NumEntries * sizeof(BPDT_ENTRY);
1373 UINT32 ptSize = sizeof(BPDT_HEADER) + ptBodySize;
1374 // Check data size again
1375 if (restSize < ptSize)
1376 continue;
1377
1378 UINT32 sizeCandidate = 0;
1379 // Parse partition table
1380 const BPDT_ENTRY* firstPtEntry = (const BPDT_ENTRY*)((const UINT8*)bpdtHeader + sizeof(BPDT_HEADER));
1381 for (UINT16 i = 0; i < bpdtHeader->NumEntries; i++) {
1382 // Populate entry header
1383 const BPDT_ENTRY* ptEntry = firstPtEntry + i;
1384 // Check that entry is present in the image
1385 if (ptEntry->Offset != 0
1386 && ptEntry->Offset != 0xFFFFFFFF
1387 && ptEntry->Size != 0
1388 && sizeCandidate < ptEntry->Offset + ptEntry->Size) {
1389 sizeCandidate = ptEntry->Offset + ptEntry->Size;
1390 }
1391 }
1392
1393 // Check size candidate
1394 if (sizeCandidate == 0)
1395 continue;
1396
1397 // All checks passed, BPDT found
1398 nextItemType = Types::BpdtStore;
1399 nextItemSize = sizeCandidate;
1400 nextItemAlternativeSize = sizeCandidate;
1401 nextItemOffset = offset;
1402 break;
1403 }
1404 }
1405
1406 // No more stores found
1407 if (offset >= dataSize - sizeof(UINT32)) {
1408 return U_STORES_NOT_FOUND;
1409 }
1410
1411 return U_SUCCESS;
1412 }
1413
parseVolumeNonUefiData(const UByteArray & data,const UINT32 localOffset,const UModelIndex & index)1414 USTATUS FfsParser::parseVolumeNonUefiData(const UByteArray & data, const UINT32 localOffset, const UModelIndex & index)
1415 {
1416 // Sanity check
1417 if (!index.isValid())
1418 return U_INVALID_PARAMETER;
1419
1420 // Get info
1421 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", data.size(), data.size());
1422
1423 // Add padding tree item
1424 UModelIndex paddingIndex = model->addItem(localOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), data, UByteArray(), Fixed, index);
1425 msg(usprintf("%s: non-UEFI data found in volume's free space", __FUNCTION__), paddingIndex);
1426
1427 // Parse contents as RAW area
1428 return parseRawArea(paddingIndex);
1429 }
1430
parseVolumeBody(const UModelIndex & index)1431 USTATUS FfsParser::parseVolumeBody(const UModelIndex & index)
1432 {
1433 // Sanity check
1434 if (!index.isValid()) {
1435 return U_INVALID_PARAMETER;
1436 }
1437
1438 // Get volume header size and body
1439 UByteArray volumeBody = model->body(index);
1440 UINT32 volumeHeaderSize = (UINT32)model->header(index).size();
1441
1442 // Parse VSS NVRAM volumes with a dedicated function
1443 if (model->subtype(index) == Subtypes::NvramVolume) {
1444 return nvramParser->parseNvramVolumeBody(index);
1445 }
1446
1447 // Parse Microcode volume with a dedicated function
1448 if (model->subtype(index) == Subtypes::MicrocodeVolume) {
1449 return parseMicrocodeVolumeBody(index);
1450 }
1451
1452 // Get required values from parsing data
1453 UINT8 emptyByte = 0xFF;
1454 UINT8 ffsVersion = 2;
1455 UINT32 usedSpace = 0;
1456 if (model->hasEmptyParsingData(index) == false) {
1457 UByteArray data = model->parsingData(index);
1458 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
1459 emptyByte = pdata->emptyByte;
1460 ffsVersion = pdata->ffsVersion;
1461 usedSpace = pdata->usedSpace;
1462 }
1463
1464 // Check for unknown FFS version
1465 if (ffsVersion != 2 && ffsVersion != 3) {
1466 msg(usprintf("%s: unknown FFS version %d", __FUNCTION__, ffsVersion), index);
1467 return U_SUCCESS;
1468 }
1469
1470 // Search for and parse all files
1471 UINT32 volumeBodySize = (UINT32)volumeBody.size();
1472 UINT32 fileOffset = 0;
1473
1474 while (fileOffset < volumeBodySize) {
1475 UINT32 fileSize = getFileSize(volumeBody, fileOffset, ffsVersion);
1476
1477 if (fileSize == 0) {
1478 msg(usprintf("%s: file header parsing failed with invalid size", __FUNCTION__), index);
1479 break; // Exit from parsing loop
1480 }
1481
1482 // Check that we are at the empty space
1483 UByteArray header = volumeBody.mid(fileOffset, (int)std::min(sizeof(EFI_FFS_FILE_HEADER), (size_t)volumeBodySize - fileOffset));
1484 if (header.count(emptyByte) == header.size()) { //Empty space
1485 // Check volume usedSpace entry to be valid
1486 if (usedSpace > 0 && usedSpace == fileOffset + volumeHeaderSize) {
1487 if (model->hasEmptyParsingData(index) == false) {
1488 UByteArray data = model->parsingData(index);
1489 VOLUME_PARSING_DATA* pdata = (VOLUME_PARSING_DATA*)data.data();
1490 pdata->hasValidUsedSpace = TRUE;
1491 model->setParsingData(index, data);
1492 model->setText(index, model->text(index) + "UsedSpace ");
1493 }
1494 }
1495
1496 // Check free space to be actually free
1497 UByteArray freeSpace = volumeBody.mid(fileOffset);
1498 if (freeSpace.count(emptyByte) != freeSpace.size()) {
1499 // Search for the first non-empty byte
1500 UINT32 i;
1501 UINT32 size = (UINT32)freeSpace.size();
1502 const UINT8* current = (UINT8*)freeSpace.constData();
1503 for (i = 0; i < size; i++) {
1504 if (*current++ != emptyByte) {
1505 break; // Exit from parsing loop
1506 }
1507 }
1508
1509 // Align found index to file alignment
1510 // It must be possible because minimum 16 bytes of empty were found before
1511 if (i != ALIGN8(i)) {
1512 i = ALIGN8(i) - 8;
1513 }
1514
1515 // Add all bytes before as free space
1516 if (i > 0) {
1517 UByteArray free = freeSpace.left(i);
1518
1519 // Get info
1520 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", free.size(), free.size());
1521
1522 // Add free space item
1523 model->addItem(volumeHeaderSize + fileOffset, Types::FreeSpace, 0, UString("Volume free space"), UString(), info, UByteArray(), free, UByteArray(), Movable, index);
1524 }
1525
1526 // Parse non-UEFI data
1527 parseVolumeNonUefiData(freeSpace.mid(i), volumeHeaderSize + fileOffset + i, index);
1528 }
1529 else {
1530 // Get info
1531 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", freeSpace.size(), freeSpace.size());
1532
1533 // Add free space item
1534 model->addItem(volumeHeaderSize + fileOffset, Types::FreeSpace, 0, UString("Volume free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Movable, index);
1535 }
1536
1537 break; // Exit from parsing loop
1538 }
1539
1540 // We aren't at the end of empty space
1541 // Check that the remaining space can still have a file in it
1542 if (volumeBodySize - fileOffset < sizeof(EFI_FFS_FILE_HEADER) || // Remaining space is smaller than the smallest possible file
1543 volumeBodySize - fileOffset < fileSize) { // Remaining space is smaller than non-empty file size
1544 // Parse non-UEFI data
1545 parseVolumeNonUefiData(volumeBody.mid(fileOffset), volumeHeaderSize + fileOffset, index);
1546
1547 break; // Exit from parsing loop
1548 }
1549
1550 // Parse current file's header
1551 UModelIndex fileIndex;
1552 USTATUS result = parseFileHeader(volumeBody.mid(fileOffset, fileSize), volumeHeaderSize + fileOffset, index, fileIndex);
1553 if (result) {
1554 msg(usprintf("%s: file header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
1555 }
1556
1557 // Move to next file
1558 fileOffset += fileSize;
1559 fileOffset = ALIGN8(fileOffset);
1560 }
1561
1562 // Check for duplicate GUIDs
1563 for (int i = 0; i < model->rowCount(index); i++) {
1564 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
1565 UModelIndex current = index.child(i, 0);
1566 #else
1567 UModelIndex current = index.model()->index(i, 0, index);
1568 #endif
1569
1570 // Skip non-file entries and pad files
1571 if (model->type(current) != Types::File || model->subtype(current) == EFI_FV_FILETYPE_PAD) {
1572 continue;
1573 }
1574
1575 // Get current file GUID
1576 UByteArray currentGuid(model->header(current).constData(), sizeof(EFI_GUID));
1577
1578 // Check files after current for having an equal GUID
1579 for (int j = i + 1; j < model->rowCount(index); j++) {
1580 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
1581 UModelIndex another = index.child(j, 0);
1582 #else
1583 UModelIndex another = index.model()->index(j, 0, index);
1584 #endif
1585
1586 // Skip non-file entries
1587 if (model->type(another) != Types::File) {
1588 continue;
1589 }
1590
1591 // Get another file GUID
1592 UByteArray anotherGuid(model->header(another).constData(), sizeof(EFI_GUID));
1593
1594 // Check GUIDs for being equal
1595 if (currentGuid == anotherGuid) {
1596 msg(usprintf("%s: file with duplicate GUID ", __FUNCTION__) + guidToUString(readUnaligned((EFI_GUID*)(anotherGuid.data()))), another);
1597 }
1598 }
1599 }
1600
1601 // Parse bodies
1602 for (int i = 0; i < model->rowCount(index); i++) {
1603 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
1604 UModelIndex current = index.child(i, 0);
1605 #else
1606 UModelIndex current = index.model()->index(i, 0, index);
1607 #endif
1608
1609 switch (model->type(current)) {
1610 case Types::File:
1611 parseFileBody(current);
1612 break;
1613 case Types::Padding:
1614 case Types::FreeSpace:
1615 // No parsing required
1616 break;
1617 default:
1618 return U_UNKNOWN_ITEM_TYPE;
1619 }
1620 }
1621
1622 return U_SUCCESS;
1623 }
1624
getFileSize(const UByteArray & volume,const UINT32 fileOffset,const UINT8 ffsVersion)1625 UINT32 FfsParser::getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion)
1626 {
1627 if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER)) {
1628 return 0;
1629 }
1630
1631 const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset);
1632
1633 if (ffsVersion == 2) {
1634 return uint24ToUint32(fileHeader->Size);
1635 }
1636 else if (ffsVersion == 3) {
1637 if (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE) {
1638 if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER2)) {
1639 return 0;
1640 }
1641
1642 const EFI_FFS_FILE_HEADER2* fileHeader2 = (const EFI_FFS_FILE_HEADER2*)(volume.constData() + fileOffset);
1643 return (UINT32) fileHeader2->ExtendedSize;
1644 }
1645
1646 return uint24ToUint32(fileHeader->Size);
1647 }
1648
1649 return 0;
1650 }
1651
parseFileHeader(const UByteArray & file,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1652 USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1653 {
1654 // Sanity check
1655 if (file.isEmpty()) {
1656 return U_INVALID_PARAMETER;
1657 }
1658 if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER)) {
1659 return U_INVALID_FILE;
1660 }
1661
1662 // Obtain required information from parent volume
1663 UINT8 ffsVersion = 2;
1664 bool isWeakAligned = false;
1665 UINT32 volumeAlignment = 0xFFFFFFFF;
1666 UINT8 volumeRevision = 2;
1667 UModelIndex parentVolumeIndex = model->type(parent) == Types::Volume ? parent : model->findParentOfType(parent, Types::Volume);
1668 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
1669 UByteArray data = model->parsingData(parentVolumeIndex);
1670 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
1671 ffsVersion = pdata->ffsVersion;
1672 volumeAlignment = pdata->alignment;
1673 volumeRevision = pdata->revision;
1674 isWeakAligned = pdata->isWeakAligned;
1675 }
1676
1677 // Get file header
1678 UByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
1679 EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)header.data();
1680 if (ffsVersion == 3 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
1681 if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2))
1682 return U_INVALID_FILE;
1683 header = file.left(sizeof(EFI_FFS_FILE_HEADER2));
1684 }
1685 const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData();
1686
1687 // Check file alignment
1688 bool msgUnalignedFile = false;
1689 UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
1690 if (volumeRevision > 1 && (fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT2)) {
1691 alignmentPower = ffsAlignment2Table[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
1692 }
1693
1694 UINT32 alignment = (UINT32)(1UL << alignmentPower);
1695 if ((localOffset + header.size()) % alignment) {
1696 msgUnalignedFile = true;
1697 }
1698
1699 // Check file alignment agains volume alignment
1700 bool msgFileAlignmentIsGreaterThanVolumeAlignment = false;
1701 if (!isWeakAligned && volumeAlignment < alignment) {
1702 msgFileAlignmentIsGreaterThanVolumeAlignment = true;
1703 }
1704
1705 // Get file body
1706 UByteArray body = file.mid(header.size());
1707
1708 // Check for file tail presence
1709 UByteArray tail;
1710 bool msgInvalidTailValue = false;
1711 if (volumeRevision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) {
1712 //Check file tail;
1713 UINT16 tailValue = *(UINT16*)body.right(sizeof(UINT16)).constData();
1714 if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tailValue)
1715 msgInvalidTailValue = true;
1716
1717 // Get tail and remove it from file body
1718 tail = body.right(sizeof(UINT16));
1719 body = body.left(body.size() - sizeof(UINT16));
1720 }
1721
1722 // Check header checksum
1723 UINT8 calculatedHeader = 0x100 - (calculateSum8((const UINT8*)header.constData(), (UINT32)header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State);
1724 bool msgInvalidHeaderChecksum = false;
1725 if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) {
1726 msgInvalidHeaderChecksum = true;
1727 }
1728
1729 // Check data checksum
1730 // Data checksum must be calculated
1731 bool msgInvalidDataChecksum = false;
1732 UINT8 calculatedData = 0;
1733 if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
1734 calculatedData = calculateChecksum8((const UINT8*)body.constData(), (UINT32)body.size());
1735 }
1736 // Data checksum must be one of predefined values
1737 else if (volumeRevision == 1) {
1738 calculatedData = FFS_FIXED_CHECKSUM;
1739 }
1740 else {
1741 calculatedData = FFS_FIXED_CHECKSUM2;
1742 }
1743
1744 if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) {
1745 msgInvalidDataChecksum = true;
1746 }
1747
1748 // Check file type
1749 bool msgUnknownType = false;
1750 if (fileHeader->Type > EFI_FV_FILETYPE_MM_CORE_STANDALONE && fileHeader->Type != EFI_FV_FILETYPE_PAD) {
1751 msgUnknownType = true;
1752 };
1753
1754 // Get info
1755 UString name;
1756 UString info;
1757 if (fileHeader->Type != EFI_FV_FILETYPE_PAD) {
1758 name = guidToUString(fileHeader->Name);
1759 } else {
1760 name = UString("Pad-file");
1761 }
1762
1763 info = UString("File GUID: ") + guidToUString(fileHeader->Name, false) +
1764 usprintf("\nType: %02Xh\nAttributes: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nTail size: %" PRIXQ "h (%" PRIuQ ")\nState: %02Xh",
1765 fileHeader->Type,
1766 fileHeader->Attributes,
1767 header.size() + body.size() + tail.size(), header.size() + body.size() + tail.size(),
1768 header.size(), header.size(),
1769 body.size(), body.size(),
1770 tail.size(), tail.size(),
1771 fileHeader->State) +
1772 usprintf("\nHeader checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.Header) + (msgInvalidHeaderChecksum ? usprintf(", invalid, should be %02Xh", calculatedHeader) : UString(", valid")) +
1773 usprintf("\nData checksum: %02Xh", fileHeader->IntegrityCheck.Checksum.File) + (msgInvalidDataChecksum ? usprintf(", invalid, should be %02Xh", calculatedData) : UString(", valid"));
1774
1775 UString text;
1776 bool isVtf = false;
1777 bool isDxeCore = false;
1778 // Check if the file is a Volume Top File
1779 UByteArray fileGuid = UByteArray((const char*)&fileHeader->Name, sizeof(EFI_GUID));
1780 if (fileGuid == EFI_FFS_VOLUME_TOP_FILE_GUID) {
1781 // Mark it as the last VTF
1782 // This information will later be used to determine memory addresses of uncompressed image elements
1783 // Because the last byte of the last VFT is mapped to 0xFFFFFFFF physical memory address
1784 isVtf = true;
1785 text = UString("Volume Top File");
1786 }
1787 // Check if the file is the first DXE Core
1788 else if (fileGuid == EFI_DXE_CORE_GUID || fileGuid == AMI_CORE_DXE_GUID) {
1789 // Mark is as first DXE core
1790 // This information may be used to determine DXE volume offset for old AMI or post-IBB protected ranges
1791 isDxeCore = true;
1792 }
1793
1794 // Construct fixed state
1795 ItemFixedState fixed = (ItemFixedState)((fileHeader->Attributes & FFS_ATTRIB_FIXED) != 0);
1796
1797 // Add tree item
1798 index = model->addItem(localOffset, Types::File, fileHeader->Type, name, text, info, header, body, tail, fixed, parent);
1799
1800 // Set parsing data for created file
1801 FILE_PARSING_DATA pdata;
1802 pdata.emptyByte = (fileHeader->State & EFI_FILE_ERASE_POLARITY) ? 0xFF : 0x00;
1803 pdata.guid = fileHeader->Name;
1804 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
1805
1806 // Override lastVtf index, if needed
1807 if (isVtf) {
1808 lastVtf = index;
1809 }
1810
1811 // Override first DXE core index, if needed
1812 if (isDxeCore && !bgDxeCoreIndex.isValid()) {
1813 bgDxeCoreIndex = index;
1814 }
1815
1816 // Show messages
1817 if (msgUnalignedFile)
1818 msg(usprintf("%s: unaligned file", __FUNCTION__), index);
1819 if (msgFileAlignmentIsGreaterThanVolumeAlignment)
1820 msg(usprintf("%s: file alignment %Xh is greater than parent volume alignment %Xh", __FUNCTION__, alignment, volumeAlignment), index);
1821 if (msgInvalidHeaderChecksum)
1822 msg(usprintf("%s: invalid header checksum %02Xh, should be %02Xh", __FUNCTION__, fileHeader->IntegrityCheck.Checksum.Header, calculatedHeader), index);
1823 if (msgInvalidDataChecksum)
1824 msg(usprintf("%s: invalid data checksum %02Xh, should be %02Xh", __FUNCTION__, fileHeader->IntegrityCheck.Checksum.File, calculatedData), index);
1825 if (msgInvalidTailValue)
1826 msg(usprintf("%s: invalid tail value %04Xh", __FUNCTION__, *(const UINT16*)tail.constData()), index);
1827 if (msgUnknownType)
1828 msg(usprintf("%s: unknown file type %02Xh", __FUNCTION__, fileHeader->Type), index);
1829
1830 return U_SUCCESS;
1831 }
1832
getSectionSize(const UByteArray & file,const UINT32 sectionOffset,const UINT8 ffsVersion)1833 UINT32 FfsParser::getSectionSize(const UByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion)
1834 {
1835 if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER)) {
1836 return 0;
1837 }
1838 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset);
1839
1840 if (ffsVersion == 2) {
1841 return uint24ToUint32(sectionHeader->Size);
1842 }
1843 else if (ffsVersion == 3) {
1844 UINT32 size = uint24ToUint32(sectionHeader->Size);
1845 if (size == EFI_SECTION2_IS_USED) {
1846 if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER2)) {
1847 return 0;
1848 }
1849 const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset);
1850 return sectionHeader2->ExtendedSize;
1851 }
1852
1853 return size;
1854 }
1855
1856 return 0;
1857 }
1858
parseFileBody(const UModelIndex & index)1859 USTATUS FfsParser::parseFileBody(const UModelIndex & index)
1860 {
1861 // Sanity check
1862 if (!index.isValid())
1863 return U_INVALID_PARAMETER;
1864
1865 // Do not parse non-file bodies
1866 if (model->type(index) != Types::File)
1867 return U_SUCCESS;
1868
1869 // Parse pad-file body
1870 if (model->subtype(index) == EFI_FV_FILETYPE_PAD)
1871 return parsePadFileBody(index);
1872
1873 // Parse raw files as raw areas
1874 if (model->subtype(index) == EFI_FV_FILETYPE_RAW || model->subtype(index) == EFI_FV_FILETYPE_ALL) {
1875 UByteArray fileGuid = UByteArray(model->header(index).constData(), sizeof(EFI_GUID));
1876
1877 // Parse NVAR store
1878 if (fileGuid == NVRAM_NVAR_STORE_FILE_GUID) {
1879 model->setText(index, UString("NVAR store"));
1880 return nvramParser->parseNvarStore(index);
1881 }
1882 else if (fileGuid == NVRAM_NVAR_PEI_EXTERNAL_DEFAULTS_FILE_GUID) {
1883 model->setText(index, UString("NVRAM external defaults"));
1884 return nvramParser->parseNvarStore(index);
1885 }
1886 else if (fileGuid == NVRAM_NVAR_BB_DEFAULTS_FILE_GUID) {
1887 model->setText(index, UString("NVAR bb defaults"));
1888 return nvramParser->parseNvarStore(index);
1889 }
1890 // Parse vendor hash file
1891 else if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) {
1892 return parseVendorHashFile(fileGuid, index);
1893 }
1894
1895 return parseRawArea(index);
1896 }
1897
1898 // Parse sections
1899 return parseSections(model->body(index), index, true);
1900 }
1901
parsePadFileBody(const UModelIndex & index)1902 USTATUS FfsParser::parsePadFileBody(const UModelIndex & index)
1903 {
1904 // Sanity check
1905 if (!index.isValid())
1906 return U_INVALID_PARAMETER;
1907
1908 // Check if all bytes of the file are empty
1909 UByteArray body = model->body(index);
1910
1911 // Obtain required information from parent file
1912 UINT8 emptyByte = 0xFF;
1913 UModelIndex parentFileIndex = model->findParentOfType(index, Types::File);
1914 if (parentFileIndex.isValid() && model->hasEmptyParsingData(parentFileIndex) == false) {
1915 UByteArray data = model->parsingData(index);
1916 const FILE_PARSING_DATA* pdata = (const FILE_PARSING_DATA*)data.constData();
1917 emptyByte = pdata->emptyByte;
1918 }
1919
1920 // Check if the while PAD file is empty
1921 if (body.size() == body.count(emptyByte))
1922 return U_SUCCESS;
1923
1924 // Search for the first non-empty byte
1925 UINT32 nonEmptyByteOffset;
1926 UINT32 size = (UINT32)body.size();
1927 const UINT8* current = (const UINT8*)body.constData();
1928 for (nonEmptyByteOffset = 0; nonEmptyByteOffset < size; nonEmptyByteOffset++) {
1929 if (*current++ != emptyByte)
1930 break;
1931 }
1932
1933 // Add all bytes before as free space...
1934 UINT32 headerSize = (UINT32)model->header(index).size();
1935 if (nonEmptyByteOffset >= 8) {
1936 // Align free space to 8 bytes boundary
1937 if (nonEmptyByteOffset != ALIGN8(nonEmptyByteOffset))
1938 nonEmptyByteOffset = ALIGN8(nonEmptyByteOffset) - 8;
1939
1940 UByteArray free = body.left(nonEmptyByteOffset);
1941
1942 // Get info
1943 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", free.size(), free.size());
1944
1945 // Add tree item
1946 model->addItem(headerSize, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), free, UByteArray(), Movable, index);
1947 }
1948 else {
1949 nonEmptyByteOffset = 0;
1950 }
1951
1952 // ... and all bytes after as a padding
1953 UByteArray padding = body.mid(nonEmptyByteOffset);
1954
1955 // Get info
1956 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
1957
1958 // Add tree item
1959 UModelIndex dataIndex = model->addItem(headerSize + nonEmptyByteOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
1960
1961 // Show message
1962 msg(usprintf("%s: non-UEFI data found in pad-file", __FUNCTION__), dataIndex);
1963
1964 // Rename the file
1965 model->setName(index, UString("Non-empty pad-file"));
1966
1967 // Parse contents as RAW area
1968 return parseRawArea(dataIndex);
1969 }
1970
parseSections(const UByteArray & sections,const UModelIndex & index,const bool insertIntoTree)1971 USTATUS FfsParser::parseSections(const UByteArray & sections, const UModelIndex & index, const bool insertIntoTree)
1972 {
1973 // Sanity check
1974 if (!index.isValid())
1975 return U_INVALID_PARAMETER;
1976
1977 // Search for and parse all sections
1978 UINT32 bodySize = (UINT32)sections.size();
1979 UINT32 headerSize = (UINT32)model->header(index).size();
1980 UINT32 sectionOffset = 0;
1981 USTATUS result = U_SUCCESS;
1982
1983 // Obtain required information from parent volume
1984 UINT8 ffsVersion = 2;
1985 UModelIndex parentVolumeIndex = model->findParentOfType(index, Types::Volume);
1986 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
1987 UByteArray data = model->parsingData(parentVolumeIndex);
1988 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
1989 ffsVersion = pdata->ffsVersion;
1990 }
1991
1992 while (sectionOffset < bodySize) {
1993 // Get section size
1994 UINT32 sectionSize = getSectionSize(sections, sectionOffset, ffsVersion);
1995
1996 // Check section size
1997 if (sectionSize < sizeof(EFI_COMMON_SECTION_HEADER) || sectionSize > (bodySize - sectionOffset)) {
1998 // Final parsing
1999 if (insertIntoTree) {
2000 // Add padding to fill the rest of sections
2001 UByteArray padding = sections.mid(sectionOffset);
2002
2003 // Get info
2004 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
2005
2006 // Add tree item
2007 UModelIndex dataIndex = model->addItem(headerSize + sectionOffset, Types::Padding, Subtypes::DataPadding, UString("Non-UEFI data"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
2008
2009 // Show message
2010 msg(usprintf("%s: non-UEFI data found in sections area", __FUNCTION__), dataIndex);
2011
2012 // Exit from parsing loop
2013 break;
2014 }
2015 // Preparsing
2016 else {
2017 return U_INVALID_SECTION;
2018 }
2019 }
2020
2021 // Parse section header
2022 UModelIndex sectionIndex;
2023 result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex, insertIntoTree);
2024 if (result) {
2025 if (insertIntoTree)
2026 msg(UString("parseSections: section header parsing failed with error ") + errorCodeToUString(result), index);
2027 else
2028 return U_INVALID_SECTION;
2029 }
2030
2031 // Move to next section
2032 sectionOffset += sectionSize;
2033 sectionOffset = ALIGN4(sectionOffset);
2034 }
2035
2036 // Parse bodies, will be skipped if insertIntoTree is not required
2037 for (int i = 0; i < model->rowCount(index); i++) {
2038 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
2039 UModelIndex current = index.child(i, 0);
2040 #else
2041 UModelIndex current = index.model()->index(i, 0, index);
2042 #endif
2043
2044 switch (model->type(current)) {
2045 case Types::Section:
2046 parseSectionBody(current);
2047 break;
2048 case Types::Padding:
2049 // No parsing required
2050 break;
2051 default:
2052 return U_UNKNOWN_ITEM_TYPE;
2053 }
2054 }
2055
2056 return U_SUCCESS;
2057 }
2058
parseSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2059 USTATUS FfsParser::parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2060 {
2061 // Check sanity
2062 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) {
2063 return U_INVALID_SECTION;
2064 }
2065
2066 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2067 switch (sectionHeader->Type) {
2068 // Special
2069 case EFI_SECTION_COMPRESSION: return parseCompressedSectionHeader(section, localOffset, parent, index, insertIntoTree);
2070 case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree);
2071 case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseFreeformGuidedSectionHeader(section, localOffset, parent, index, insertIntoTree);
2072 case EFI_SECTION_VERSION: return parseVersionSectionHeader(section, localOffset, parent, index, insertIntoTree);
2073 case PHOENIX_SECTION_POSTCODE:
2074 case INSYDE_SECTION_POSTCODE: return parsePostcodeSectionHeader(section, localOffset, parent, index, insertIntoTree);
2075 // Common
2076 case EFI_SECTION_DISPOSABLE:
2077 case EFI_SECTION_DXE_DEPEX:
2078 case EFI_SECTION_PEI_DEPEX:
2079 case EFI_SECTION_MM_DEPEX:
2080 case EFI_SECTION_PE32:
2081 case EFI_SECTION_PIC:
2082 case EFI_SECTION_TE:
2083 case EFI_SECTION_COMPATIBILITY16:
2084 case EFI_SECTION_USER_INTERFACE:
2085 case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
2086 case EFI_SECTION_RAW: return parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree);
2087 // Unknown
2088 default:
2089 USTATUS result = parseCommonSectionHeader(section, localOffset, parent, index, insertIntoTree);
2090 msg(usprintf("%s: section with unknown type %02Xh", __FUNCTION__, sectionHeader->Type), index);
2091 return result;
2092 }
2093 }
2094
parseCommonSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2095 USTATUS FfsParser::parseCommonSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2096 {
2097 // Check sanity
2098 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) {
2099 return U_INVALID_SECTION;
2100 }
2101
2102 // Obtain required information from parent volume
2103 UINT8 ffsVersion = 2;
2104 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2105 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2106 UByteArray data = model->parsingData(parentVolumeIndex);
2107 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2108 ffsVersion = pdata->ffsVersion;
2109 }
2110
2111 // Obtain header fields
2112 UINT32 headerSize;
2113 UINT8 type;
2114 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2115 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) {
2116 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE);
2117 type = appleHeader->Type;
2118 }
2119 else {
2120 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2121 headerSize = sizeof(EFI_COMMON_SECTION_HEADER);
2122 if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED)
2123 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2);
2124 type = sectionHeader->Type;
2125 }
2126
2127 // Check sanity again
2128 if ((UINT32)section.size() < headerSize) {
2129 return U_INVALID_SECTION;
2130 }
2131
2132 UByteArray header = section.left(headerSize);
2133 UByteArray body = section.mid(headerSize);
2134
2135 // Get info
2136 UString name = sectionTypeToUString(type) + UString(" section");
2137 UString info = usprintf("Type: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %Xh (%u)\nBody size: %" PRIXQ "h (%" PRIuQ ")",
2138 type,
2139 section.size(), section.size(),
2140 headerSize, headerSize,
2141 body.size(), body.size());
2142
2143 // Add tree item
2144 if (insertIntoTree) {
2145 index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2146 }
2147
2148 return U_SUCCESS;
2149 }
2150
parseCompressedSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2151 USTATUS FfsParser::parseCompressedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2152 {
2153 // Check sanity
2154 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2155 return U_INVALID_SECTION;
2156
2157 // Obtain required information from parent volume
2158 UINT8 ffsVersion = 2;
2159 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2160 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2161 UByteArray data = model->parsingData(parentVolumeIndex);
2162 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2163 ffsVersion = pdata->ffsVersion;
2164 }
2165
2166 // Obtain header fields
2167 UINT32 headerSize;
2168 UINT8 compressionType;
2169 UINT32 uncompressedLength;
2170 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2171 const EFI_COMMON_SECTION_HEADER2* section2Header = (const EFI_COMMON_SECTION_HEADER2*)(section.constData());
2172 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2173
2174 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) { // Check for apple section
2175 const EFI_COMPRESSION_SECTION_APPLE* appleSectionHeader = (const EFI_COMPRESSION_SECTION_APPLE*)(appleHeader + 1);
2176 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE) + sizeof(EFI_COMPRESSION_SECTION_APPLE);
2177 compressionType = (UINT8)appleSectionHeader->CompressionType;
2178 uncompressedLength = appleSectionHeader->UncompressedLength;
2179 }
2180 else if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { // Check for extended header section
2181 const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)(section2Header + 1);
2182 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_COMPRESSION_SECTION))
2183 return U_INVALID_SECTION;
2184 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_COMPRESSION_SECTION);
2185 compressionType = compressedSectionHeader->CompressionType;
2186 uncompressedLength = compressedSectionHeader->UncompressedLength;
2187 }
2188 else { // Normal section
2189 const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)(sectionHeader + 1);
2190 headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_COMPRESSION_SECTION);
2191 compressionType = compressedSectionHeader->CompressionType;
2192 uncompressedLength = compressedSectionHeader->UncompressedLength;
2193 }
2194
2195 // Check sanity again
2196 if ((UINT32)section.size() < headerSize) {
2197 return U_INVALID_SECTION;
2198 }
2199
2200 UByteArray header = section.left(headerSize);
2201 UByteArray body = section.mid(headerSize);
2202
2203 // Get info
2204 UString name = sectionTypeToUString(sectionHeader->Type) + UString(" section");
2205 UString info = usprintf("Type: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %Xh (%u)\nBody size: %" PRIXQ "h (%" PRIuQ ")\nCompression type: %02Xh\nDecompressed size: %Xh (%u)",
2206 sectionHeader->Type,
2207 section.size(), section.size(),
2208 headerSize, headerSize,
2209 body.size(), body.size(),
2210 compressionType,
2211 uncompressedLength, uncompressedLength);
2212
2213 // Add tree item
2214 if (insertIntoTree) {
2215 index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2216
2217 // Set section parsing data
2218 COMPRESSED_SECTION_PARSING_DATA pdata;
2219 pdata.compressionType = compressionType;
2220 pdata.uncompressedSize = uncompressedLength;
2221 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
2222 }
2223
2224 return U_SUCCESS;
2225 }
2226
parseGuidedSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2227 USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2228 {
2229 // Check sanity
2230 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2231 return U_INVALID_SECTION;
2232
2233 // Obtain required information from parent volume
2234 UINT8 ffsVersion = 2;
2235 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2236 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2237 UByteArray data = model->parsingData(parentVolumeIndex);
2238 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2239 ffsVersion = pdata->ffsVersion;
2240 }
2241
2242 // Obtain header fields
2243 UINT32 headerSize;
2244 EFI_GUID guid;
2245 UINT16 dataOffset;
2246 UINT16 attributes;
2247 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2248 const EFI_COMMON_SECTION_HEADER2* section2Header = (const EFI_COMMON_SECTION_HEADER2*)(section.constData());
2249 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2250
2251 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) { // Check for apple section
2252 const EFI_GUID_DEFINED_SECTION_APPLE* appleSectionHeader = (const EFI_GUID_DEFINED_SECTION_APPLE*)(appleHeader + 1);
2253 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE) + sizeof(EFI_GUID_DEFINED_SECTION_APPLE);
2254 if ((UINT32)section.size() < headerSize)
2255 return U_INVALID_SECTION;
2256 guid = appleSectionHeader->SectionDefinitionGuid;
2257 dataOffset = appleSectionHeader->DataOffset;
2258 attributes = appleSectionHeader->Attributes;
2259 }
2260 else if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { // Check for extended header section
2261 const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(section2Header + 1);
2262 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_GUID_DEFINED_SECTION))
2263 return U_INVALID_SECTION;
2264 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_GUID_DEFINED_SECTION);
2265 guid = guidDefinedSectionHeader->SectionDefinitionGuid;
2266 dataOffset = guidDefinedSectionHeader->DataOffset;
2267 attributes = guidDefinedSectionHeader->Attributes;
2268 }
2269 else { // Normal section
2270 const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(sectionHeader + 1);
2271 headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_GUID_DEFINED_SECTION);
2272 guid = guidDefinedSectionHeader->SectionDefinitionGuid;
2273 dataOffset = guidDefinedSectionHeader->DataOffset;
2274 attributes = guidDefinedSectionHeader->Attributes;
2275 }
2276 // Check sanity again
2277 if ((UINT32)section.size() < headerSize)
2278 return U_INVALID_SECTION;
2279
2280 // Check for special GUIDed sections
2281 UString additionalInfo;
2282 UByteArray baGuid((const char*)&guid, sizeof(EFI_GUID));
2283 bool msgSignedSectionFound = false;
2284 bool msgNoAuthStatusAttribute = false;
2285 bool msgNoProcessingRequiredAttributeCompressed = false;
2286 bool msgNoProcessingRequiredAttributeSigned = false;
2287 bool msgInvalidCrc = false;
2288 bool msgUnknownCertType = false;
2289 bool msgUnknownCertSubtype = false;
2290 bool msgProcessingRequiredAttributeOnUnknownGuidedSection = false;
2291 if (baGuid == EFI_GUIDED_SECTION_CRC32) {
2292 if ((attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) == 0) { // Check that AuthStatusValid attribute is set on compressed GUIDed sections
2293 msgNoAuthStatusAttribute = true;
2294 }
2295
2296 if ((UINT32)section.size() < headerSize + sizeof(UINT32))
2297 return U_INVALID_SECTION;
2298
2299 UINT32 crc = *(UINT32*)(section.constData() + headerSize);
2300 additionalInfo += UString("\nChecksum type: CRC32");
2301 // Calculate CRC32 of section data
2302 UINT32 calculated = (UINT32)crc32(0, (const UINT8*)section.constData() + dataOffset, (uInt)(section.size() - dataOffset));
2303 if (crc == calculated) {
2304 additionalInfo += usprintf("\nChecksum: %08Xh, valid", crc);
2305 }
2306 else {
2307 additionalInfo += usprintf("\nChecksum: %08Xh, invalid, should be %08Xh", crc, calculated);
2308 msgInvalidCrc = true;
2309 }
2310 // No need to change dataOffset here
2311 }
2312 else if (baGuid == EFI_GUIDED_SECTION_LZMA || baGuid == EFI_GUIDED_SECTION_LZMAF86 || baGuid == EFI_GUIDED_SECTION_TIANO || baGuid == EFI_GUIDED_SECTION_GZIP) {
2313 if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on compressed GUIDed sections
2314 msgNoProcessingRequiredAttributeCompressed = true;
2315 }
2316 // No need to change dataOffset here
2317 }
2318 else if (baGuid == EFI_CERT_TYPE_RSA2048_SHA256_GUID) {
2319 if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on signed GUIDed sections
2320 msgNoProcessingRequiredAttributeSigned = true;
2321 }
2322
2323 // Get certificate type and length
2324 if ((UINT32)section.size() < headerSize + sizeof(EFI_CERT_BLOCK_RSA2048_SHA256))
2325 return U_INVALID_SECTION;
2326
2327 // Adjust dataOffset
2328 dataOffset += sizeof(EFI_CERT_BLOCK_RSA2048_SHA256);
2329 additionalInfo += UString("\nCertificate type: RSA2048/SHA256");
2330 msgSignedSectionFound = true;
2331 }
2332 else if (baGuid == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) {
2333 if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { // Check that ProcessingRequired attribute is set on signed GUIDed sections
2334 msgNoProcessingRequiredAttributeSigned = true;
2335 }
2336
2337 // Get certificate type and length
2338 if ((UINT32)section.size() < headerSize + sizeof(WIN_CERTIFICATE))
2339 return U_INVALID_SECTION;
2340
2341 const WIN_CERTIFICATE* winCertificate = (const WIN_CERTIFICATE*)(section.constData() + headerSize);
2342 UINT32 certLength = winCertificate->Length;
2343 UINT16 certType = winCertificate->CertificateType;
2344
2345 // Adjust dataOffset
2346 dataOffset += certLength;
2347
2348 // Check section size once again
2349 if ((UINT32)section.size() < dataOffset)
2350 return U_INVALID_SECTION;
2351
2352 // Check certificate type
2353 if (certType == WIN_CERT_TYPE_EFI_GUID) {
2354 additionalInfo += UString("\nCertificate type: UEFI");
2355
2356 // Get certificate GUID
2357 const WIN_CERTIFICATE_UEFI_GUID* winCertificateUefiGuid = (const WIN_CERTIFICATE_UEFI_GUID*)(section.constData() + headerSize);
2358 UByteArray certTypeGuid((const char*)&winCertificateUefiGuid->CertType, sizeof(EFI_GUID));
2359
2360 if (certTypeGuid == EFI_CERT_TYPE_RSA2048_SHA256_GUID) {
2361 additionalInfo += UString("\nCertificate subtype: RSA2048/SHA256");
2362 }
2363 else {
2364 additionalInfo += UString("\nCertificate subtype: unknown, GUID ") + guidToUString(winCertificateUefiGuid->CertType);
2365 msgUnknownCertSubtype = true;
2366 }
2367 }
2368 else {
2369 additionalInfo += usprintf("\nCertificate type: unknown (%04Xh)", certType);
2370 msgUnknownCertType = true;
2371 }
2372 msgSignedSectionFound = true;
2373 }
2374 // Check that ProcessingRequired attribute is not set on GUIDed sections with unknown GUID
2375 else if ((attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == EFI_GUIDED_SECTION_PROCESSING_REQUIRED) {
2376 msgProcessingRequiredAttributeOnUnknownGuidedSection = true;
2377 }
2378
2379 UByteArray header = section.left(dataOffset);
2380 UByteArray body = section.mid(dataOffset);
2381
2382 // Get info
2383 UString name = guidToUString(guid);
2384 UString info = UString("Section GUID: ") + guidToUString(guid, false) +
2385 usprintf("\nType: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nData offset: %Xh\nAttributes: %04Xh",
2386 sectionHeader->Type,
2387 section.size(), section.size(),
2388 header.size(), header.size(),
2389 body.size(), body.size(),
2390 dataOffset,
2391 attributes);
2392
2393 // Append additional info
2394 info += additionalInfo;
2395
2396 // Add tree item
2397 if (insertIntoTree) {
2398 index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2399
2400 // Set parsing data
2401 GUIDED_SECTION_PARSING_DATA pdata;
2402 pdata.guid = guid;
2403 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
2404
2405 // Show messages
2406 if (msgSignedSectionFound)
2407 msg(usprintf("%s: section signature may become invalid after any modification", __FUNCTION__), index);
2408 if (msgNoAuthStatusAttribute)
2409 msg(usprintf("%s: CRC32 GUIDed section without AuthStatusValid attribute", __FUNCTION__), index);
2410 if (msgNoProcessingRequiredAttributeCompressed)
2411 msg(usprintf("%s: compressed GUIDed section without ProcessingRequired attribute", __FUNCTION__), index);
2412 if (msgNoProcessingRequiredAttributeSigned)
2413 msg(usprintf("%s: signed GUIDed section without ProcessingRequired attribute", __FUNCTION__), index);
2414 if (msgInvalidCrc)
2415 msg(usprintf("%s: GUID defined section with invalid CRC32", __FUNCTION__), index);
2416 if (msgUnknownCertType)
2417 msg(usprintf("%s: signed GUIDed section with unknown type", __FUNCTION__), index);
2418 if (msgUnknownCertSubtype)
2419 msg(usprintf("%s: signed GUIDed section with unknown subtype", __FUNCTION__), index);
2420 if (msgProcessingRequiredAttributeOnUnknownGuidedSection)
2421 msg(usprintf("%s: processing required bit set for GUIDed section with unknown GUID", __FUNCTION__), index);
2422 }
2423
2424 return U_SUCCESS;
2425 }
2426
parseFreeformGuidedSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2427 USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2428 {
2429 // Check sanity
2430 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2431 return U_INVALID_SECTION;
2432
2433 // Obtain required information from parent volume
2434 UINT8 ffsVersion = 2;
2435 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2436 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2437 UByteArray data = model->parsingData(parentVolumeIndex);
2438 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2439 ffsVersion = pdata->ffsVersion;
2440 }
2441
2442 // Obtain header fields
2443 UINT32 headerSize;
2444 EFI_GUID guid;
2445 UINT8 type;
2446 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2447 const EFI_COMMON_SECTION_HEADER2* section2Header = (const EFI_COMMON_SECTION_HEADER2*)(section.constData());
2448 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2449
2450 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) { // Check for apple section
2451 const EFI_FREEFORM_SUBTYPE_GUID_SECTION* appleSectionHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)(appleHeader + 1);
2452 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE) + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION);
2453 guid = appleSectionHeader->SubTypeGuid;
2454 type = appleHeader->Type;
2455 }
2456 else if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { // Check for extended header section
2457 const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgSectionHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)(section2Header + 1);
2458 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION))
2459 return U_INVALID_SECTION;
2460 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION);
2461 guid = fsgSectionHeader->SubTypeGuid;
2462 type = section2Header->Type;
2463 }
2464 else { // Normal section
2465 const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgSectionHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)(sectionHeader + 1);
2466 headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION);
2467 guid = fsgSectionHeader->SubTypeGuid;
2468 type = sectionHeader->Type;
2469 }
2470
2471 // Check sanity again
2472 if ((UINT32)section.size() < headerSize)
2473 return U_INVALID_SECTION;
2474
2475 UByteArray header = section.left(headerSize);
2476 UByteArray body = section.mid(headerSize);
2477
2478 // Get info
2479 UString name = sectionTypeToUString(type) + (" section");
2480 UString info = usprintf("Type: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nSubtype GUID: ",
2481 type,
2482 section.size(), section.size(),
2483 header.size(), header.size(),
2484 body.size(), body.size())
2485 + guidToUString(guid, false);
2486
2487 // Add tree item
2488 if (insertIntoTree) {
2489 index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2490
2491 // Set parsing data
2492 FREEFORM_GUIDED_SECTION_PARSING_DATA pdata;
2493 pdata.guid = guid;
2494 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
2495
2496 // Rename section
2497 model->setName(index, guidToUString(guid));
2498 }
2499
2500 return U_SUCCESS;
2501 }
2502
parseVersionSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2503 USTATUS FfsParser::parseVersionSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2504 {
2505 // Check sanity
2506 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2507 return U_INVALID_SECTION;
2508
2509 // Obtain required information from parent volume
2510 UINT8 ffsVersion = 2;
2511 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2512 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2513 UByteArray data = model->parsingData(parentVolumeIndex);
2514 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2515 ffsVersion = pdata->ffsVersion;
2516 }
2517
2518 // Obtain header fields
2519 UINT32 headerSize;
2520 UINT16 buildNumber;
2521 UINT8 type;
2522 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2523 const EFI_COMMON_SECTION_HEADER2* section2Header = (const EFI_COMMON_SECTION_HEADER2*)(section.constData());
2524 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2525
2526 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) { // Check for apple section
2527 const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)(appleHeader + 1);
2528 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE) + sizeof(EFI_VERSION_SECTION);
2529 buildNumber = versionHeader->BuildNumber;
2530 type = appleHeader->Type;
2531 }
2532 else if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { // Check for extended header section
2533 const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)(section2Header + 1);
2534 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(EFI_VERSION_SECTION);
2535 buildNumber = versionHeader->BuildNumber;
2536 type = section2Header->Type;
2537 }
2538 else { // Normal section
2539 const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)(sectionHeader + 1);
2540 headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_VERSION_SECTION);
2541 buildNumber = versionHeader->BuildNumber;
2542 type = sectionHeader->Type;
2543 }
2544
2545 // Check sanity again
2546 if ((UINT32)section.size() < headerSize)
2547 return U_INVALID_SECTION;
2548
2549 UByteArray header = section.left(headerSize);
2550 UByteArray body = section.mid(headerSize);
2551
2552 // Get info
2553 UString name = sectionTypeToUString(type) + (" section");
2554 UString info = usprintf("Type: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nBuild number: %u",
2555 type,
2556 section.size(), section.size(),
2557 header.size(), header.size(),
2558 body.size(), body.size(),
2559 buildNumber);
2560
2561 // Add tree item
2562 if (insertIntoTree) {
2563 index = model->addItem(localOffset, Types::Section, type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2564 }
2565
2566 return U_SUCCESS;
2567 }
2568
parsePostcodeSectionHeader(const UByteArray & section,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index,const bool insertIntoTree)2569 USTATUS FfsParser::parsePostcodeSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree)
2570 {
2571 // Check sanity
2572 if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2573 return U_INVALID_SECTION;
2574
2575 // Obtain required information from parent volume
2576 UINT8 ffsVersion = 2;
2577 UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
2578 if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
2579 UByteArray data = model->parsingData(parentVolumeIndex);
2580 const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
2581 ffsVersion = pdata->ffsVersion;
2582 }
2583
2584 // Obtain header fields
2585 UINT32 headerSize;
2586 UINT32 postCode;
2587 UINT8 type;
2588 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData());
2589 const EFI_COMMON_SECTION_HEADER2* section2Header = (const EFI_COMMON_SECTION_HEADER2*)(section.constData());
2590 const EFI_COMMON_SECTION_HEADER_APPLE* appleHeader = (const EFI_COMMON_SECTION_HEADER_APPLE*)(section.constData());
2591
2592 if ((UINT32)section.size() >= sizeof(EFI_COMMON_SECTION_HEADER_APPLE) && appleHeader->Reserved == EFI_SECTION_APPLE_USED) { // Check for apple section
2593 const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)(appleHeader + 1);
2594 headerSize = sizeof(EFI_COMMON_SECTION_HEADER_APPLE) + sizeof(POSTCODE_SECTION);
2595 postCode = postcodeHeader->Postcode;
2596 type = appleHeader->Type;
2597 }
2598 else if (ffsVersion == 3 && uint24ToUint32(sectionHeader->Size) == EFI_SECTION2_IS_USED) { // Check for extended header section
2599 const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)(section2Header + 1);
2600 headerSize = sizeof(EFI_COMMON_SECTION_HEADER2) + sizeof(POSTCODE_SECTION);
2601 postCode = postcodeHeader->Postcode;
2602 type = section2Header->Type;
2603 }
2604 else { // Normal section
2605 const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)(sectionHeader + 1);
2606 headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(POSTCODE_SECTION);
2607 postCode = postcodeHeader->Postcode;
2608 type = sectionHeader->Type;
2609 }
2610
2611 // Check sanity again
2612 if ((UINT32)section.size() < headerSize)
2613 return U_INVALID_SECTION;
2614
2615 UByteArray header = section.left(headerSize);
2616 UByteArray body = section.mid(headerSize);
2617
2618 // Get info
2619 UString name = sectionTypeToUString(type) + (" section");
2620 UString info = usprintf("Type: %02Xh\nFull size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nPostcode: %Xh",
2621 type,
2622 section.size(), section.size(),
2623 header.size(), header.size(),
2624 body.size(), body.size(),
2625 postCode);
2626
2627 // Add tree item
2628 if (insertIntoTree) {
2629 index = model->addItem(localOffset, Types::Section, sectionHeader->Type, name, UString(), info, header, body, UByteArray(), Movable, parent);
2630 }
2631
2632 return U_SUCCESS;
2633 }
2634
parseSectionBody(const UModelIndex & index)2635 USTATUS FfsParser::parseSectionBody(const UModelIndex & index)
2636 {
2637 // Sanity check
2638 if (!index.isValid())
2639 return U_INVALID_PARAMETER;
2640 UByteArray header = model->header(index);
2641 if ((UINT32)header.size() < sizeof(EFI_COMMON_SECTION_HEADER))
2642 return U_INVALID_SECTION;
2643
2644 const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(header.constData());
2645
2646 switch (sectionHeader->Type) {
2647 // Encapsulation
2648 case EFI_SECTION_COMPRESSION: return parseCompressedSectionBody(index);
2649 case EFI_SECTION_GUID_DEFINED: return parseGuidedSectionBody(index);
2650 case EFI_SECTION_DISPOSABLE: return parseSections(model->body(index), index, true);
2651 // Leaf
2652 case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return parseRawArea(index);
2653 case EFI_SECTION_VERSION: return parseVersionSectionBody(index);
2654 case EFI_SECTION_DXE_DEPEX:
2655 case EFI_SECTION_PEI_DEPEX:
2656 case EFI_SECTION_MM_DEPEX: return parseDepexSectionBody(index);
2657 case EFI_SECTION_TE: return parseTeImageSectionBody(index);
2658 case EFI_SECTION_PE32:
2659 case EFI_SECTION_PIC: return parsePeImageSectionBody(index);
2660 case EFI_SECTION_USER_INTERFACE: return parseUiSectionBody(index);
2661 case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return parseRawArea(index);
2662 case EFI_SECTION_RAW: return parseRawSectionBody(index);
2663 // No parsing needed
2664 case EFI_SECTION_COMPATIBILITY16:
2665 case PHOENIX_SECTION_POSTCODE:
2666 case INSYDE_SECTION_POSTCODE:
2667 default:
2668 return U_SUCCESS;
2669 }
2670 }
2671
parseCompressedSectionBody(const UModelIndex & index)2672 USTATUS FfsParser::parseCompressedSectionBody(const UModelIndex & index)
2673 {
2674 // Sanity check
2675 if (!index.isValid())
2676 return U_INVALID_PARAMETER;
2677
2678 // Obtain required information from parsing data
2679 UINT8 compressionType = EFI_NOT_COMPRESSED;
2680 UINT32 uncompressedSize = (UINT32)model->body(index).size();
2681 if (model->hasEmptyParsingData(index) == false) {
2682 UByteArray data = model->parsingData(index);
2683 const COMPRESSED_SECTION_PARSING_DATA* pdata = (const COMPRESSED_SECTION_PARSING_DATA*)data.constData();
2684 compressionType = readUnaligned(pdata).compressionType;
2685 uncompressedSize = readUnaligned(pdata).uncompressedSize;
2686 }
2687
2688 // Decompress section
2689 UINT8 algorithm = COMPRESSION_ALGORITHM_NONE;
2690 UINT32 dictionarySize = 0;
2691 UByteArray decompressed;
2692 UByteArray efiDecompressed;
2693 USTATUS result = decompress(model->body(index), compressionType, algorithm, dictionarySize, decompressed, efiDecompressed);
2694 if (result) {
2695 msg(UString("parseCompressedSectionBody: decompression failed with error ") + errorCodeToUString(result), index);
2696 return U_SUCCESS;
2697 }
2698
2699 // Check reported uncompressed size
2700 if (uncompressedSize != (UINT32)decompressed.size()) {
2701 msg(usprintf("parseCompressedSectionBody: decompressed size stored in header %Xh (%u) differs from actual %" PRIXQ "h (%" PRIuQ ")",
2702 uncompressedSize, uncompressedSize,
2703 decompressed.size(), decompressed.size()),
2704 index);
2705 model->addInfo(index, usprintf("\nActual decompressed size: %" PRIXQ "h (%" PRIuQ ")", decompressed.size(), decompressed.size()));
2706 }
2707
2708 // Check for undecided compression algorithm, this is a special case
2709 if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) {
2710 // Try preparse of sections decompressed with Tiano algorithm
2711 if (U_SUCCESS == parseSections(decompressed, index, false)) {
2712 algorithm = COMPRESSION_ALGORITHM_TIANO;
2713 }
2714 // Try preparse of sections decompressed with EFI 1.1 algorithm
2715 else if (U_SUCCESS == parseSections(efiDecompressed, index, false)) {
2716 algorithm = COMPRESSION_ALGORITHM_EFI11;
2717 decompressed = efiDecompressed;
2718 }
2719 else {
2720 msg(UString("parseCompressedSectionBody: can't guess the correct decompression algorithm, both preparse steps are failed"), index);
2721 }
2722 }
2723
2724 // Add info
2725 model->addInfo(index, UString("\nCompression algorithm: ") + compressionTypeToUString(algorithm));
2726 if (algorithm == COMPRESSION_ALGORITHM_LZMA || algorithm == COMPRESSION_ALGORITHM_LZMA_INTEL_LEGACY) {
2727 model->addInfo(index, usprintf("\nLZMA dictionary size: %Xh", dictionarySize));
2728 }
2729
2730 // Update parsing data
2731 COMPRESSED_SECTION_PARSING_DATA pdata;
2732 pdata.algorithm = algorithm;
2733 pdata.dictionarySize = dictionarySize;
2734 pdata.compressionType = compressionType;
2735 pdata.uncompressedSize = uncompressedSize;
2736 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
2737
2738 if (algorithm != COMPRESSION_ALGORITHM_NONE)
2739 model->setCompressed(index, true);
2740
2741 // Parse decompressed data
2742 return parseSections(decompressed, index, true);
2743 }
2744
parseGuidedSectionBody(const UModelIndex & index)2745 USTATUS FfsParser::parseGuidedSectionBody(const UModelIndex & index)
2746 {
2747 // Sanity check
2748 if (!index.isValid())
2749 return U_INVALID_PARAMETER;
2750
2751 // Obtain required information from parsing data
2752 EFI_GUID guid = { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0 }};
2753 if (model->hasEmptyParsingData(index) == false) {
2754 UByteArray data = model->parsingData(index);
2755 const GUIDED_SECTION_PARSING_DATA* pdata = (const GUIDED_SECTION_PARSING_DATA*)data.constData();
2756 guid = readUnaligned(pdata).guid;
2757 }
2758
2759 // Check if section requires processing
2760 UByteArray processed = model->body(index);
2761 UByteArray efiDecompressed;
2762 UString info;
2763 bool parseCurrentSection = true;
2764 UINT8 algorithm = COMPRESSION_ALGORITHM_NONE;
2765 UINT32 dictionarySize = 0;
2766 UByteArray baGuid = UByteArray((const char*)&guid, sizeof(EFI_GUID));
2767 // Tiano compressed section
2768 if (baGuid == EFI_GUIDED_SECTION_TIANO) {
2769 USTATUS result = decompress(model->body(index), EFI_STANDARD_COMPRESSION, algorithm, dictionarySize, processed, efiDecompressed);
2770 if (result) {
2771 msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
2772 return U_SUCCESS;
2773 }
2774
2775 // Check for undecided compression algorithm, this is a special case
2776 if (algorithm == COMPRESSION_ALGORITHM_UNDECIDED) {
2777 // Try preparse of sections decompressed with Tiano algorithm
2778 if (U_SUCCESS == parseSections(processed, index, false)) {
2779 algorithm = COMPRESSION_ALGORITHM_TIANO;
2780 }
2781 // Try preparse of sections decompressed with EFI 1.1 algorithm
2782 else if (U_SUCCESS == parseSections(efiDecompressed, index, false)) {
2783 algorithm = COMPRESSION_ALGORITHM_EFI11;
2784 processed = efiDecompressed;
2785 }
2786 else {
2787 msg(usprintf("%s: can't guess the correct decompression algorithm, both preparse steps are failed", __FUNCTION__), index);
2788 parseCurrentSection = false;
2789 }
2790 }
2791
2792 info += UString("\nCompression algorithm: ") + compressionTypeToUString(algorithm);
2793 info += usprintf("\nDecompressed size: %" PRIXQ "h (%" PRIuQ ")", processed.size(), processed.size());
2794 }
2795 // LZMA compressed section
2796 else if (baGuid == EFI_GUIDED_SECTION_LZMA) {
2797 USTATUS result = decompress(model->body(index), EFI_CUSTOMIZED_COMPRESSION, algorithm, dictionarySize, processed, efiDecompressed);
2798 if (result) {
2799 msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
2800 return U_SUCCESS;
2801 }
2802
2803 if (algorithm == COMPRESSION_ALGORITHM_LZMA) {
2804 info += UString("\nCompression algorithm: LZMA");
2805 info += usprintf("\nDecompressed size: %" PRIXQ "h (%" PRIuQ ")", processed.size(), processed.size());
2806 info += usprintf("\nLZMA dictionary size: %Xh", dictionarySize);
2807 }
2808 else {
2809 info += UString("\nCompression algorithm: unknown");
2810 parseCurrentSection = false;
2811 }
2812 }
2813 // LZMAF86 compressed section
2814 else if (baGuid == EFI_GUIDED_SECTION_LZMAF86) {
2815 USTATUS result = decompress(model->body(index), EFI_CUSTOMIZED_COMPRESSION_LZMAF86, algorithm, dictionarySize, processed, efiDecompressed);
2816 if (result) {
2817 msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
2818 return U_SUCCESS;
2819 }
2820
2821 if (algorithm == COMPRESSION_ALGORITHM_LZMAF86) {
2822 info += UString("\nCompression algorithm: LZMAF86");
2823 info += usprintf("\nDecompressed size: %" PRIXQ "h (%" PRIuQ ")", processed.size(), processed.size());
2824 info += usprintf("\nLZMA dictionary size: %Xh", dictionarySize);
2825 }
2826 else {
2827 info += UString("\nCompression algorithm: unknown");
2828 parseCurrentSection = false;
2829 }
2830 }
2831 // GZip compressed section
2832 else if (baGuid == EFI_GUIDED_SECTION_GZIP) {
2833 USTATUS result = gzipDecompress(model->body(index), processed);
2834 if (result) {
2835 msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
2836 return U_SUCCESS;
2837 }
2838
2839 info += UString("\nCompression algorithm: GZip");
2840 info += usprintf("\nDecompressed size: %" PRIXQ "h (%" PRIuQ ")", processed.size(), processed.size());
2841 }
2842
2843 // Add info
2844 model->addInfo(index, info);
2845
2846 // Update data
2847 if (algorithm != COMPRESSION_ALGORITHM_NONE)
2848 model->setCompressed(index, true);
2849
2850 // Set parsing data
2851 GUIDED_SECTION_PARSING_DATA pdata;
2852 pdata.dictionarySize = dictionarySize;
2853 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
2854
2855 if (!parseCurrentSection) {
2856 msg(usprintf("%s: GUID defined section can not be processed", __FUNCTION__), index);
2857 return U_SUCCESS;
2858 }
2859
2860 return parseSections(processed, index, true);
2861 }
2862
parseVersionSectionBody(const UModelIndex & index)2863 USTATUS FfsParser::parseVersionSectionBody(const UModelIndex & index)
2864 {
2865 // Sanity check
2866 if (!index.isValid())
2867 return U_INVALID_PARAMETER;
2868
2869 // Add info
2870 #if QT_VERSION_MAJOR >= 6
2871 model->addInfo(index, UString("\nVersion string: ") + UString::fromUtf16((const char16_t*)model->body(index).constData()));
2872 #else
2873 model->addInfo(index, UString("\nVersion string: ") + UString::fromUtf16((const CHAR16*)model->body(index).constData()));
2874 #endif
2875
2876 return U_SUCCESS;
2877 }
2878
parseDepexSectionBody(const UModelIndex & index)2879 USTATUS FfsParser::parseDepexSectionBody(const UModelIndex & index)
2880 {
2881 // Sanity check
2882 if (!index.isValid())
2883 return U_INVALID_PARAMETER;
2884
2885 UByteArray body = model->body(index);
2886 UString parsed;
2887
2888 // Check data to be present
2889 if (body.size() < 2) { // 2 is a minimal sane value, i.e TRUE + END
2890 msg(usprintf("%s: DEPEX section too short", __FUNCTION__), index);
2891 return U_DEPEX_PARSE_FAILED;
2892 }
2893
2894 const EFI_GUID * guid;
2895 const UINT8* current = (const UINT8*)body.constData();
2896
2897 // Special cases of first opcode
2898 switch (*current) {
2899 case EFI_DEP_BEFORE:
2900 if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
2901 msg(usprintf("%s: DEPEX section too long for a section starting with BEFORE opcode", __FUNCTION__), index);
2902 return U_SUCCESS;
2903 }
2904 guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
2905 parsed += UString("\nBEFORE ") + guidToUString(readUnaligned(guid));
2906 current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
2907 if (*current != EFI_DEP_END){
2908 msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
2909 return U_SUCCESS;
2910 }
2911 return U_SUCCESS;
2912 case EFI_DEP_AFTER:
2913 if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)){
2914 msg(usprintf("%s: DEPEX section too long for a section starting with AFTER opcode", __FUNCTION__), index);
2915 return U_SUCCESS;
2916 }
2917 guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
2918 parsed += UString("\nAFTER ") + guidToUString(readUnaligned(guid));
2919 current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
2920 if (*current != EFI_DEP_END) {
2921 msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
2922 return U_SUCCESS;
2923 }
2924 return U_SUCCESS;
2925 case EFI_DEP_SOR:
2926 if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) {
2927 msg(usprintf("%s: DEPEX section too short for a section starting with SOR opcode", __FUNCTION__), index);
2928 return U_SUCCESS;
2929 }
2930 parsed += UString("\nSOR");
2931 current += EFI_DEP_OPCODE_SIZE;
2932 break;
2933 }
2934
2935 // Parse the rest of depex
2936 while (current - (const UINT8*)body.constData() < body.size()) {
2937 switch (*current) {
2938 case EFI_DEP_BEFORE: {
2939 msg(usprintf("%s: misplaced BEFORE opcode", __FUNCTION__), index);
2940 return U_SUCCESS;
2941 }
2942 case EFI_DEP_AFTER: {
2943 msg(usprintf("%s: misplaced AFTER opcode", __FUNCTION__), index);
2944 return U_SUCCESS;
2945 }
2946 case EFI_DEP_SOR: {
2947 msg(usprintf("%s: misplaced SOR opcode", __FUNCTION__), index);
2948 return U_SUCCESS;
2949 }
2950 case EFI_DEP_PUSH:
2951 // Check that the rest of depex has correct size
2952 if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) {
2953 parsed.clear();
2954 msg(usprintf("%s: remains of DEPEX section too short for PUSH opcode", __FUNCTION__), index);
2955 return U_SUCCESS;
2956 }
2957 guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE);
2958 parsed += UString("\nPUSH ") + guidToUString(readUnaligned(guid));
2959 current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID);
2960 break;
2961 case EFI_DEP_AND:
2962 parsed += UString("\nAND");
2963 current += EFI_DEP_OPCODE_SIZE;
2964 break;
2965 case EFI_DEP_OR:
2966 parsed += UString("\nOR");
2967 current += EFI_DEP_OPCODE_SIZE;
2968 break;
2969 case EFI_DEP_NOT:
2970 parsed += UString("\nNOT");
2971 current += EFI_DEP_OPCODE_SIZE;
2972 break;
2973 case EFI_DEP_TRUE:
2974 parsed += UString("\nTRUE");
2975 current += EFI_DEP_OPCODE_SIZE;
2976 break;
2977 case EFI_DEP_FALSE:
2978 parsed += UString("\nFALSE");
2979 current += EFI_DEP_OPCODE_SIZE;
2980 break;
2981 case EFI_DEP_END:
2982 parsed += UString("\nEND");
2983 current += EFI_DEP_OPCODE_SIZE;
2984 // Check that END is the last opcode
2985 if (current - (const UINT8*)body.constData() < body.size()) {
2986 parsed.clear();
2987 msg(usprintf("%s: DEPEX section ends with non-END opcode", __FUNCTION__), index);
2988 }
2989 break;
2990 default:
2991 msg(usprintf("%s: unknown opcode %02Xh", __FUNCTION__, *current), index);
2992 return U_SUCCESS;
2993 break;
2994 }
2995 }
2996
2997 // Add info
2998 model->addInfo(index, UString("\nParsed expression:") + parsed);
2999
3000 return U_SUCCESS;
3001 }
3002
parseUiSectionBody(const UModelIndex & index)3003 USTATUS FfsParser::parseUiSectionBody(const UModelIndex & index)
3004 {
3005 // Sanity check
3006 if (!index.isValid())
3007 return U_INVALID_PARAMETER;
3008
3009 #if QT_VERSION_MAJOR >= 6
3010 UString text = UString::fromUtf16((const char16_t*)model->body(index).constData());
3011 #else
3012 UString text = UString::fromUtf16((const CHAR16*)model->body(index).constData());
3013 #endif
3014
3015 // Add info
3016 model->addInfo(index, UString("\nText: ") + text);
3017
3018 // Rename parent file
3019 model->setText(model->findParentOfType(index, Types::File), text);
3020
3021 return U_SUCCESS;
3022 }
3023
parseAprioriRawSection(const UByteArray & body,UString & parsed)3024 USTATUS FfsParser::parseAprioriRawSection(const UByteArray & body, UString & parsed)
3025 {
3026 // Sanity check
3027 if (body.size() % sizeof(EFI_GUID)) {
3028 msg(usprintf("%s: apriori file has size is not a multiple of 16", __FUNCTION__));
3029 }
3030 parsed.clear();
3031 UINT32 count = (UINT32)(body.size() / sizeof(EFI_GUID));
3032 if (count > 0) {
3033 for (UINT32 i = 0; i < count; i++) {
3034 const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i;
3035 parsed += UString("\n") + guidToUString(readUnaligned(guid));
3036 }
3037 }
3038
3039 return U_SUCCESS;
3040 }
3041
parseRawSectionBody(const UModelIndex & index)3042 USTATUS FfsParser::parseRawSectionBody(const UModelIndex & index)
3043 {
3044 // Sanity check
3045 if (!index.isValid())
3046 return U_INVALID_PARAMETER;
3047
3048 // Check for apriori file
3049 UModelIndex parentFile = model->findParentOfType(index, Types::File);
3050 if (!parentFile.isValid())
3051 return U_INVALID_FILE; //TODO: better return code
3052
3053 // Get parent file parsing data
3054 UByteArray parentFileGuid(model->header(parentFile).constData(), sizeof(EFI_GUID));
3055 if (parentFileGuid == EFI_PEI_APRIORI_FILE_GUID) { // PEI apriori file
3056 // Set parent file text
3057 model->setText(parentFile, UString("PEI apriori file"));
3058 // Parse apriori file list
3059 UString str;
3060 USTATUS result = parseAprioriRawSection(model->body(index), str);
3061 if (!result && !str.isEmpty())
3062 model->addInfo(index, UString("\nFile list:") + str);
3063 return result;
3064 }
3065 else if (parentFileGuid == EFI_DXE_APRIORI_FILE_GUID) { // DXE apriori file
3066 // Rename parent file
3067 model->setText(parentFile, UString("DXE apriori file"));
3068 // Parse apriori file list
3069 UString str;
3070 USTATUS result = parseAprioriRawSection(model->body(index), str);
3071 if (!result && !str.isEmpty())
3072 model->addInfo(index, UString("\nFile list:") + str);
3073 return result;
3074 }
3075 else if (parentFileGuid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) { // AMI NVRAM external defaults
3076 // Rename parent file
3077 model->setText(parentFile, UString("NVRAM external defaults"));
3078 // Parse NVAR area
3079 return nvramParser->parseNvarStore(index);
3080 }
3081 else if (parentFileGuid == BG_VENDOR_HASH_FILE_GUID_AMI) { // AMI vendor hash file
3082 // Parse AMI vendor hash file
3083 return parseVendorHashFile(parentFileGuid, index);
3084 }
3085
3086 // Parse as raw area
3087 return parseRawArea(index);
3088 }
3089
3090
parsePeImageSectionBody(const UModelIndex & index)3091 USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index)
3092 {
3093 // Sanity check
3094 if (!index.isValid())
3095 return U_INVALID_PARAMETER;
3096
3097 // Get section body
3098 UByteArray body = model->body(index);
3099 if ((UINT32)body.size() < sizeof(EFI_IMAGE_DOS_HEADER)) {
3100 msg(usprintf("%s: section body size is smaller than DOS header size", __FUNCTION__), index);
3101 return U_SUCCESS;
3102 }
3103
3104 UString info;
3105 const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData();
3106 if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
3107 info += usprintf("\nDOS signature: %04Xh, invalid", dosHeader->e_magic);
3108 msg(usprintf("%s: PE32 image with invalid DOS signature", __FUNCTION__), index);
3109 model->addInfo(index, info);
3110 return U_SUCCESS;
3111 }
3112
3113 const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew);
3114 if (body.size() < (UINT8*)peHeader - (UINT8*)dosHeader) {
3115 info += UString("\nDOS header: invalid");
3116 msg(usprintf("%s: PE32 image with invalid DOS header", __FUNCTION__), index);
3117 model->addInfo(index, info);
3118 return U_SUCCESS;
3119 }
3120
3121 if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) {
3122 info += usprintf("\nPE signature: %08Xh, invalid", peHeader->Signature);
3123 msg(usprintf("%s: PE32 image with invalid PE signature", __FUNCTION__), index);
3124 model->addInfo(index, info);
3125 return U_SUCCESS;
3126 }
3127
3128 const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1);
3129 if (body.size() < (UINT8*)imageFileHeader - (UINT8*)dosHeader) {
3130 info += UString("\nPE header: invalid");
3131 msg(usprintf("%s: PE32 image with invalid PE header", __FUNCTION__), index);
3132 model->addInfo(index, info);
3133 return U_SUCCESS;
3134 }
3135
3136 info += usprintf("\nDOS signature: %04Xh\nPE signature: %08Xh",
3137 dosHeader->e_magic,
3138 peHeader->Signature) +
3139 UString("\nMachine type: ") + machineTypeToUString(imageFileHeader->Machine) +
3140 usprintf("\nNumber of sections: %u\nCharacteristics: %04Xh",
3141 imageFileHeader->NumberOfSections,
3142 imageFileHeader->Characteristics);
3143
3144 EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader;
3145 optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1);
3146 if (body.size() < (UINT8*)optionalHeader.H32 - (UINT8*)dosHeader) {
3147 info += UString("\nPE optional header: invalid");
3148 msg(usprintf("%s: PE32 image with invalid PE optional header", __FUNCTION__), index);
3149 model->addInfo(index, info);
3150 return U_SUCCESS;
3151 }
3152
3153 if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) {
3154 info += usprintf("\nOptional header signature: %04Xh\nSubsystem: %04Xh\nAddress of entry point: %Xh\nBase of code: %Xh\nImage base: %Xh",
3155 optionalHeader.H32->Magic,
3156 optionalHeader.H32->Subsystem,
3157 optionalHeader.H32->AddressOfEntryPoint,
3158 optionalHeader.H32->BaseOfCode,
3159 optionalHeader.H32->ImageBase);
3160 }
3161 else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) {
3162 info += usprintf("\nOptional header signature: %04Xh\nSubsystem: %04Xh\nAddress of entry point: %Xh\nBase of code: %Xh\nImage base: %" PRIX64 "h",
3163 optionalHeader.H64->Magic,
3164 optionalHeader.H64->Subsystem,
3165 optionalHeader.H64->AddressOfEntryPoint,
3166 optionalHeader.H64->BaseOfCode,
3167 optionalHeader.H64->ImageBase);
3168 }
3169 else {
3170 info += usprintf("\nOptional header signature: %04Xh, unknown", optionalHeader.H32->Magic);
3171 msg(usprintf("%s: PE32 image with invalid optional PE header signature", __FUNCTION__), index);
3172 }
3173
3174 model->addInfo(index, info);
3175 return U_SUCCESS;
3176 }
3177
3178
parseTeImageSectionBody(const UModelIndex & index)3179 USTATUS FfsParser::parseTeImageSectionBody(const UModelIndex & index)
3180 {
3181 // Check sanity
3182 if (!index.isValid())
3183 return U_INVALID_PARAMETER;
3184
3185 // Get section body
3186 UByteArray body = model->body(index);
3187 if ((UINT32)body.size() < sizeof(EFI_IMAGE_TE_HEADER)) {
3188 msg(usprintf("%s: section body size is smaller than TE header size", __FUNCTION__), index);
3189 return U_SUCCESS;
3190 }
3191
3192 UString info;
3193 const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData();
3194 if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) {
3195 info += usprintf("\nSignature: %04Xh, invalid", teHeader->Signature);
3196 msg(usprintf("%s: TE image with invalid TE signature", __FUNCTION__), index);
3197 }
3198 else {
3199 info += usprintf("\nSignature: %04Xh", teHeader->Signature) +
3200 UString("\nMachine type: ") + machineTypeToUString(teHeader->Machine) +
3201 usprintf("\nNumber of sections: %u\nSubsystem: %02Xh\nStripped size: %Xh (%u)\n"
3202 "Base of code: %Xh\nAddress of entry point: %Xh\nImage base: %" PRIX64 "h\nAdjusted image base: %" PRIX64 "h",
3203 teHeader->NumberOfSections,
3204 teHeader->Subsystem,
3205 teHeader->StrippedSize, teHeader->StrippedSize,
3206 teHeader->BaseOfCode,
3207 teHeader->AddressOfEntryPoint,
3208 teHeader->ImageBase,
3209 teHeader->ImageBase + teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER));
3210 }
3211
3212 // Update parsing data
3213 TE_IMAGE_SECTION_PARSING_DATA pdata;
3214 pdata.imageBaseType = EFI_IMAGE_TE_BASE_OTHER; // Will be determined later
3215 pdata.originalImageBase = (UINT32)teHeader->ImageBase;
3216 pdata.adjustedImageBase = (UINT32)(teHeader->ImageBase + teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER));
3217 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
3218
3219 // Add TE info
3220 model->addInfo(index, info);
3221
3222 return U_SUCCESS;
3223 }
3224
3225
performSecondPass(const UModelIndex & index)3226 USTATUS FfsParser::performSecondPass(const UModelIndex & index)
3227 {
3228 // Sanity check
3229 if (!index.isValid() || !lastVtf.isValid())
3230 return U_INVALID_PARAMETER;
3231
3232 // Check for compressed lastVtf
3233 if (model->compressed(lastVtf)) {
3234 msg(usprintf("%s: the last VTF appears inside compressed item, the image may be damaged", __FUNCTION__), lastVtf);
3235 return U_SUCCESS;
3236 }
3237
3238 // Calculate address difference
3239 const UINT32 vtfSize = (UINT32)(model->header(lastVtf).size() + model->body(lastVtf).size() + model->tail(lastVtf).size());
3240 addressDiff = 0xFFFFFFFFULL - model->base(lastVtf) - vtfSize + 1;
3241
3242 // Parse reset vector data
3243 parseResetVectorData();
3244
3245 // Find and parse FIT
3246 parseFit(index);
3247
3248 // Check protected ranges
3249 checkProtectedRanges(index);
3250
3251 // Check TE files to have original or adjusted base
3252 checkTeImageBase(index);
3253
3254 return U_SUCCESS;
3255 }
3256
parseResetVectorData()3257 USTATUS FfsParser::parseResetVectorData()
3258 {
3259 // Sanity check
3260 if (!lastVtf.isValid())
3261 return U_SUCCESS;
3262
3263 // Check VTF to have enough space at the end to fit Reset Vector Data
3264 UByteArray vtf = model->header(lastVtf) + model->body(lastVtf) + model->tail(lastVtf);
3265 if ((UINT32)vtf.size() < sizeof(X86_RESET_VECTOR_DATA))
3266 return U_SUCCESS;
3267
3268 const X86_RESET_VECTOR_DATA* resetVectorData = (const X86_RESET_VECTOR_DATA*)(vtf.constData() + vtf.size() - sizeof(X86_RESET_VECTOR_DATA));
3269
3270 // Add info
3271 UString info = usprintf("\nAP entry vector: %02X %02X %02X %02X %02X %02X %02X %02X\n"
3272 "Reset vector: %02X %02X %02X %02X %02X %02X %02X %02X\n"
3273 "PEI core entry point: %08Xh\n"
3274 "AP startup segment: %08X\n"
3275 "BootFV base address: %08X\n",
3276 resetVectorData->ApEntryVector[0], resetVectorData->ApEntryVector[1], resetVectorData->ApEntryVector[2], resetVectorData->ApEntryVector[3],
3277 resetVectorData->ApEntryVector[4], resetVectorData->ApEntryVector[5], resetVectorData->ApEntryVector[6], resetVectorData->ApEntryVector[7],
3278 resetVectorData->ResetVector[0], resetVectorData->ResetVector[1], resetVectorData->ResetVector[2], resetVectorData->ResetVector[3],
3279 resetVectorData->ResetVector[4], resetVectorData->ResetVector[5], resetVectorData->ResetVector[6], resetVectorData->ResetVector[7],
3280 resetVectorData->PeiCoreEntryPoint,
3281 resetVectorData->ApStartupSegment,
3282 resetVectorData->BootFvBaseAddress);
3283
3284 model->addInfo(lastVtf, info);
3285 return U_SUCCESS;
3286 }
3287
checkTeImageBase(const UModelIndex & index)3288 USTATUS FfsParser::checkTeImageBase(const UModelIndex & index)
3289 {
3290 // Sanity check
3291 if (!index.isValid())
3292 return U_SUCCESS;
3293
3294 // Determine relocation type of uncompressed TE image sections
3295 if (model->compressed(index) == false
3296 && model->type(index) == Types::Section
3297 && model->subtype(index) == EFI_SECTION_TE) {
3298 // Obtain required values from parsing data
3299 UINT32 originalImageBase = 0;
3300 UINT32 adjustedImageBase = 0;
3301 UINT8 imageBaseType = EFI_IMAGE_TE_BASE_OTHER;
3302 if (model->hasEmptyParsingData(index) == false) {
3303 UByteArray data = model->parsingData(index);
3304 const TE_IMAGE_SECTION_PARSING_DATA* pdata = (const TE_IMAGE_SECTION_PARSING_DATA*)data.constData();
3305 originalImageBase = readUnaligned(pdata).originalImageBase;
3306 adjustedImageBase = readUnaligned(pdata).adjustedImageBase;
3307 }
3308
3309 if (originalImageBase != 0 || adjustedImageBase != 0) {
3310 // Check data memory address to be equal to either OriginalImageBase or AdjustedImageBase
3311 UINT64 address = addressDiff + model->base(index);
3312 UINT32 base = (UINT32)(address + model->header(index).size());
3313
3314 if (originalImageBase == base) {
3315 imageBaseType = EFI_IMAGE_TE_BASE_ORIGINAL;
3316 }
3317 else if (adjustedImageBase == base) {
3318 imageBaseType = EFI_IMAGE_TE_BASE_ADJUSTED;
3319 }
3320 else {
3321 // Check for one-bit difference
3322 UINT32 xored = base ^ originalImageBase; // XOR result can't be zero
3323 if ((xored & (xored - 1)) == 0) { // Check that XOR result is a power of 2, i.e. has exactly one bit set
3324 imageBaseType = EFI_IMAGE_TE_BASE_ORIGINAL;
3325 }
3326 else { // The same check for adjustedImageBase
3327 xored = base ^ adjustedImageBase;
3328 if ((xored & (xored - 1)) == 0) {
3329 imageBaseType = EFI_IMAGE_TE_BASE_ADJUSTED;
3330 }
3331 }
3332 }
3333
3334 // Show message if imageBaseType is still unknown
3335 if (imageBaseType == EFI_IMAGE_TE_BASE_OTHER) {
3336 msg(usprintf("%s: TE image base is neither zero, nor original, nor adjusted, nor top-swapped", __FUNCTION__), index);
3337 }
3338
3339 // Update parsing data
3340 TE_IMAGE_SECTION_PARSING_DATA pdata;
3341 pdata.imageBaseType = imageBaseType;
3342 pdata.originalImageBase = originalImageBase;
3343 pdata.adjustedImageBase = adjustedImageBase;
3344 model->setParsingData(index, UByteArray((const char*)&pdata, sizeof(pdata)));
3345 }
3346 }
3347
3348 // Process child items
3349 for (int i = 0; i < model->rowCount(index); i++) {
3350 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
3351 checkTeImageBase(index.child(i, 0));
3352 #else
3353 checkTeImageBase(index.model()->index(i, 0, index));
3354 #endif
3355 }
3356
3357 return U_SUCCESS;
3358 }
3359
addInfoRecursive(const UModelIndex & index)3360 USTATUS FfsParser::addInfoRecursive(const UModelIndex & index)
3361 {
3362 // Sanity check
3363 if (!index.isValid())
3364 return U_INVALID_PARAMETER;
3365
3366 // Add offset
3367 model->addInfo(index, usprintf("Offset: %Xh\n", model->offset(index)), false);
3368
3369 // Add current base if the element is not compressed
3370 // or it's compressed, but its parent isn't
3371 if ((!model->compressed(index)) || (index.parent().isValid() && !model->compressed(index.parent()))) {
3372 // Add physical address of the whole item or its header and data portions separately
3373 UINT64 address = addressDiff + model->base(index);
3374 if (address <= 0xFFFFFFFFUL) {
3375 UINT32 headerSize = (UINT32)model->header(index).size();
3376 if (headerSize) {
3377 model->addInfo(index, usprintf("Data address: %08llXh\n", (unsigned long long)address + headerSize),false);
3378 model->addInfo(index, usprintf("Header address: %08llXh\n", (unsigned long long)address), false);
3379 }
3380 else {
3381 model->addInfo(index, usprintf("Address: %08llXh\n", (unsigned long long)address), false);
3382 }
3383 }
3384 // Add base
3385 model->addInfo(index, usprintf("Base: %Xh\n", model->base(index)), false);
3386 }
3387 model->addInfo(index, usprintf("Fixed: %s\n", model->fixed(index) ? "Yes" : "No"), false);
3388
3389 // Process child items
3390 for (int i = 0; i < model->rowCount(index); i++) {
3391 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
3392 addInfoRecursive(index.child(i, 0));
3393 #else
3394 addInfoRecursive(index.model()->index(i, 0, index));
3395 #endif
3396 }
3397
3398 return U_SUCCESS;
3399 }
3400
checkProtectedRanges(const UModelIndex & index)3401 USTATUS FfsParser::checkProtectedRanges(const UModelIndex & index)
3402 {
3403 // Sanity check
3404 if (!index.isValid())
3405 return U_INVALID_PARAMETER;
3406
3407 // Calculate digest for BG-protected ranges
3408 UByteArray protectedParts;
3409 bool bgProtectedRangeFound = false;
3410 try {
3411 for (UINT32 i = 0; i < (UINT32)bgProtectedRanges.size(); i++) {
3412 if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB && bgProtectedRanges[i].Size > 0) {
3413 bgProtectedRangeFound = true;
3414 if ((UINT64)bgProtectedRanges[i].Offset >= addressDiff) {
3415 bgProtectedRanges[i].Offset -= (UINT32)addressDiff;
3416 } else {
3417 // TODO: Explore this.
3418 msg(usprintf("%s: Suspicious BG protection offset", __FUNCTION__), index);
3419 }
3420 protectedParts += openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3421 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3422 }
3423 }
3424 } catch (...) {
3425 bgProtectedRangeFound = false;
3426 }
3427
3428 if (bgProtectedRangeFound) {
3429 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3430 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3431
3432 if (digest != bgBpDigest) {
3433 msg(usprintf("%s: BG-protected ranges hash mismatch, opened image may refuse to boot", __FUNCTION__), index);
3434 }
3435 }
3436 else if (bgBootPolicyFound) {
3437 msg(usprintf("%s: BootPolicy doesn't define any BG-protected ranges", __FUNCTION__), index);
3438 }
3439
3440 // Calculate digests for vendor-protected ranges
3441 for (UINT32 i = 0; i < (UINT32)bgProtectedRanges.size(); i++) {
3442 if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_AMI_OLD
3443 && bgProtectedRanges[i].Size != 0 && bgProtectedRanges[i].Size != 0xFFFFFFFF) {
3444 if (!bgDxeCoreIndex.isValid()) {
3445 msg(usprintf("%s: can't determine DXE volume offset, old AMI protected range hash can't be checked", __FUNCTION__), index);
3446 }
3447 else {
3448 // Offset will be determined as the offset of root volume with first DXE core
3449 UModelIndex dxeRootVolumeIndex = model->findLastParentOfType(bgDxeCoreIndex, Types::Volume);
3450 if (!dxeRootVolumeIndex.isValid()) {
3451 msg(usprintf("%s: can't determine DXE volume offset, old AMI protected range hash can't be checked", __FUNCTION__), index);
3452 }
3453 else {
3454 bgProtectedRanges[i].Offset = model->base(dxeRootVolumeIndex);
3455 protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3456
3457 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3458 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3459
3460 if (digest != bgProtectedRanges[i].Hash) {
3461 msg(usprintf("%s: old AMI protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__,
3462 bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size),
3463 model->findByBase(bgProtectedRanges[i].Offset));
3464 }
3465
3466 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3467 }
3468 }
3469 }
3470 else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB) {
3471 if (!bgDxeCoreIndex.isValid()) {
3472 msg(usprintf("%s: can't determine DXE volume offset, post-IBB protected range hash can't be checked", __FUNCTION__), index);
3473 }
3474 else {
3475 // Offset will be determined as the offset of root volume with first DXE core
3476 UModelIndex dxeRootVolumeIndex = model->findLastParentOfType(bgDxeCoreIndex, Types::Volume);
3477 if (!dxeRootVolumeIndex.isValid()) {
3478 msg(usprintf("%s: can't determine DXE volume offset, post-IBB protected range hash can't be checked", __FUNCTION__), index);
3479 }
3480 else
3481 {
3482 bgProtectedRanges[i].Offset = model->base(dxeRootVolumeIndex);
3483 bgProtectedRanges[i].Size = (UINT32)(model->header(dxeRootVolumeIndex).size() + model->body(dxeRootVolumeIndex).size() + model->tail(dxeRootVolumeIndex).size());
3484 protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3485
3486 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3487 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3488
3489 if (digest != bgProtectedRanges[i].Hash) {
3490 msg(usprintf("%s: post-IBB protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__,
3491 bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size),
3492 model->findByBase(bgProtectedRanges[i].Offset));
3493 }
3494
3495 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3496 }
3497 }
3498 }
3499 else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_AMI_NEW
3500 && bgProtectedRanges[i].Size != 0 && bgProtectedRanges[i].Size != 0xFFFFFFFF
3501 && bgProtectedRanges[i].Offset != 0 && bgProtectedRanges[i].Offset != 0xFFFFFFFF) {
3502
3503 if ((UINT64)bgProtectedRanges[i].Offset >= addressDiff) {
3504 bgProtectedRanges[i].Offset -= (UINT32)addressDiff;
3505 protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3506
3507 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3508 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3509
3510 if (digest != bgProtectedRanges[i].Hash) {
3511 msg(usprintf("%s: AMI protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__,
3512 bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size),
3513 model->findByBase(bgProtectedRanges[i].Offset));
3514 }
3515
3516 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3517 } else {
3518 // TODO: Explore this.
3519 msg(usprintf("%s: Suspicious AMI new BG protection offset", __FUNCTION__), index);
3520 }
3521 }
3522 else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_PHOENIX
3523 && bgProtectedRanges[i].Size != 0 && bgProtectedRanges[i].Size != 0xFFFFFFFF
3524 && bgProtectedRanges[i].Offset != 0xFFFFFFFF) {
3525 bgProtectedRanges[i].Offset += (UINT32)bgProtectedRegionsBase;
3526 protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3527
3528 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3529 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3530
3531 if (digest != bgProtectedRanges[i].Hash) {
3532 msg(usprintf("%s: Phoenix protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__,
3533 bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size),
3534 model->findByBase(bgProtectedRanges[i].Offset));
3535 }
3536
3537 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3538 }
3539 else if (bgProtectedRanges[i].Type == BG_PROTECTED_RANGE_VENDOR_HASH_MICROSOFT
3540 && bgProtectedRanges[i].Size != 0 && bgProtectedRanges[i].Size != 0xFFFFFFFF
3541 && bgProtectedRanges[i].Offset != 0 && bgProtectedRanges[i].Offset != 0xFFFFFFFF) {
3542 bgProtectedRanges[i].Offset -= (UINT32)addressDiff;
3543 protectedParts = openedImage.mid(bgProtectedRanges[i].Offset, bgProtectedRanges[i].Size);
3544
3545 UByteArray digest(SHA256_DIGEST_SIZE, '\x00');
3546 sha256(protectedParts.constData(), protectedParts.size(), digest.data());
3547
3548 if (digest != bgProtectedRanges[i].Hash) {
3549 msg(usprintf("%s: Microsoft protected range [%Xh:%Xh] hash mismatch, opened image may refuse to boot", __FUNCTION__,
3550 bgProtectedRanges[i].Offset, bgProtectedRanges[i].Offset + bgProtectedRanges[i].Size),
3551 model->findByBase(bgProtectedRanges[i].Offset));
3552 }
3553
3554 markProtectedRangeRecursive(index, bgProtectedRanges[i]);
3555 }
3556 }
3557
3558 return U_SUCCESS;
3559 }
3560
markProtectedRangeRecursive(const UModelIndex & index,const BG_PROTECTED_RANGE & range)3561 USTATUS FfsParser::markProtectedRangeRecursive(const UModelIndex & index, const BG_PROTECTED_RANGE & range)
3562 {
3563 if (!index.isValid())
3564 return U_SUCCESS;
3565
3566 // Mark compressed items
3567 UModelIndex parentIndex = model->parent(index);
3568 if (parentIndex.isValid() && model->compressed(index) && model->compressed(parentIndex)) {
3569 model->setMarking(index, model->marking(parentIndex));
3570 }
3571 // Mark normal items
3572 else {
3573 UINT32 currentOffset = model->base(index);
3574 UINT32 currentSize = (UINT32)(model->header(index).size() + model->body(index).size() + model->tail(index).size());
3575
3576 if (std::min(currentOffset + currentSize, range.Offset + range.Size) > std::max(currentOffset, range.Offset)) {
3577 if (range.Offset <= currentOffset && currentOffset + currentSize <= range.Offset + range.Size) { // Mark as fully in range
3578 if (range.Type == BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB) {
3579 model->setMarking(index, Qt::red);
3580 }
3581 else {
3582 model->setMarking(index, Qt::cyan);
3583 }
3584 }
3585 else { // Mark as partially in range
3586 model->setMarking(index, Qt::yellow);
3587 }
3588 }
3589 }
3590
3591 for (int i = 0; i < model->rowCount(index); i++) {
3592 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
3593 markProtectedRangeRecursive(index.child(i, 0), range);
3594 #else
3595 markProtectedRangeRecursive(index.model()->index(i, 0, index), range);
3596 #endif
3597 }
3598
3599 return U_SUCCESS;
3600 }
3601
parseVendorHashFile(const UByteArray & fileGuid,const UModelIndex & index)3602 USTATUS FfsParser::parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index)
3603 {
3604 if (!index.isValid())
3605 return EFI_INVALID_PARAMETER;
3606
3607 if (fileGuid == BG_VENDOR_HASH_FILE_GUID_PHOENIX) {
3608 const UByteArray &body = model->body(index);
3609 UINT32 size = (UINT32)body.size();
3610
3611 // File too small to have even a signature
3612 if (size < sizeof(BG_VENDOR_HASH_FILE_SIGNATURE_PHOENIX)) {
3613 msg(usprintf("%s: unknown or corrupted Phoenix hash file found", __FUNCTION__), index);
3614 model->setText(index, UString("Phoenix hash file"));
3615 return U_INVALID_FILE;
3616 }
3617
3618 const BG_VENDOR_HASH_FILE_HEADER_PHOENIX* header = (const BG_VENDOR_HASH_FILE_HEADER_PHOENIX*)body.constData();
3619 if (header->Signature == BG_VENDOR_HASH_FILE_SIGNATURE_PHOENIX) {
3620 if (size < sizeof(BG_VENDOR_HASH_FILE_HEADER_PHOENIX) ||
3621 size < sizeof(BG_VENDOR_HASH_FILE_HEADER_PHOENIX) + header->NumEntries * sizeof(BG_VENDOR_HASH_FILE_ENTRY)) {
3622 msg(usprintf("%s: unknown or corrupted Phoenix hash file found", __FUNCTION__), index);
3623 model->setText(index, UString("Phoenix hash file"));
3624 return U_INVALID_FILE;
3625 }
3626
3627 if (header->NumEntries > 0) {
3628 bool protectedRangesFound = false;
3629 for (UINT32 i = 0; i < header->NumEntries; i++) {
3630 protectedRangesFound = true;
3631 const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(header + 1) + i;
3632
3633 BG_PROTECTED_RANGE range;
3634 range.Offset = entry->Offset;
3635 range.Size = entry->Size;
3636 range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash));
3637 range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_PHOENIX;
3638 bgProtectedRanges.push_back(range);
3639 }
3640
3641 if (protectedRangesFound) {
3642 securityInfo += usprintf("Phoenix hash file found at base %Xh\nProtected ranges:", model->base(index));
3643 for (UINT32 i = 0; i < header->NumEntries; i++) {
3644 const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(header + 1) + i;
3645 securityInfo += usprintf("\nRelativeOffset: %08Xh Size: %Xh\nHash: ", entry->Offset, entry->Size);
3646 for (UINT8 j = 0; j < sizeof(entry->Hash); j++) {
3647 securityInfo += usprintf("%02X", entry->Hash[j]);
3648 }
3649 }
3650 securityInfo += UString("\n------------------------------------------------------------------------\n\n");
3651 }
3652
3653 msg(usprintf("%s: Phoenix hash file found", __FUNCTION__), index);
3654 }
3655 else {
3656 msg(usprintf("%s: empty Phoenix hash file found", __FUNCTION__), index);
3657 }
3658
3659 model->setText(index, UString("Phoenix hash file"));
3660 }
3661 }
3662 else if (fileGuid == BG_VENDOR_HASH_FILE_GUID_AMI) {
3663 UModelIndex fileIndex = model->parent(index);
3664 const UByteArray &body = model->body(index);
3665 UINT32 size = (UINT32)body.size();
3666 if (size != (UINT32)body.count('\xFF')) {
3667 if (size == sizeof(BG_VENDOR_HASH_FILE_HEADER_AMI_NEW)) {
3668 bool protectedRangesFound = false;
3669 UINT32 NumEntries = (UINT32)body.size() / sizeof(BG_VENDOR_HASH_FILE_ENTRY);
3670 for (UINT32 i = 0; i < NumEntries; i++) {
3671 protectedRangesFound = true;
3672 const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(body.constData()) + i;
3673 BG_PROTECTED_RANGE range;
3674 range.Offset = entry->Offset;
3675 range.Size = entry->Size;
3676 range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash));
3677 range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_AMI_NEW;
3678 bgProtectedRanges.push_back(range);
3679 }
3680
3681 if (protectedRangesFound) {
3682 securityInfo += usprintf("New AMI hash file found at base %Xh\nProtected ranges:", model->base(fileIndex));
3683 for (UINT32 i = 0; i < NumEntries; i++) {
3684 const BG_VENDOR_HASH_FILE_ENTRY* entry = (const BG_VENDOR_HASH_FILE_ENTRY*)(body.constData()) + i;
3685 securityInfo += usprintf("\nAddress: %08Xh Size: %Xh\nHash: ", entry->Offset, entry->Size);
3686 for (UINT8 j = 0; j < sizeof(entry->Hash); j++) {
3687 securityInfo += usprintf("%02X", entry->Hash[j]);
3688 }
3689 }
3690 securityInfo += UString("\n------------------------------------------------------------------------\n\n");
3691 }
3692
3693 msg(usprintf("%s: new AMI hash file found", __FUNCTION__), fileIndex);
3694 }
3695 else if (size == sizeof(BG_VENDOR_HASH_FILE_HEADER_AMI_OLD)) {
3696 securityInfo += usprintf("Old AMI hash file found at base %Xh\nProtected range:", model->base(fileIndex));
3697 const BG_VENDOR_HASH_FILE_HEADER_AMI_OLD* entry = (const BG_VENDOR_HASH_FILE_HEADER_AMI_OLD*)(body.constData());
3698 securityInfo += usprintf("\nSize: %Xh\nHash: ", entry->Size);
3699 for (UINT8 i = 0; i < sizeof(entry->Hash); i++) {
3700 securityInfo += usprintf("%02X", entry->Hash[i]);
3701 }
3702 securityInfo += UString("\n------------------------------------------------------------------------\n\n");
3703
3704 BG_PROTECTED_RANGE range;
3705 range.Offset = 0;
3706 range.Size = entry->Size;
3707 range.Hash = UByteArray((const char*)entry->Hash, sizeof(entry->Hash));
3708 range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_AMI_OLD;
3709 bgProtectedRanges.push_back(range);
3710
3711 msg(usprintf("%s: old AMI hash file found", __FUNCTION__), fileIndex);
3712 }
3713 else {
3714 msg(usprintf("%s: unknown or corrupted AMI hash file found", __FUNCTION__), index);
3715 }
3716 }
3717 else {
3718 msg(usprintf("%s: empty AMI hash file found", __FUNCTION__), fileIndex);
3719 }
3720
3721 model->setText(fileIndex, UString("AMI hash file"));
3722 }
3723
3724 return U_SUCCESS;
3725 }
3726
3727 #ifndef U_ENABLE_FIT_PARSING_SUPPORT
parseFit(const UModelIndex & index)3728 USTATUS FfsParser::parseFit(const UModelIndex & index)
3729 {
3730 U_UNUSED_PARAMETER(index);
3731 return U_SUCCESS;
3732 }
3733
3734 #else
parseFit(const UModelIndex & index)3735 USTATUS FfsParser::parseFit(const UModelIndex & index)
3736 {
3737 // Check sanity
3738 if (!index.isValid())
3739 return EFI_INVALID_PARAMETER;
3740
3741 // Search for FIT
3742 UModelIndex fitIndex;
3743 UINT32 fitOffset;
3744 findFitRecursive(index, fitIndex, fitOffset);
3745
3746 // FIT not found
3747 if (!fitIndex.isValid())
3748 return U_SUCCESS;
3749
3750 // Explicitly set the item containing FIT as fixed
3751 model->setFixed(fitIndex, true);
3752
3753 // Special case of FIT header
3754 UByteArray fitBody = model->body(fitIndex);
3755 const FIT_ENTRY* fitHeader = (const FIT_ENTRY*)(fitBody.constData() + fitOffset);
3756
3757 // Check FIT checksum, if present
3758 UINT32 fitSize = fitHeader->Size * sizeof(FIT_ENTRY);
3759 if (fitHeader->CsFlag) {
3760 // Calculate FIT entry checksum
3761 UByteArray tempFIT = model->body(fitIndex).mid(fitOffset, fitSize);
3762 FIT_ENTRY* tempFitHeader = (FIT_ENTRY*)tempFIT.data();
3763 tempFitHeader->Checksum = 0;
3764 UINT8 calculated = calculateChecksum8((const UINT8*)tempFitHeader, fitSize);
3765 if (calculated != fitHeader->Checksum) {
3766 msg(usprintf("%s: invalid FIT table checksum %02Xh, should be %02Xh", __FUNCTION__, fitHeader->Checksum, calculated), fitIndex);
3767 }
3768 }
3769
3770 // Check fit header type
3771 if (fitHeader->Type != FIT_TYPE_HEADER) {
3772 msg(UString("Invalid FIT header type"), fitIndex);
3773 return U_INVALID_FIT;
3774 }
3775
3776 // Add FIT header
3777 std::vector<UString> currentStrings;
3778 currentStrings.push_back(UString("_FIT_ "));
3779 currentStrings.push_back(usprintf("%08Xh", fitSize));
3780 currentStrings.push_back(usprintf("%04Xh", fitHeader->Version));
3781 currentStrings.push_back(usprintf("%02Xh", fitHeader->Checksum));
3782 currentStrings.push_back(fitEntryTypeToUString(fitHeader->Type));
3783 currentStrings.push_back(UString()); // Empty info for FIT header
3784 fitTable.push_back(std::pair<std::vector<UString>, UModelIndex>(currentStrings, fitIndex));
3785
3786 // Process all other entries
3787 UModelIndex acmIndex;
3788 UModelIndex kmIndex;
3789 UModelIndex bpIndex;
3790 for (UINT32 i = 1; i < fitHeader->Size; i++) {
3791 currentStrings.clear();
3792 UString info;
3793 UModelIndex itemIndex;
3794 const FIT_ENTRY* currentEntry = fitHeader + i;
3795 UINT32 currentEntrySize = currentEntry->Size;
3796
3797 // Check sanity
3798 if (currentEntry->Type == FIT_TYPE_HEADER) {
3799 msg(usprintf("%s: second FIT header found, the table is damaged", __FUNCTION__), fitIndex);
3800 return U_INVALID_FIT;
3801 }
3802
3803 // Special case of version 0 entries
3804 if (currentEntry->Version == 0) {
3805 const FIT_ENTRY_VERSION_0_CONFIG_POLICY* policy = (const FIT_ENTRY_VERSION_0_CONFIG_POLICY*)currentEntry;
3806 info += usprintf("Index: %04Xh BitPosition: %02Xh AccessWidth: %02Xh DataRegAddr: %04Xh IndexRegAddr: %04Xh",
3807 policy->Index,
3808 policy->BitPosition,
3809 policy->AccessWidth,
3810 policy->DataRegisterAddress,
3811 policy->IndexRegisterAddress);
3812 }
3813 else if (currentEntry->Address > addressDiff && currentEntry->Address < 0xFFFFFFFFUL) { // Only elements in the image need to be parsed
3814 UINT32 currentEntryBase = (UINT32)(currentEntry->Address - addressDiff);
3815 itemIndex = model->findByBase(currentEntryBase);
3816 if (itemIndex.isValid()) {
3817 USTATUS status = U_INVALID_FIT;
3818 UByteArray item = model->header(itemIndex) + model->body(itemIndex) + model->tail(itemIndex);
3819 UINT32 localOffset = currentEntryBase - model->base(itemIndex);
3820
3821 switch (currentEntry->Type) {
3822 case FIT_TYPE_MICROCODE:
3823 status = parseFitEntryMicrocode(item, localOffset, itemIndex, info, currentEntrySize);
3824 break;
3825
3826 case FIT_TYPE_BIOS_AC_MODULE:
3827 status = parseFitEntryAcm(item, localOffset, itemIndex, info, currentEntrySize);
3828 acmIndex = itemIndex;
3829 break;
3830
3831 case FIT_TYPE_AC_KEY_MANIFEST:
3832 status = parseFitEntryBootGuardKeyManifest(item, localOffset, itemIndex, info, currentEntrySize);
3833 kmIndex = itemIndex;
3834 break;
3835
3836 case FIT_TYPE_AC_BOOT_POLICY:
3837 status = parseFitEntryBootGuardBootPolicy(item, localOffset, itemIndex, info, currentEntrySize);
3838 bpIndex = itemIndex;
3839 break;
3840
3841 default:
3842 // Do nothing
3843 status = U_SUCCESS;
3844 break;
3845 }
3846
3847 if (status != U_SUCCESS)
3848 itemIndex = UModelIndex();
3849 }
3850 else {
3851 msg(usprintf("%s: FIT entry #%d not found in the image", __FUNCTION__, i), fitIndex);
3852 }
3853 }
3854
3855 if (itemIndex.isValid()) {
3856 // Explicitly set the item referenced by FIT as fixed
3857 // TODO: lift this restriction after FIT builder is ready
3858 model->setFixed(itemIndex, true);
3859 }
3860
3861 // Add entry to fitTable
3862 currentStrings.push_back(usprintf("%016" PRIX64 "h", currentEntry->Address));
3863 currentStrings.push_back(usprintf("%08Xh", currentEntrySize));
3864 currentStrings.push_back(usprintf("%04Xh", currentEntry->Version));
3865 currentStrings.push_back(usprintf("%02Xh", currentEntry->Checksum));
3866 currentStrings.push_back(fitEntryTypeToUString(currentEntry->Type));
3867 currentStrings.push_back(info);
3868 fitTable.push_back(std::pair<std::vector<UString>, UModelIndex>(currentStrings, itemIndex));
3869 }
3870
3871 // Perform validation of BootGuard stuff
3872 if (bgAcmFound) {
3873 if (!bgKeyManifestFound) {
3874 msg(usprintf("%s: ACM found, but KeyManifest is not", __FUNCTION__), acmIndex);
3875 }
3876 else if (!bgBootPolicyFound) {
3877 msg(usprintf("%s: ACM and KeyManifest found, BootPolicy is not", __FUNCTION__), kmIndex);
3878 }
3879 else {
3880 // Check key hashes
3881 if (!bgKmHash.isEmpty() && bgBpHash.isEmpty() && bgKmHash != bgBpHash) {
3882 msg(usprintf("%s: BootPolicy key hash stored in KeyManifest differs from the hash of public key stored in BootPolicy", __FUNCTION__), bpIndex);
3883 return U_SUCCESS;
3884 }
3885 }
3886 }
3887
3888 return U_SUCCESS;
3889 }
3890
findFitRecursive(const UModelIndex & index,UModelIndex & found,UINT32 & fitOffset)3891 void FfsParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset)
3892 {
3893 // Sanity check
3894 if (!index.isValid()) {
3895 return;
3896 }
3897
3898 // Process child items
3899 for (int i = 0; i < model->rowCount(index); i++) {
3900 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
3901 findFitRecursive(index.child(i, 0), found, fitOffset);
3902 #else
3903 findFitRecursive(index.model()->index(i, 0, index), found, fitOffset);
3904 #endif
3905
3906 if (found.isValid())
3907 return;
3908 }
3909
3910 // Check for all FIT signatures in item's body
3911 UByteArray lastVtfBody = model->body(lastVtf);
3912 UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - FIT_POINTER_OFFSET);
3913 for (INT32 offset = (INT32)model->body(index).indexOf(FIT_SIGNATURE);
3914 offset >= 0;
3915 offset = (INT32)model->body(index).indexOf(FIT_SIGNATURE, offset + 1)) {
3916 // FIT candidate found, calculate its physical address
3917 UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)addressDiff + model->header(index).size() + (UINT32)offset);
3918
3919 // Check FIT address to be stored in the last VTF
3920 if (fitAddress == storedFitAddress) {
3921 found = index;
3922 fitOffset = offset;
3923 msg(usprintf("%s: real FIT table found at physical address %08Xh", __FUNCTION__, fitAddress), found);
3924 break;
3925 }
3926 else if (model->rowCount(index) == 0) // Show messages only to leaf items
3927 msg(usprintf("%s: FIT table candidate found, but not referenced from the last VTF", __FUNCTION__), index);
3928 }
3929 }
3930
parseFitEntryMicrocode(const UByteArray & microcode,const UINT32 localOffset,const UModelIndex & parent,UString & info,UINT32 & realSize)3931 USTATUS FfsParser::parseFitEntryMicrocode(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
3932 {
3933 U_UNUSED_PARAMETER(parent);
3934 if ((UINT32)microcode.size() - localOffset < sizeof(INTEL_MICROCODE_HEADER)) {
3935 return U_INVALID_MICROCODE;
3936 }
3937
3938 const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset);
3939 if (!microcodeHeaderValid(ucodeHeader)) {
3940 return U_INVALID_MICROCODE;
3941 }
3942
3943 if ((UINT32)microcode.size() - localOffset < ucodeHeader->TotalSize) {
3944 return U_INVALID_MICROCODE;
3945 }
3946
3947 // Valid microcode found
3948 info = usprintf("CpuSignature: %08Xh, Revision: %08Xh, Date: %02X.%02X.%04X",
3949 ucodeHeader->ProcessorSignature,
3950 ucodeHeader->UpdateRevision,
3951 ucodeHeader->DateDay,
3952 ucodeHeader->DateMonth,
3953 ucodeHeader->DateYear);
3954 realSize = ucodeHeader->TotalSize;
3955 return U_SUCCESS;
3956 }
3957
parseFitEntryAcm(const UByteArray & acm,const UINT32 localOffset,const UModelIndex & parent,UString & info,UINT32 & realSize)3958 USTATUS FfsParser::parseFitEntryAcm(const UByteArray & acm, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
3959 {
3960 if ((UINT32)acm.size() < localOffset + sizeof(INTEL_ACM_HEADER)) {
3961 return U_INVALID_ACM;
3962 }
3963
3964 const INTEL_ACM_HEADER* header = (const INTEL_ACM_HEADER*)(acm.constData() + localOffset);
3965 if (header->ModuleType != INTEL_ACM_MODULE_TYPE || header->ModuleVendor != INTEL_ACM_MODULE_VENDOR) {
3966 return U_INVALID_ACM;
3967 }
3968
3969 UINT32 acmSize = header->ModuleSize * sizeof(UINT32);
3970 if ((UINT32)acm.size() < localOffset + acmSize) {
3971 return U_INVALID_ACM;
3972 }
3973
3974 // Valid ACM found
3975 info = usprintf("LocalOffset: %08Xh, EntryPoint: %08Xh, ACM SVN: %04Xh, Date: %02X.%02X.%04X",
3976 localOffset,
3977 header->EntryPoint,
3978 header->AcmSvn,
3979 header->DateDay,
3980 header->DateMonth,
3981 header->DateYear
3982 );
3983 realSize = acmSize;
3984
3985 // Add ACM header info
3986 UString acmInfo;
3987 acmInfo += usprintf(" found at base %Xh\n"
3988 "ModuleType: %04Xh ModuleSubtype: %04Xh HeaderLength: %08lXh\n"
3989 "HeaderVersion: %08Xh ChipsetId: %04Xh Flags: %04Xh\n"
3990 "ModuleVendor: %04Xh Date: %02X.%02X.%04X ModuleSize: %08lXh\n"
3991 "EntryPoint: %08Xh AcmSvn: %04Xh Unknown1: %08Xh\n"
3992 "Unknown2: %08Xh GdtBase: %08Xh GdtMax: %08Xh\n"
3993 "SegSel: %08Xh KeySize: %08lXh Unknown3: %08lXh",
3994 model->base(parent) + localOffset,
3995 header->ModuleType,
3996 header->ModuleSubtype,
3997 header->ModuleSize * sizeof(UINT32),
3998 header->HeaderVersion,
3999 header->ChipsetId,
4000 header->Flags,
4001 header->ModuleVendor,
4002 header->DateDay, header->DateMonth, header->DateYear,
4003 header->ModuleSize * sizeof(UINT32),
4004 header->EntryPoint,
4005 header->AcmSvn,
4006 header->Unknown1,
4007 header->Unknown2,
4008 header->GdtBase,
4009 header->GdtMax,
4010 header->SegmentSel,
4011 header->KeySize * sizeof(UINT32),
4012 header->Unknown4 * sizeof(UINT32)
4013 );
4014 // Add PubKey
4015 acmInfo += usprintf("\n\nACM RSA Public Key (Exponent: %Xh):", header->RsaPubExp);
4016 for (UINT16 i = 0; i < sizeof(header->RsaPubKey); i++) {
4017 if (i % 32 == 0)
4018 acmInfo += UString("\n");
4019 acmInfo += usprintf("%02X", header->RsaPubKey[i]);
4020 }
4021 // Add RsaSig
4022 acmInfo += UString("\n\nACM RSA Signature:");
4023 for (UINT16 i = 0; i < sizeof(header->RsaSig); i++) {
4024 if (i % 32 == 0)
4025 acmInfo += UString("\n");
4026 acmInfo += usprintf("%02X", header->RsaSig[i]);
4027 }
4028 acmInfo += UString("\n------------------------------------------------------------------------\n\n");
4029
4030 if(header->ModuleSubtype == INTEL_ACM_MODULE_SUBTYPE_TXT_ACM)
4031 securityInfo += "TXT ACM" + acmInfo;
4032 else if(header->ModuleSubtype == INTEL_ACM_MODULE_SUBTYPE_S_ACM)
4033 securityInfo += "S-ACM" + acmInfo;
4034 else if (header->ModuleSubtype == INTEL_ACM_MODULE_SUBTYPE_BOOTGUARD)
4035 securityInfo += "BootGuard ACM" + acmInfo;
4036 else
4037 securityInfo += "Intel ACM" + acmInfo;
4038
4039 bgAcmFound = true;
4040 return U_SUCCESS;
4041 }
4042
parseFitEntryBootGuardKeyManifest(const UByteArray & keyManifest,const UINT32 localOffset,const UModelIndex & parent,UString & info,UINT32 & realSize)4043 USTATUS FfsParser::parseFitEntryBootGuardKeyManifest(const UByteArray & keyManifest, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
4044 {
4045 U_UNUSED_PARAMETER(realSize);
4046 if ((UINT32)keyManifest.size() < localOffset + sizeof(BG_KEY_MANIFEST)) {
4047 return U_INVALID_BG_KEY_MANIFEST;
4048 }
4049
4050 const BG_KEY_MANIFEST* header = (const BG_KEY_MANIFEST*)(keyManifest.constData() + localOffset);
4051 if (header->Tag != BG_KEY_MANIFEST_TAG) {
4052 return U_INVALID_BG_KEY_MANIFEST;
4053 }
4054
4055 // Valid KM found
4056 info = usprintf("LocalOffset: %08Xh, KM Version: %02Xh, KM SVN: %02Xh, KM ID: %02Xh",
4057 localOffset,
4058 header->KmVersion,
4059 header->KmSvn,
4060 header->KmId
4061 );
4062
4063 // Add KM header info
4064 securityInfo += usprintf(
4065 "Intel BootGuard Key manifest found at base %Xh\n"
4066 "Tag: __KEYM__ Version: %02Xh KmVersion: %02Xh KmSvn: %02Xh KmId: %02Xh",
4067 model->base(parent) + localOffset,
4068 header->Version,
4069 header->KmVersion,
4070 header->KmSvn,
4071 header->KmId
4072 );
4073
4074 // Add hash of Key Manifest PubKey, this hash will be written to FPFs
4075 UINT8 hash[SHA256_DIGEST_SIZE];
4076 sha256(&header->KeyManifestSignature.PubKey.Modulus, sizeof(header->KeyManifestSignature.PubKey.Modulus), hash);
4077 securityInfo += UString("\n\nKey Manifest RSA Public Key Hash:\n");
4078 for (UINT8 i = 0; i < sizeof(hash); i++) {
4079 securityInfo += usprintf("%02X", hash[i]);
4080 }
4081
4082 // Add BpKeyHash
4083 securityInfo += UString("\n\nBoot Policy RSA Public Key Hash:\n");
4084 for (UINT8 i = 0; i < sizeof(header->BpKeyHash.HashBuffer); i++) {
4085 securityInfo += usprintf("%02X", header->BpKeyHash.HashBuffer[i]);
4086 }
4087 bgKmHash = UByteArray((const char*)header->BpKeyHash.HashBuffer, sizeof(header->BpKeyHash.HashBuffer));
4088
4089 // Add Key Manifest PubKey
4090 securityInfo += usprintf("\n\nKey Manifest RSA Public Key (Exponent: %Xh):",
4091 header->KeyManifestSignature.PubKey.Exponent);
4092 for (UINT16 i = 0; i < sizeof(header->KeyManifestSignature.PubKey.Modulus); i++) {
4093 if (i % 32 == 0)
4094 securityInfo += UString("\n");
4095 securityInfo += usprintf("%02X", header->KeyManifestSignature.PubKey.Modulus[i]);
4096 }
4097 // Add Key Manifest Signature
4098 securityInfo += UString("\n\nKey Manifest RSA Signature:");
4099 for (UINT16 i = 0; i < sizeof(header->KeyManifestSignature.Signature.Signature); i++) {
4100 if (i % 32 == 0)
4101 securityInfo += UString("\n");
4102 securityInfo += usprintf("%02X", header->KeyManifestSignature.Signature.Signature[i]);
4103 }
4104 securityInfo += UString("\n------------------------------------------------------------------------\n\n");
4105 bgKeyManifestFound = true;
4106 return U_SUCCESS;
4107 }
4108
findNextBootGuardBootPolicyElement(const UByteArray & bootPolicy,const UINT32 elementOffset,UINT32 & nextElementOffset,UINT32 & nextElementSize)4109 USTATUS FfsParser::findNextBootGuardBootPolicyElement(const UByteArray & bootPolicy, const UINT32 elementOffset, UINT32 & nextElementOffset, UINT32 & nextElementSize)
4110 {
4111 UINT32 dataSize = (UINT32)bootPolicy.size();
4112 if (dataSize < sizeof(UINT64)) {
4113 return U_ELEMENTS_NOT_FOUND;
4114 }
4115
4116 UINT32 offset = elementOffset;
4117 for (; offset < dataSize - sizeof(UINT64); offset++) {
4118 const UINT64* currentPos = (const UINT64*)(bootPolicy.constData() + offset);
4119 if (*currentPos == BG_BOOT_POLICY_MANIFEST_IBB_ELEMENT_TAG && offset + sizeof(BG_IBB_ELEMENT) < dataSize) {
4120 const BG_IBB_ELEMENT* header = (const BG_IBB_ELEMENT*)currentPos;
4121 // Check that all segments are present
4122 if (offset + sizeof(BG_IBB_ELEMENT) + sizeof(BG_IBB_SEGMENT_ELEMENT) * header->IbbSegCount < dataSize) {
4123 nextElementOffset = offset;
4124 nextElementSize = sizeof(BG_IBB_ELEMENT) + sizeof(BG_IBB_SEGMENT_ELEMENT) * header->IbbSegCount;
4125 return U_SUCCESS;
4126 }
4127 }
4128 else if (*currentPos == BG_BOOT_POLICY_MANIFEST_PLATFORM_MANUFACTURER_ELEMENT_TAG && offset + sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) < dataSize) {
4129 const BG_PLATFORM_MANUFACTURER_ELEMENT* header = (const BG_PLATFORM_MANUFACTURER_ELEMENT*)currentPos;
4130 // Check that data is present
4131 if (offset + sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) + header->DataSize < dataSize) {
4132 nextElementOffset = offset;
4133 nextElementSize = sizeof(BG_PLATFORM_MANUFACTURER_ELEMENT) + header->DataSize;
4134 return U_SUCCESS;
4135 }
4136 }
4137 else if (*currentPos == BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_TAG && offset + sizeof(BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT) < dataSize) {
4138 nextElementOffset = offset;
4139 nextElementSize = sizeof(BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT);
4140 return U_SUCCESS;
4141 }
4142 }
4143
4144 return U_ELEMENTS_NOT_FOUND;
4145 }
4146
parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolicy,const UINT32 localOffset,const UModelIndex & parent,UString & info,UINT32 & realSize)4147 USTATUS FfsParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolicy, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize)
4148 {
4149 U_UNUSED_PARAMETER(realSize);
4150 if ((UINT32)bootPolicy.size() < localOffset + sizeof(BG_BOOT_POLICY_MANIFEST_HEADER)) {
4151 return U_INVALID_BG_BOOT_POLICY;
4152 }
4153
4154 const BG_BOOT_POLICY_MANIFEST_HEADER* header = (const BG_BOOT_POLICY_MANIFEST_HEADER*)(bootPolicy.constData() + localOffset);
4155 if (header->Tag != BG_BOOT_POLICY_MANIFEST_HEADER_TAG) {
4156 return U_INVALID_BG_BOOT_POLICY;
4157 }
4158
4159 UINT32 bmSize = sizeof(BG_BOOT_POLICY_MANIFEST_HEADER);
4160 if ((UINT32)bootPolicy.size() < localOffset + bmSize) {
4161 return U_INVALID_BG_BOOT_POLICY;
4162 }
4163
4164 // Valid BPM found
4165 info = usprintf("LocalOffset: %08Xh, BP SVN: %02Xh, ACM SVN: %02Xh",
4166 localOffset,
4167 header->BPSVN,
4168 header->ACMSVN
4169 );
4170
4171 // Add BP header info
4172 securityInfo += usprintf(
4173 "Intel BootGuard Boot Policy Manifest found at base %Xh\n"
4174 "Tag: __ACBP__ Version: %02Xh HeaderVersion: %02Xh\n"
4175 "PMBPMVersion: %02Xh PBSVN: %02Xh ACMSVN: %02Xh NEMDataStack: %04Xh\n",
4176 model->base(parent) + localOffset,
4177 header->Version,
4178 header->HeaderVersion,
4179 header->PMBPMVersion,
4180 header->BPSVN,
4181 header->ACMSVN,
4182 header->NEMDataSize
4183 );
4184
4185 // Iterate over elements to get them all
4186 UINT32 elementOffset = 0;
4187 UINT32 elementSize = 0;
4188 USTATUS status = findNextBootGuardBootPolicyElement(bootPolicy, localOffset + sizeof(BG_BOOT_POLICY_MANIFEST_HEADER), elementOffset, elementSize);
4189 while (status == U_SUCCESS) {
4190 const UINT64* currentPos = (const UINT64*)(bootPolicy.constData() + elementOffset);
4191 if (*currentPos == BG_BOOT_POLICY_MANIFEST_IBB_ELEMENT_TAG) {
4192 const BG_IBB_ELEMENT* elementHeader = (const BG_IBB_ELEMENT*)currentPos;
4193 // Valid IBB element found
4194 securityInfo += usprintf(
4195 "\nInitial Boot Block Element found at base %Xh\n"
4196 "Tag: __IBBS__ Version: %02Xh Unknown: %02Xh\n"
4197 "Flags: %08Xh IbbMchBar: %08llXh VtdBar: %08llXh\n"
4198 "PmrlBase: %08Xh PmrlLimit: %08Xh EntryPoint: %08Xh",
4199 model->base(parent) + localOffset + elementOffset,
4200 elementHeader->Version,
4201 elementHeader->Unknown,
4202 elementHeader->Flags,
4203 (unsigned long long)elementHeader->IbbMchBar,
4204 (unsigned long long)elementHeader->VtdBar,
4205 elementHeader->PmrlBase,
4206 elementHeader->PmrlLimit,
4207 elementHeader->EntryPoint
4208 );
4209
4210 // Add PostIbbHash
4211 securityInfo += UString("\n\nPost IBB Hash:\n");
4212 for (UINT8 i = 0; i < sizeof(elementHeader->IbbHash.HashBuffer); i++) {
4213 securityInfo += usprintf("%02X", elementHeader->IbbHash.HashBuffer[i]);
4214 }
4215
4216 // Check for non-empry PostIbbHash
4217 UByteArray postIbbHash((const char*)elementHeader->IbbHash.HashBuffer, sizeof(elementHeader->IbbHash.HashBuffer));
4218 if (postIbbHash.count('\x00') != postIbbHash.size() && postIbbHash.count('\xFF') != postIbbHash.size()) {
4219 BG_PROTECTED_RANGE range;
4220 range.Type = BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB;
4221 range.Hash = postIbbHash;
4222 bgProtectedRanges.push_back(range);
4223 }
4224
4225 // Add Digest
4226 bgBpDigest = UByteArray((const char*)elementHeader->Digest.HashBuffer, sizeof(elementHeader->Digest.HashBuffer));
4227 securityInfo += UString("\n\nIBB Digest:\n");
4228 for (UINT8 i = 0; i < (UINT8)bgBpDigest.size(); i++) {
4229 securityInfo += usprintf("%02X", (UINT8)bgBpDigest.at(i));
4230 }
4231
4232 // Add all IBB segments
4233 securityInfo += UString("\n\nIBB Segments:\n");
4234 const BG_IBB_SEGMENT_ELEMENT* segments = (const BG_IBB_SEGMENT_ELEMENT*)(elementHeader + 1);
4235 for (UINT8 i = 0; i < elementHeader->IbbSegCount; i++) {
4236 securityInfo += usprintf("Flags: %04Xh Address: %08Xh Size: %08Xh\n",
4237 segments[i].Flags, segments[i].Base, segments[i].Size);
4238 if (segments[i].Flags == BG_IBB_SEGMENT_FLAG_IBB) {
4239 BG_PROTECTED_RANGE range;
4240 range.Offset = segments[i].Base;
4241 range.Size = segments[i].Size;
4242 range.Type = BG_PROTECTED_RANGE_INTEL_BOOT_GUARD_IBB;
4243 bgProtectedRanges.push_back(range);
4244 }
4245 }
4246 }
4247 else if (*currentPos == BG_BOOT_POLICY_MANIFEST_PLATFORM_MANUFACTURER_ELEMENT_TAG) {
4248 const BG_PLATFORM_MANUFACTURER_ELEMENT* elementHeader = (const BG_PLATFORM_MANUFACTURER_ELEMENT*)currentPos;
4249 securityInfo += usprintf(
4250 "\nPlatform Manufacturer Data Element found at base %Xh\n"
4251 "Tag: __PMDA__ Version: %02Xh DataSize: %02Xh",
4252 model->base(parent) + localOffset + elementOffset,
4253 elementHeader->Version,
4254 elementHeader->DataSize
4255 );
4256 // Check for Microsoft PMDA hash data
4257 const BG_MICROSOFT_PMDA_HEADER* pmdaHeader = (const BG_MICROSOFT_PMDA_HEADER*)(elementHeader + 1);
4258 if (pmdaHeader->Version == BG_MICROSOFT_PMDA_VERSION
4259 && elementHeader->DataSize == sizeof(BG_MICROSOFT_PMDA_HEADER) + sizeof(BG_MICROSOFT_PMDA_ENTRY)*pmdaHeader->NumEntries) {
4260 // Add entries
4261 securityInfo += UString("\nMicrosoft PMDA-based protected ranges:\n");
4262 const BG_MICROSOFT_PMDA_ENTRY* entries = (const BG_MICROSOFT_PMDA_ENTRY*)(pmdaHeader + 1);
4263 for (UINT32 i = 0; i < pmdaHeader->NumEntries; i++) {
4264
4265 securityInfo += usprintf("Address: %08Xh Size: %08Xh\n", entries[i].Address, entries[i].Size);
4266 securityInfo += UString("Hash: ");
4267 for (UINT8 j = 0; j < sizeof(entries[i].Hash); j++) {
4268 securityInfo += usprintf("%02X", entries[i].Hash[j]);
4269 }
4270 securityInfo += UString("\n");
4271
4272 BG_PROTECTED_RANGE range;
4273 range.Offset = entries[i].Address;
4274 range.Size = entries[i].Size;
4275 range.Hash = UByteArray((const char*)entries[i].Hash, sizeof(entries[i].Hash));
4276 range.Type = BG_PROTECTED_RANGE_VENDOR_HASH_MICROSOFT;
4277 bgProtectedRanges.push_back(range);
4278 }
4279 }
4280 else {
4281 // Add raw data
4282 const UINT8* data = (const UINT8*)(elementHeader + 1);
4283 for (UINT16 i = 0; i < elementHeader->DataSize; i++) {
4284 if (i % 32 == 0)
4285 securityInfo += UString("\n");
4286 securityInfo += usprintf("%02X", data[i]);
4287 }
4288 securityInfo += UString("\n");
4289 }
4290 }
4291 else if (*currentPos == BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT_TAG) {
4292 const BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT* elementHeader = (const BG_BOOT_POLICY_MANIFEST_SIGNATURE_ELEMENT*)currentPos;
4293 securityInfo += usprintf(
4294 "\nBoot Policy Signature Element found at base %Xh\n"
4295 "Tag: __PMSG__ Version: %02Xh",
4296 model->base(parent) + localOffset + elementOffset,
4297 elementHeader->Version
4298 );
4299
4300 // Add PubKey
4301 securityInfo += usprintf("\n\nBoot Policy RSA Public Key (Exponent: %Xh):", elementHeader->KeySignature.PubKey.Exponent);
4302 for (UINT16 i = 0; i < sizeof(elementHeader->KeySignature.PubKey.Modulus); i++) {
4303 if (i % 32 == 0)
4304 securityInfo += UString("\n");
4305 securityInfo += usprintf("%02X", elementHeader->KeySignature.PubKey.Modulus[i]);
4306 }
4307
4308 // Calculate and add PubKey hash
4309 UINT8 hash[SHA256_DIGEST_SIZE];
4310 sha256(&elementHeader->KeySignature.PubKey.Modulus, sizeof(elementHeader->KeySignature.PubKey.Modulus), hash);
4311 securityInfo += UString("\n\nBoot Policy RSA Public Key Hash:");
4312 for (UINT8 i = 0; i < sizeof(hash); i++) {
4313 if (i % 32 == 0)
4314 securityInfo += UString("\n");
4315 securityInfo += usprintf("%02X", hash[i]);
4316 }
4317 bgBpHash = UByteArray((const char*)hash, sizeof(hash));
4318
4319 // Add Signature
4320 securityInfo += UString("\n\nBoot Policy RSA Signature:");
4321 for (UINT16 i = 0; i < sizeof(elementHeader->KeySignature.Signature.Signature); i++) {
4322 if (i % 32 == 0)
4323 securityInfo += UString("\n");
4324 securityInfo += usprintf("%02X", elementHeader->KeySignature.Signature.Signature[i]);
4325 }
4326 }
4327 status = findNextBootGuardBootPolicyElement(bootPolicy, elementOffset + elementSize, elementOffset, elementSize);
4328 }
4329
4330 securityInfo += UString("\n------------------------------------------------------------------------\n\n");
4331 bgBootPolicyFound = true;
4332 return U_SUCCESS;
4333 }
4334 #endif
4335
parseMicrocodeVolumeBody(const UModelIndex & index)4336 USTATUS FfsParser::parseMicrocodeVolumeBody(const UModelIndex & index)
4337 {
4338 const UINT32 headerSize = (UINT32)model->header(index).size();
4339 const UINT32 bodySize = (UINT32)model->body(index).size();
4340 UINT32 offset = 0;
4341 USTATUS result = U_SUCCESS;
4342
4343 while(true) {
4344 // Parse current microcode
4345 UModelIndex currentMicrocode;
4346 UByteArray ucode = model->body(index).mid(offset);
4347
4348 // Check for empty area
4349 if (ucode.size() == ucode.count('\xFF') || ucode.size() == ucode.count('\x00')) {
4350 result = U_INVALID_MICROCODE;
4351 }
4352 else {
4353 result = parseIntelMicrocodeHeader(ucode, headerSize + offset, index, currentMicrocode);
4354 }
4355
4356 // Add the rest as padding
4357 if (result) {
4358 if (offset < bodySize) {
4359 // Get info
4360 UString name = UString("Padding");
4361 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", ucode.size(), ucode.size());
4362
4363 // Add tree item
4364 model->addItem(headerSize + offset, Types::Padding, getPaddingType(ucode), name, UString(), info, UByteArray(), ucode, UByteArray(), Fixed, index);
4365 }
4366 return U_SUCCESS;
4367 }
4368
4369 // Get to next candidate
4370 offset += model->header(currentMicrocode).size() + model->body(currentMicrocode).size() + model->tail(currentMicrocode).size();
4371 if (offset >= bodySize)
4372 break;
4373 }
4374 return U_SUCCESS;
4375 }
4376
parseIntelMicrocodeHeader(const UByteArray & microcode,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)4377 USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
4378 {
4379 // We have enough data to fit the header
4380 if ((UINT32)microcode.size() < sizeof(INTEL_MICROCODE_HEADER)) {
4381 return U_INVALID_MICROCODE;
4382 }
4383
4384 const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)microcode.constData();
4385
4386 if (!microcodeHeaderValid(ucodeHeader)) {
4387 return U_INVALID_MICROCODE;
4388 }
4389
4390 // We have enough data to fit the whole TotalSize
4391 if ((UINT32)microcode.size() < ucodeHeader->TotalSize) {
4392 return U_INVALID_MICROCODE;
4393 }
4394
4395 // Valid microcode found
4396 UINT32 dataSize = ucodeHeader->DataSize;
4397 if (dataSize == 0) {
4398 dataSize = INTEL_MICROCODE_REAL_DATA_SIZE_ON_ZERO;
4399 }
4400
4401 // Cross check DataSize and TotalSize
4402 if (ucodeHeader->TotalSize < sizeof(INTEL_MICROCODE_HEADER) + dataSize) {
4403 return U_INVALID_MICROCODE;
4404 }
4405
4406 // Recalculate the whole microcode checksum
4407 UByteArray tempMicrocode = microcode;
4408 INTEL_MICROCODE_HEADER* tempUcodeHeader = (INTEL_MICROCODE_HEADER*)(tempMicrocode.data());
4409 tempUcodeHeader->Checksum = 0;
4410 UINT32 calculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), tempUcodeHeader->TotalSize);
4411 bool msgInvalidChecksum = (ucodeHeader->Checksum != calculated);
4412
4413 // Construct header, body and tail
4414 UByteArray header = microcode.left(sizeof(INTEL_MICROCODE_HEADER));
4415 UByteArray body = microcode.mid(sizeof(INTEL_MICROCODE_HEADER), dataSize);
4416 UByteArray tail;
4417
4418 // Check if the tail is present
4419 if (ucodeHeader->TotalSize > sizeof(INTEL_MICROCODE_HEADER) + dataSize) {
4420 tail = microcode.mid(sizeof(INTEL_MICROCODE_HEADER) + dataSize, ucodeHeader->TotalSize - (sizeof(INTEL_MICROCODE_HEADER) + dataSize));
4421 }
4422
4423 // Check if we have extended header in the tail
4424 UString extendedHeaderInfo;
4425 bool msgUnknownOrDamagedMicrocodeTail = false;
4426 if ((UINT32)tail.size() >= sizeof(INTEL_MICROCODE_EXTENDED_HEADER)) {
4427 const INTEL_MICROCODE_EXTENDED_HEADER* extendedHeader = (const INTEL_MICROCODE_EXTENDED_HEADER*)tail.constData();
4428
4429 // Reserved bytes are all zeroes
4430 bool extendedReservedBytesValid = true;
4431 for (UINT8 i = 0; i < sizeof(extendedHeader->Reserved); i++) {
4432 if (extendedHeader->Reserved[i] != 0x00) {
4433 extendedReservedBytesValid = false;
4434 break;
4435 }
4436 }
4437
4438 // We have more than 0 entries and they are all in the tail
4439 if (extendedReservedBytesValid
4440 && extendedHeader->EntryCount > 0
4441 && (UINT32)tail.size() == sizeof(INTEL_MICROCODE_EXTENDED_HEADER) + extendedHeader->EntryCount * sizeof(INTEL_MICROCODE_EXTENDED_HEADER_ENTRY)) {
4442 // Recalculate extended header checksum
4443 INTEL_MICROCODE_EXTENDED_HEADER* tempExtendedHeader = (INTEL_MICROCODE_EXTENDED_HEADER*)(tempMicrocode.data() + sizeof(INTEL_MICROCODE_HEADER) + dataSize);
4444 tempExtendedHeader->Checksum = 0;
4445 UINT32 extendedCalculated = calculateChecksum32((const UINT32*)tempExtendedHeader, sizeof(INTEL_MICROCODE_EXTENDED_HEADER) + extendedHeader->EntryCount * sizeof(INTEL_MICROCODE_EXTENDED_HEADER_ENTRY));
4446
4447 extendedHeaderInfo = usprintf("\nExtended header entries: %u\nExtended header checksum: %08Xh, ",
4448 extendedHeader->EntryCount,
4449 extendedHeader->Checksum)
4450 + (extendedHeader->Checksum == extendedCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", extendedCalculated));
4451
4452 const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY* firstEntry = (const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY*)(extendedHeader + 1);
4453 for (UINT8 i = 0; i < extendedHeader->EntryCount; i++) {
4454 const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY* entry = (const INTEL_MICROCODE_EXTENDED_HEADER_ENTRY*)(firstEntry + i);
4455
4456 // Recalculate checksum after patching
4457 tempUcodeHeader->Checksum = 0;
4458 tempUcodeHeader->ProcessorFlags = entry->ProcessorFlags;
4459 tempUcodeHeader->ProcessorSignature = entry->ProcessorSignature;
4460 UINT32 entryCalculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), sizeof(INTEL_MICROCODE_HEADER) + dataSize);
4461
4462 extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU flags #%u: %02Xh\nChecksum #%u: %08Xh, ",
4463 i + 1, entry->ProcessorSignature,
4464 i + 1, entry->ProcessorFlags,
4465 i + 1, entry->Checksum)
4466 + (entry->Checksum == entryCalculated ? UString("valid") : usprintf("invalid, should be %08Xh", entryCalculated));
4467 }
4468 }
4469 else {
4470 msgUnknownOrDamagedMicrocodeTail = true;
4471 }
4472 }
4473 else if (tail.size() != 0) {
4474 msgUnknownOrDamagedMicrocodeTail = true;
4475 }
4476
4477 // Get microcode binary
4478 UByteArray microcodeBinary = microcode.left(ucodeHeader->TotalSize);
4479
4480 // Add info
4481 UString name("Intel microcode");
4482 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: 0h (0u)\nBody size: %" PRIXQ "h (%" PRIuQ ")\nTail size: 0h (0u)\n"
4483 "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nCPU flags: %02Xh\nChecksum: %08Xh, ",
4484 microcodeBinary.size(), microcodeBinary.size(),
4485 microcodeBinary.size(), microcodeBinary.size(),
4486 ucodeHeader->DateDay,
4487 ucodeHeader->DateMonth,
4488 ucodeHeader->DateYear,
4489 ucodeHeader->ProcessorSignature,
4490 ucodeHeader->UpdateRevision,
4491 ucodeHeader->ProcessorFlags,
4492 ucodeHeader->Checksum)
4493 + (ucodeHeader->Checksum == calculated ? UString("valid") : usprintf("invalid, should be %08Xh", calculated))
4494 + extendedHeaderInfo;
4495
4496 // Add tree item
4497 index = model->addItem(localOffset, Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, UByteArray(), microcodeBinary, UByteArray(), Fixed, parent);
4498 if (msgInvalidChecksum)
4499 msg(usprintf("%s: invalid microcode checksum %08Xh, should be %08Xh", __FUNCTION__, ucodeHeader->Checksum, calculated), index);
4500 if (msgUnknownOrDamagedMicrocodeTail)
4501 msg(usprintf("%s: extended header of size %" PRIXQ "h (%" PRIuQ ") found, but it's damaged or has unknown format", __FUNCTION__, tail.size(), tail.size()), index);
4502
4503 // No need to parse the body further for now
4504 return U_SUCCESS;
4505 }
4506
parseBpdtRegion(const UByteArray & region,const UINT32 localOffset,const UINT32 sbpdtOffsetFixup,const UModelIndex & parent,UModelIndex & index)4507 USTATUS FfsParser::parseBpdtRegion(const UByteArray & region, const UINT32 localOffset, const UINT32 sbpdtOffsetFixup, const UModelIndex & parent, UModelIndex & index)
4508 {
4509 UINT32 regionSize = (UINT32)region.size();
4510
4511 // Check region size
4512 if (regionSize < sizeof(BPDT_HEADER)) {
4513 msg(usprintf("%s: BPDT region too small to fit BPDT partition table header", __FUNCTION__), parent);
4514 return U_INVALID_ME_PARTITION_TABLE;
4515 }
4516
4517 // Populate partition table header
4518 const BPDT_HEADER* ptHeader = (const BPDT_HEADER*)(region.constData());
4519
4520 // Check region size again
4521 UINT32 ptBodySize = ptHeader->NumEntries * sizeof(BPDT_ENTRY);
4522 UINT32 ptSize = sizeof(BPDT_HEADER) + ptBodySize;
4523 if (regionSize < ptSize) {
4524 msg(usprintf("%s: BPDT region too small to fit BPDT partition table", __FUNCTION__), parent);
4525 return U_INVALID_ME_PARTITION_TABLE;
4526 }
4527
4528 // Get info
4529 UByteArray header = region.left(sizeof(BPDT_HEADER));
4530 UByteArray body = region.mid(sizeof(BPDT_HEADER), ptBodySize);
4531
4532 UString name = UString("BPDT partition table");
4533 UString info = usprintf("Full size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %Xh (%u)\nNumber of entries: %u\nVersion: %2Xh\n"
4534 "IFWI version: %Xh\nFITC version: %u.%u.%u.%u",
4535 ptSize, ptSize,
4536 header.size(), header.size(),
4537 ptBodySize, ptBodySize,
4538 ptHeader->NumEntries,
4539 ptHeader->HeaderVersion,
4540 ptHeader->IfwiVersion,
4541 ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild);
4542
4543 // Add tree item
4544 index = model->addItem(localOffset, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
4545
4546 // Adjust offset
4547 UINT32 offset = sizeof(BPDT_HEADER);
4548
4549 // Add partition table entries
4550 std::vector<BPDT_PARTITION_INFO> partitions;
4551 const BPDT_ENTRY* firstPtEntry = (const BPDT_ENTRY*)((const UINT8*)ptHeader + sizeof(BPDT_HEADER));
4552 for (UINT16 i = 0; i < ptHeader->NumEntries; i++) {
4553 // Populate entry header
4554 const BPDT_ENTRY* ptEntry = firstPtEntry + i;
4555
4556 // Get info
4557 name = bpdtEntryTypeToUString(ptEntry->Type);
4558 info = usprintf("Full size: %lXh (%lu)\nType: %Xh\nPartition offset: %Xh\nPartition length: %Xh",
4559 sizeof(BPDT_ENTRY), sizeof(BPDT_ENTRY),
4560 ptEntry->Type,
4561 ptEntry->Offset,
4562 ptEntry->Size) +
4563 UString("\nSplit sub-partition first part: ") + (ptEntry->SplitSubPartitionFirstPart ? "Yes" : "No") +
4564 UString("\nSplit sub-partition second part: ") + (ptEntry->SplitSubPartitionSecondPart ? "Yes" : "No") +
4565 UString("\nCode sub-partition: ") + (ptEntry->CodeSubPartition ? "Yes" : "No") +
4566 UString("\nUMA cachable: ") + (ptEntry->UmaCachable ? "Yes" : "No");
4567
4568 // Add tree item
4569 UModelIndex entryIndex = model->addItem(localOffset + offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(BPDT_ENTRY)), UByteArray(), Fixed, index);
4570
4571 // Adjust offset
4572 offset += sizeof(BPDT_ENTRY);
4573
4574 if (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0) {
4575 // Add to partitions vector
4576 BPDT_PARTITION_INFO partition;
4577 partition.type = Types::BpdtPartition;
4578 partition.ptEntry = *ptEntry;
4579 partition.ptEntry.Offset -= sbpdtOffsetFixup;
4580 partition.index = entryIndex;
4581 partitions.push_back(partition);
4582 }
4583 }
4584
4585 // Add padding if there's no partions to add
4586 if (partitions.size() == 0) {
4587 UByteArray partition = region.mid(ptSize);
4588
4589 // Get info
4590 name = UString("Padding");
4591 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")",
4592 partition.size(), partition.size());
4593
4594 // Add tree item
4595 model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
4596 return U_SUCCESS;
4597 }
4598
4599 make_partition_table_consistent:
4600 // Sort partitions by offset
4601 std::sort(partitions.begin(), partitions.end());
4602
4603 // Check for intersections and paddings between partitions
4604 BPDT_PARTITION_INFO padding;
4605
4606 // Check intersection with the partition table header
4607 if (partitions.front().ptEntry.Offset < ptSize) {
4608 msg(usprintf("%s: BPDT partition has intersection with BPDT partition table, skipped", __FUNCTION__),
4609 partitions.front().index);
4610 partitions.erase(partitions.begin());
4611 goto make_partition_table_consistent;
4612 }
4613 // Check for padding between partition table and the first partition
4614 else if (partitions.front().ptEntry.Offset > ptSize) {
4615 padding.ptEntry.Offset = ptSize;
4616 padding.ptEntry.Size = partitions.front().ptEntry.Offset - padding.ptEntry.Offset;
4617 padding.type = Types::Padding;
4618 partitions.insert(partitions.begin(), padding);
4619 }
4620 // Check for intersections/paddings between partitions
4621 for (size_t i = 1; i < partitions.size(); i++) {
4622 UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size;
4623
4624 // Check that partition is fully present in the image
4625 if ((UINT64)partitions[i].ptEntry.Offset + (UINT64)partitions[i].ptEntry.Size > regionSize) {
4626 if ((UINT64)partitions[i].ptEntry.Offset >= (UINT64)region.size()) {
4627 msg(usprintf("%s: BPDT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
4628 partitions.erase(partitions.begin() + i);
4629 goto make_partition_table_consistent;
4630 }
4631 else {
4632 msg(usprintf("%s: BPDT partition can't fit into its region, truncated", __FUNCTION__), partitions[i].index);
4633 partitions[i].ptEntry.Size = regionSize - (UINT32)partitions[i].ptEntry.Offset;
4634 }
4635 }
4636
4637 // Check for intersection with previous partition
4638 if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
4639 // Check if current partition is located inside previous one
4640 if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) {
4641 msg(usprintf("%s: BPDT partition is located inside another BPDT partition, skipped", __FUNCTION__),
4642 partitions[i].index);
4643 partitions.erase(partitions.begin() + i);
4644 goto make_partition_table_consistent;
4645 }
4646 else {
4647 msg(usprintf("%s: BPDT partition intersects with prevous one, skipped", __FUNCTION__),
4648 partitions[i].index);
4649 partitions.erase(partitions.begin() + i);
4650 goto make_partition_table_consistent;
4651 }
4652 }
4653
4654 // Check for padding between current and previous partitions
4655 else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
4656 padding.ptEntry.Offset = previousPartitionEnd;
4657 padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd;
4658 padding.type = Types::Padding;
4659 std::vector<BPDT_PARTITION_INFO>::iterator iter = partitions.begin();
4660 std::advance(iter, i);
4661 partitions.insert(iter, padding);
4662 }
4663 }
4664
4665 // Partition map is consistent
4666 for (size_t i = 0; i < partitions.size(); i++) {
4667 if (partitions[i].type == Types::BpdtPartition) {
4668 // Get info
4669 UString name = bpdtEntryTypeToUString(partitions[i].ptEntry.Type);
4670 UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size);
4671 UByteArray signature = partition.left(sizeof(UINT32));
4672
4673 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh",
4674 partition.size(), partition.size(),
4675 partitions[i].ptEntry.Type) +
4676 UString("\nSplit sub-partition first part: ") + (partitions[i].ptEntry.SplitSubPartitionFirstPart ? "Yes" : "No") +
4677 UString("\nSplit sub-partition second part: ") + (partitions[i].ptEntry.SplitSubPartitionSecondPart ? "Yes" : "No") +
4678 UString("\nCode sub-partition: ") + (partitions[i].ptEntry.CodeSubPartition ? "Yes" : "No") +
4679 UString("\nUMA cachable: ") + (partitions[i].ptEntry.UmaCachable ? "Yes" : "No");
4680
4681 UString text = bpdtEntryTypeToUString(partitions[i].ptEntry.Type);
4682
4683 // Add tree item
4684 UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent);
4685
4686 // Special case of S-BPDT
4687 if (partitions[i].ptEntry.Type == BPDT_ENTRY_TYPE_SBPDT) {
4688 UModelIndex sbpdtIndex;
4689 parseBpdtRegion(partition, 0, partitions[i].ptEntry.Offset, partitionIndex, sbpdtIndex); // Third parameter is a fixup for S-BPDT offset entries, because they are calculated from the start of BIOS region
4690 }
4691
4692 // Parse code partitions
4693 if (readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) {
4694 // Parse code partition contents
4695 UModelIndex cpdIndex;
4696 parseCpdRegion(partition, 0, partitionIndex, cpdIndex);
4697 }
4698
4699 // TODO: make this generic again
4700 if (partitions[i].ptEntry.Type > BPDT_ENTRY_TYPE_TBT
4701 && partitions[i].ptEntry.Type != BPDT_ENTRY_TYPE_SAMF
4702 && partitions[i].ptEntry.Type != BPDT_ENTRY_TYPE_PPHY) {
4703 msg(usprintf("%s: BPDT entry of unknown type found", __FUNCTION__), partitionIndex);
4704 }
4705 }
4706 else if (partitions[i].type == Types::Padding) {
4707 UByteArray padding = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size);
4708
4709 // Get info
4710 name = UString("Padding");
4711 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")",
4712 padding.size(), padding.size());
4713
4714 // Add tree item
4715 model->addItem(localOffset + partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
4716 }
4717 }
4718
4719 // Add padding after the last region
4720 if ((UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Size < regionSize) {
4721 UINT64 usedSize = (UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Size;
4722 UByteArray padding = region.mid(partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, (int)(regionSize - usedSize));
4723
4724 // Get info
4725 name = UString("Padding");
4726 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")",
4727 padding.size(), padding.size());
4728
4729 // Add tree item
4730 model->addItem(localOffset + partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
4731 }
4732
4733 return U_SUCCESS;
4734 }
4735
parseCpdRegion(const UByteArray & region,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)4736 USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
4737 {
4738 // Check directory size
4739 if ((UINT32)region.size() < sizeof(CPD_REV1_HEADER)) {
4740 msg(usprintf("%s: CPD too small to fit rev1 partition table header", __FUNCTION__), parent);
4741 return U_INVALID_ME_PARTITION_TABLE;
4742 }
4743
4744 // Populate partition table header
4745 const CPD_REV1_HEADER* cpdHeader = (const CPD_REV1_HEADER*)region.constData();
4746
4747 // Check header version to be known
4748 UINT32 ptHeaderSize = 0;
4749 if (cpdHeader->HeaderVersion == 2) {
4750 if ((UINT32)region.size() < sizeof(CPD_REV2_HEADER)) {
4751 msg(usprintf("%s: CPD too small to fit rev2 partition table header", __FUNCTION__), parent);
4752 return U_INVALID_ME_PARTITION_TABLE;
4753 }
4754
4755 ptHeaderSize = sizeof(CPD_REV2_HEADER);
4756 }
4757 else if (cpdHeader->HeaderVersion == 1) {
4758 ptHeaderSize = sizeof(CPD_REV1_HEADER);
4759 }
4760
4761 // Check directory size again
4762 UINT32 ptBodySize = cpdHeader->NumEntries * sizeof(CPD_ENTRY);
4763 UINT32 ptSize = ptHeaderSize + ptBodySize;
4764 if ((UINT32)region.size() < ptSize) {
4765 msg(usprintf("%s: CPD too small to fit the whole partition table", __FUNCTION__), parent);
4766 return U_INVALID_ME_PARTITION_TABLE;
4767 }
4768
4769 // Get info
4770 UByteArray header = region.left(ptHeaderSize);
4771 UByteArray body = region.mid(ptHeaderSize, ptBodySize);
4772 UString name = usprintf("CPD partition table");
4773 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nNumber of entries: %u\n"
4774 "Header version: %u\nEntry version: %u",
4775 ptSize, ptSize,
4776 header.size(), header.size(),
4777 body.size(), body.size(),
4778 cpdHeader->NumEntries,
4779 cpdHeader->HeaderVersion,
4780 cpdHeader->EntryVersion);
4781
4782 // Add tree item
4783 index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
4784
4785 // Add partition table entries
4786 std::vector<CPD_PARTITION_INFO> partitions;
4787 UINT32 offset = ptHeaderSize;
4788 const CPD_ENTRY* firstCpdEntry = (const CPD_ENTRY*)(body.constData());
4789 for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) {
4790 // Populate entry header
4791 const CPD_ENTRY* cpdEntry = firstCpdEntry + i;
4792 UByteArray entry((const char*)cpdEntry, sizeof(CPD_ENTRY));
4793
4794 // Get info
4795 name = usprintf("%.12s", cpdEntry->EntryName);
4796 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ",
4797 entry.size(), entry.size(),
4798 cpdEntry->Offset.Offset,
4799 cpdEntry->Length)
4800 + (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No");
4801
4802 // Add tree item
4803 UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index);
4804
4805 // Adjust offset
4806 offset += sizeof(CPD_ENTRY);
4807
4808 if (cpdEntry->Offset.Offset != 0 && cpdEntry->Length != 0) {
4809 // Add to partitions vector
4810 CPD_PARTITION_INFO partition;
4811 partition.type = Types::CpdPartition;
4812 partition.ptEntry = *cpdEntry;
4813 partition.index = entryIndex;
4814 partition.hasMetaData = false;
4815 partitions.push_back(partition);
4816 }
4817 }
4818
4819 // Add padding if there's no partions to add
4820 if (partitions.size() == 0) {
4821 UByteArray partition = region.mid(ptSize);
4822
4823 // Get info
4824 name = UString("Padding");
4825 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")",
4826 partition.size(), partition.size());
4827
4828 // Add tree item
4829 model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
4830
4831 return U_SUCCESS;
4832 }
4833
4834 // Sort partitions by offset
4835 std::sort(partitions.begin(), partitions.end());
4836
4837 // Because lenghts for all Huffmann-compressed partitions mean nothing at all, we need to split all partitions into 2 classes:
4838 // 1. CPD manifest
4839 // 2. Metadata entries
4840 UINT32 i = 1; // manifest is index 0, .met partitions start at index 1
4841 while (i < partitions.size()) {
4842 name = usprintf("%.12s", partitions[i].ptEntry.EntryName);
4843
4844 // Check if the current entry is metadata entry
4845 if (!name.endsWith(".met")) {
4846 // No need to parse further, all metadata partitions are parsed
4847 break;
4848 }
4849
4850 // Parse into data block, find Module Attributes extension, and get compressed size from there
4851 UINT32 offset = 0;
4852 UINT32 length = 0xFFFFFFFF; // Special guardian value
4853 UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
4854 while (offset < (UINT32)partition.size()) {
4855 const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (partition.constData() + offset);
4856 if (extHeader->Length <= ((UINT32)partition.size() - offset)) {
4857 if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) {
4858 const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset);
4859 length = attrHeader->CompressedSize;
4860 }
4861 offset += extHeader->Length;
4862 }
4863 else break;
4864 }
4865
4866 // Search down for corresponding code partition
4867 // Construct its name by removing the .met suffix
4868 name.chop(4);
4869
4870 // Search
4871 bool found = false;
4872 UINT32 j = 1;
4873 while (j < partitions.size()) {
4874 UString namej = usprintf("%.12s", partitions[j].ptEntry.EntryName);
4875
4876 if (name == namej) {
4877 found = true;
4878 // Found it, update its Length if needed
4879 if (partitions[j].ptEntry.Offset.HuffmanCompressed) {
4880 partitions[j].ptEntry.Length = length;
4881 }
4882 else if (length != 0xFFFFFFFF && partitions[j].ptEntry.Length != length) {
4883 msg(usprintf("%s: partition size mismatch between partition table (%Xh) and partition metadata (%Xh)", __FUNCTION__,
4884 partitions[j].ptEntry.Length, length), partitions[j].index);
4885 partitions[j].ptEntry.Length = length; // Believe metadata
4886 }
4887 partitions[j].hasMetaData = true;
4888 // No need to search further
4889 break;
4890 }
4891 // Check the next partition
4892 j++;
4893 }
4894 if (!found) {
4895 msg(usprintf("%s: no code partition", __FUNCTION__), partitions[i].index);
4896 }
4897
4898 // Check the next partition
4899 i++;
4900 }
4901
4902 make_partition_table_consistent:
4903 // Sort partitions by offset
4904 std::sort(partitions.begin(), partitions.end());
4905
4906 // Check for intersections and paddings between partitions
4907 CPD_PARTITION_INFO padding;
4908
4909 // Check intersection with the partition table header
4910 if (partitions.front().ptEntry.Offset.Offset < ptSize) {
4911 msg(usprintf("%s: CPD partition has intersection with CPD partition table, skipped", __FUNCTION__),
4912 partitions.front().index);
4913 partitions.erase(partitions.begin());
4914 goto make_partition_table_consistent;
4915 }
4916 // Check for padding between partition table and the first partition
4917 else if (partitions.front().ptEntry.Offset.Offset > ptSize) {
4918 padding.ptEntry.Offset.Offset = ptSize;
4919 padding.ptEntry.Length = partitions.front().ptEntry.Offset.Offset - padding.ptEntry.Offset.Offset;
4920 padding.type = Types::Padding;
4921 partitions.insert(partitions.begin(), padding);
4922 }
4923 // Check for intersections/paddings between partitions
4924 for (size_t i = 1; i < partitions.size(); i++) {
4925 UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset.Offset + partitions[i - 1].ptEntry.Length;
4926
4927 // Check that current region is fully present in the image
4928 if ((UINT64)partitions[i].ptEntry.Offset.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)region.size()) {
4929 if ((UINT64)partitions[i].ptEntry.Offset.Offset >= (UINT64)region.size()) {
4930 msg(usprintf("%s: CPD partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
4931 partitions.erase(partitions.begin() + i);
4932 goto make_partition_table_consistent;
4933 }
4934 else {
4935 if (!partitions[i].hasMetaData && partitions[i].ptEntry.Offset.HuffmanCompressed) {
4936 msg(usprintf("%s: CPD partition is compressed but doesn't have metadata and can't fit into its region, length adjusted", __FUNCTION__),
4937 partitions[i].index);
4938 }
4939 else {
4940 msg(usprintf("%s: CPD partition can't fit into its region, truncated", __FUNCTION__), partitions[i].index);
4941 }
4942 partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset.Offset;
4943 }
4944 }
4945
4946 // Check for intersection with previous partition
4947 if (partitions[i].ptEntry.Offset.Offset < previousPartitionEnd) {
4948 // Check if previous partition was compressed but did not have metadata
4949 if (!partitions[i - 1].hasMetaData && partitions[i - 1].ptEntry.Offset.HuffmanCompressed) {
4950 msg(usprintf("%s: CPD partition is compressed but doesn't have metadata, length adjusted", __FUNCTION__),
4951 partitions[i - 1].index);
4952 partitions[i - 1].ptEntry.Length = (UINT32)partitions[i].ptEntry.Offset.Offset - (UINT32)partitions[i - 1].ptEntry.Offset.Offset;
4953 goto make_partition_table_consistent;
4954 }
4955
4956 // Check if current partition is located inside previous one
4957 if (partitions[i].ptEntry.Offset.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) {
4958 msg(usprintf("%s: CPD partition is located inside another CPD partition, skipped", __FUNCTION__),
4959 partitions[i].index);
4960 partitions.erase(partitions.begin() + i);
4961 goto make_partition_table_consistent;
4962 }
4963 else {
4964 msg(usprintf("%s: CPD partition intersects with previous one, skipped", __FUNCTION__),
4965 partitions[i].index);
4966 partitions.erase(partitions.begin() + i);
4967 goto make_partition_table_consistent;
4968 }
4969 }
4970 // Check for padding between current and previous partitions
4971 else if (partitions[i].ptEntry.Offset.Offset > previousPartitionEnd) {
4972 padding.ptEntry.Offset.Offset = previousPartitionEnd;
4973 padding.ptEntry.Length = partitions[i].ptEntry.Offset.Offset - previousPartitionEnd;
4974 padding.type = Types::Padding;
4975 std::vector<CPD_PARTITION_INFO>::iterator iter = partitions.begin();
4976 std::advance(iter, i);
4977 partitions.insert(iter, padding);
4978 }
4979 }
4980 // Check for padding after the last region
4981 if ((UINT64)partitions.back().ptEntry.Offset.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)region.size()) {
4982 padding.ptEntry.Offset.Offset = partitions.back().ptEntry.Offset.Offset + partitions.back().ptEntry.Length;
4983 padding.ptEntry.Length = (UINT32)region.size() - padding.ptEntry.Offset.Offset;
4984 padding.type = Types::Padding;
4985 partitions.push_back(padding);
4986 }
4987
4988 // Partition map is consistent
4989 for (size_t i = 0; i < partitions.size(); i++) {
4990 if (partitions[i].type == Types::CpdPartition) {
4991 UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
4992
4993 // Get info
4994 name = usprintf("%.12s", partitions[i].ptEntry.EntryName);
4995
4996 // It's a manifest
4997 if (name.endsWith(".man")) {
4998 if (!partitions[i].ptEntry.Offset.HuffmanCompressed
4999 && partitions[i].ptEntry.Length >= sizeof(CPD_MANIFEST_HEADER)) {
5000 const CPD_MANIFEST_HEADER* manifestHeader = (const CPD_MANIFEST_HEADER*) partition.constData();
5001 if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) {
5002 UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32));
5003 UByteArray body = partition.mid(manifestHeader->HeaderLength * sizeof(UINT32));
5004
5005 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")"
5006 "\nHeader type: %u\nHeader length: %lXh (%lu)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n"
5007 "Date: %Xh\nSize: %lXh (%lu)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %lXh (%lu)\nExponent size: %lXh (%lu)",
5008 partition.size(), partition.size(),
5009 header.size(), header.size(),
5010 body.size(), body.size(),
5011 manifestHeader->HeaderType,
5012 manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32),
5013 manifestHeader->HeaderVersion,
5014 manifestHeader->Flags,
5015 manifestHeader->Vendor,
5016 manifestHeader->Date,
5017 manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32),
5018 manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild,
5019 manifestHeader->SecurityVersion,
5020 manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32),
5021 manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32));
5022
5023 // Add tree item
5024 UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent);
5025
5026 // Parse data as extensions area
5027 parseCpdExtensionsArea(partitionIndex);
5028 }
5029 }
5030 }
5031 // It's a metadata
5032 else if (name.endsWith(".met")) {
5033 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHuffman compressed: ",
5034 partition.size(), partition.size())
5035 + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No");
5036
5037 // Calculate SHA256 hash over the metadata and add it to its info
5038 UByteArray hash(SHA256_DIGEST_SIZE, '\x00');
5039 sha256(partition.constData(), partition.size(), hash.data());
5040 info += UString("\nMetadata hash: ") + UString(hash.toHex().constData());
5041
5042 // Add three item
5043 UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
5044
5045 // Parse data as extensions area
5046 parseCpdExtensionsArea(partitionIndex);
5047 }
5048 // It's a code
5049 else {
5050 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHuffman compressed: ",
5051 partition.size(), partition.size())
5052 + (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No");
5053
5054 // Calculate SHA256 hash over the code and add it to its info
5055 UByteArray hash(SHA256_DIGEST_SIZE, '\x00');
5056 sha256(partition.constData(), partition.size(), hash.data());
5057 info += UString("\nHash: ") + UString(hash.toHex().constData());
5058
5059 UModelIndex codeIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
5060 parseRawArea(codeIndex);
5061 }
5062 }
5063 else if (partitions[i].type == Types::Padding) {
5064 UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
5065
5066 // Get info
5067 name = UString("Padding");
5068 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", partition.size(), partition.size());
5069
5070 // Add tree item
5071 model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
5072 }
5073 else {
5074 msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent);
5075 return U_INVALID_ME_PARTITION_TABLE;
5076 }
5077 }
5078
5079 return U_SUCCESS;
5080 }
5081
parseCpdExtensionsArea(const UModelIndex & index)5082 USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index)
5083 {
5084 if (!index.isValid()) {
5085 return U_INVALID_PARAMETER;
5086 }
5087
5088 UByteArray body = model->body(index);
5089 UINT32 offset = 0;
5090 while (offset < (UINT32)body.size()) {
5091 const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (body.constData() + offset);
5092 if (extHeader->Length > 0 && extHeader->Length <= ((UINT32)body.size() - offset)) {
5093 UByteArray partition = body.mid(offset, extHeader->Length);
5094
5095 UString name = cpdExtensionTypeToUstring(extHeader->Type);
5096 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh", partition.size(), partition.size(), extHeader->Type);
5097
5098 // Parse Signed Package Info a bit further
5099 UModelIndex extIndex;
5100 if (extHeader->Type == CPD_EXT_TYPE_SIGNED_PACKAGE_INFO) {
5101 UByteArray header = partition.left(sizeof(CPD_EXT_SIGNED_PACKAGE_INFO));
5102 UByteArray data = partition.mid(header.size());
5103
5104 const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData();
5105
5106 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh\n"
5107 "Package name: %.4s\nVersion control number: %Xh\nSecurity version number: %Xh\n"
5108 "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
5109 partition.size(), partition.size(),
5110 header.size(), header.size(),
5111 body.size(), body.size(),
5112 infoHeader->ExtensionType,
5113 infoHeader->PackageName,
5114 infoHeader->Vcn,
5115 infoHeader->Svn,
5116 infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3],
5117 infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7],
5118 infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11],
5119 infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]);
5120
5121 // Add tree item
5122 extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index);
5123 parseSignedPackageInfoData(extIndex);
5124 }
5125 // Parse IFWI Partition Manifest a bit further
5126 else if (extHeader->Type == CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST) {
5127 const CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData();
5128
5129 // Check HashSize to be sane.
5130 UINT32 hashSize = attrHeader->HashSize;
5131 bool msgHashSizeMismatch = false;
5132 if (hashSize > sizeof(attrHeader->CompletePartitionHash)) {
5133 hashSize = sizeof(attrHeader->CompletePartitionHash);
5134 msgHashSizeMismatch = true;
5135 }
5136
5137 // This hash is stored reversed
5138 // Need to reverse it back to normal
5139 UByteArray hash((const char*)&attrHeader->CompletePartitionHash, hashSize);
5140 std::reverse(hash.begin(), hash.end());
5141
5142 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh\n"
5143 "Partition name: %.4s\nPartition length: %Xh\nPartition version major: %Xh\nPartition version minor: %Xh\n"
5144 "Data format version: %Xh\nInstance ID: %Xh\nHash algorithm: %Xh\nHash size: %Xh\nAction on update: %Xh",
5145 partition.size(), partition.size(),
5146 attrHeader->ExtensionType,
5147 attrHeader->PartitionName,
5148 attrHeader->CompletePartitionLength,
5149 attrHeader->PartitionVersionMajor, attrHeader->PartitionVersionMinor,
5150 attrHeader->DataFormatVersion,
5151 attrHeader->InstanceId,
5152 attrHeader->HashAlgorithm,
5153 attrHeader->HashSize,
5154 attrHeader->ActionOnUpdate)
5155 + UString("\nSupport multiple instances: ") + (attrHeader->SupportMultipleInstances ? "Yes" : "No")
5156 + UString("\nSupport API version based update: ") + (attrHeader->SupportApiVersionBasedUpdate ? "Yes" : "No")
5157 + UString("\nObey full update rules: ") + (attrHeader->ObeyFullUpdateRules ? "Yes" : "No")
5158 + UString("\nIFR enable only: ") + (attrHeader->IfrEnableOnly ? "Yes" : "No")
5159 + UString("\nAllow cross point update: ") + (attrHeader->AllowCrossPointUpdate ? "Yes" : "No")
5160 + UString("\nAllow cross hotfix update: ") + (attrHeader->AllowCrossHotfixUpdate ? "Yes" : "No")
5161 + UString("\nPartial update only: ") + (attrHeader->PartialUpdateOnly ? "Yes" : "No")
5162 + UString("\nPartition hash: ") + UString(hash.toHex().constData());
5163
5164 // Add tree item
5165 extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
5166 if (msgHashSizeMismatch) {
5167 msg(usprintf("%s: IFWI Partition Manifest hash size is %u, maximum allowed is %lu, truncated", __FUNCTION__, attrHeader->HashSize, sizeof(attrHeader->CompletePartitionHash)), extIndex);
5168 }
5169 }
5170 // Parse Module Attributes a bit further
5171 else if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) {
5172 const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)partition.constData();
5173 int hashSize = partition.size() - CpdExtModuleImageHashOffset;
5174
5175 // This hash is stored reversed
5176 // Need to reverse it back to normal
5177 UByteArray hash((const char*)attrHeader + CpdExtModuleImageHashOffset, hashSize);
5178 std::reverse(hash.begin(), hash.end());
5179
5180 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh\n"
5181 "Compression type: %Xh\nUncompressed size: %Xh (%u)\nCompressed size: %Xh (%u)\nGlobal module ID: %Xh\nImage hash: ",
5182 partition.size(), partition.size(),
5183 attrHeader->ExtensionType,
5184 attrHeader->CompressionType,
5185 attrHeader->UncompressedSize, attrHeader->UncompressedSize,
5186 attrHeader->CompressedSize, attrHeader->CompressedSize,
5187 attrHeader->GlobalModuleId) + UString(hash.toHex().constData());
5188
5189 // Add tree item
5190 extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
5191 }
5192 // Parse everything else
5193 else {
5194 // Add tree item, if needed
5195 extIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
5196 }
5197
5198 // TODO: make this generic again
5199 if (extHeader->Type > CPD_EXT_TYPE_TBT_METADATA
5200 && extHeader->Type != CPD_EXT_TYPE_GMF_CERTIFICATE
5201 && extHeader->Type != CPD_EXT_TYPE_GMF_BODY
5202 && extHeader->Type != CPD_EXT_TYPE_KEY_MANIFEST_EXT
5203 && extHeader->Type != CPD_EXT_TYPE_SIGNED_PACKAGE_INFO_EXT
5204 && extHeader->Type != CPD_EXT_TYPE_SPS_PLATFORM_ID) {
5205 msg(usprintf("%s: CPD extention of unknown type found", __FUNCTION__), extIndex);
5206 }
5207
5208 offset += extHeader->Length;
5209 }
5210 else break;
5211 // TODO: add padding at the end
5212 }
5213
5214 return U_SUCCESS;
5215 }
5216
parseSignedPackageInfoData(const UModelIndex & index)5217 USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index)
5218 {
5219 if (!index.isValid()) {
5220 return U_INVALID_PARAMETER;
5221 }
5222
5223 UByteArray body = model->body(index);
5224 UINT32 offset = 0;
5225 while (offset < (UINT32)body.size()) {
5226 const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE* moduleHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE*)(body.constData() + offset);
5227 if (sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) <= ((UINT32)body.size() - offset)) {
5228 // TODO: check sanity of moduleHeader->HashSize
5229 UByteArray module((const char*)moduleHeader, CpdExtSignedPkgMetadataHashOffset + moduleHeader->HashSize);
5230 UString name = usprintf("%.12s", moduleHeader->Name);
5231
5232 // This hash is stored reversed
5233 // Need to reverse it back to normal
5234 UByteArray hash((const char*)moduleHeader + CpdExtSignedPkgMetadataHashOffset, moduleHeader->HashSize);
5235 std::reverse(hash.begin(), hash.end());
5236
5237 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ",
5238 module.size(), module.size(),
5239 moduleHeader->Type,
5240 moduleHeader->HashAlgorithm,
5241 moduleHeader->HashSize, moduleHeader->HashSize,
5242 moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData());
5243 // Add tree otem
5244 UModelIndex extIndex = model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index);
5245 offset += module.size();
5246 }
5247 else break;
5248 // TODO: add padding at the end
5249 }
5250
5251 return U_SUCCESS;
5252 }
5253
outputInfo(void)5254 void FfsParser::outputInfo(void) {
5255 // Show ffsParser's messages
5256 std::vector<std::pair<UString, UModelIndex> > messages = getMessages();
5257 for (size_t i = 0; i < messages.size(); i++) {
5258 std::cout << (const char *)messages[i].first.toLocal8Bit() << std::endl;
5259 }
5260
5261 // Get last VTF
5262 std::vector<std::pair<std::vector<UString>, UModelIndex > > fitTable = getFitTable();
5263 if (fitTable.size()) {
5264 std::cout << "---------------------------------------------------------------------------" << std::endl;
5265 std::cout << " Address | Size | Ver | CS | Type / Info " << std::endl;
5266 std::cout << "---------------------------------------------------------------------------" << std::endl;
5267 for (size_t i = 0; i < fitTable.size(); i++) {
5268 std::cout
5269 << (const char *)fitTable[i].first[0].toLocal8Bit() << " | "
5270 << (const char *)fitTable[i].first[1].toLocal8Bit() << " | "
5271 << (const char *)fitTable[i].first[2].toLocal8Bit() << " | "
5272 << (const char *)fitTable[i].first[3].toLocal8Bit() << " | "
5273 << (const char *)fitTable[i].first[4].toLocal8Bit() << " | "
5274 << (const char *)fitTable[i].first[5].toLocal8Bit() << std::endl;
5275 }
5276 }
5277
5278 // Get security info
5279 UString secInfo = getSecurityInfo();
5280 if (!secInfo.isEmpty()) {
5281 std::cout << "------------------------------------------------------------------------" << std::endl;
5282 std::cout << "Security Info" << std::endl;
5283 std::cout << "------------------------------------------------------------------------" << std::endl;
5284 std::cout << (const char *)secInfo.toLocal8Bit() << std::endl;
5285 }
5286 }
5287