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