UEFITool/ffsengine.cpp
Nikolaj Schlej 3ffbc01a3f Version 0.3.0
Refactor code to separate GUI from FFS ttraversal
2013-10-15 17:19:15 +02:00

1164 lines
49 KiB
C++

/* ffsengine.cpp
Copyright (c) 2013, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include <math.h>
#include "ffsengine.h"
#include "treeitem.h"
#include "treemodel.h"
#include "treeitemtypes.h"
#include "descriptor.h"
#include "ffs.h"
#include "gbe.h"
#include "me.h"
#include "Tiano/EfiTianoCompress.h"
#include "Tiano/EfiTianoDecompress.h"
#include "LZMA/LzmaCompress.h"
#include "LZMA/LzmaDecompress.h"
FfsEngine::FfsEngine(QObject *parent)
: QObject(parent)
{
rootItem = new TreeItem(RootItem, 0, 0, tr("Object"), tr("Type"), tr("Subtype"), tr("Text"));
treeModel = new TreeModel(rootItem);
}
FfsEngine::~FfsEngine(void)
{
delete treeModel;
delete rootItem;
}
TreeModel* FfsEngine::model() const
{
return treeModel;
}
QString FfsEngine::message() const
{
return text;
}
void FfsEngine::msg(const QString & message)
{
text.append(message).append("\n");
}
UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
{
UINT32 capsuleHeaderSize = 0;
FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL;
QByteArray flashImage;
QByteArray bios;
QModelIndex index;
// Check buffer size to be more or equal then sizeof(EFI_CAPSULE_HEADER)
if (buffer.size() <= sizeof(EFI_CAPSULE_HEADER))
{
msg(tr("parseInputFile: Input file is smaller then mininum size of %1 bytes").arg(sizeof(EFI_CAPSULE_HEADER)));
return ERR_INVALID_PARAMETER;
}
// Check buffer for being normal EFI capsule header
if (buffer.startsWith(EFI_CAPSULE_GUID)) {
EFI_CAPSULE_HEADER* capsuleHeader = (EFI_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = capsuleHeader->HeaderSize;
QByteArray header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize);
index = addTreeItem(CapsuleItem, UefiCapsule, 0, header, body);
}
// Check buffer for being extended Aptio capsule header
else if (buffer.startsWith(APTIO_CAPSULE_GUID)) {
APTIO_CAPSULE_HEADER* aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = aptioCapsuleHeader->RomImageOffset;
QByteArray header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize);
index = addTreeItem(CapsuleItem, AptioCapsule, 0, header, body);
}
// Skip capsule header to have flash chip image
flashImage = buffer.right(buffer.size() - capsuleHeaderSize);
// Check buffer for being Intel flash descriptor
descriptorHeader = (FLASH_DESCRIPTOR_HEADER*) flashImage.constData();
// Check descriptor signature
if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) {
FLASH_DESCRIPTOR_MAP* descriptorMap;
FLASH_DESCRIPTOR_REGION_SECTION* regionSection;
// Store the beginning of descriptor as descriptor base address
UINT8* descriptor = (UINT8*) flashImage.constData();
UINT8* gbeRegion = NULL;
UINT8* meRegion = NULL;
UINT8* biosRegion = NULL;
UINT8* pdrRegion = NULL;
// Check for buffer size to be greater or equal to descriptor region size
if (flashImage.size() < FLASH_DESCRIPTOR_SIZE) {
msg(tr("parseInputFile: Input file is smaller then mininum descriptor size of 4096 bytes"));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// Parse descriptor map
descriptorMap = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER));
regionSection = (FLASH_DESCRIPTOR_REGION_SECTION*) calculateAddress8(descriptor, descriptorMap->RegionBase);
// Add tree item
QByteArray header = flashImage.left(sizeof(FLASH_DESCRIPTOR_HEADER));
QByteArray body = flashImage.mid(sizeof(FLASH_DESCRIPTOR_HEADER), FLASH_DESCRIPTOR_SIZE - sizeof(FLASH_DESCRIPTOR_HEADER));
index = addTreeItem(DescriptorItem, 0, 0, header, body, index);
// Parse region section
QModelIndex gbeIndex(index);
QModelIndex meIndex(index);
QModelIndex biosIndex(index);
QModelIndex pdrIndex(index);
gbeRegion = parseRegion(flashImage, GbeRegion, regionSection->GbeBase, regionSection->GbeLimit, gbeIndex);
meRegion = parseRegion(flashImage, MeRegion, regionSection->MeBase, regionSection->MeLimit, meIndex);
biosRegion = parseRegion(flashImage, BiosRegion, regionSection->BiosBase, regionSection->BiosLimit, biosIndex);
pdrRegion = parseRegion(flashImage, PdrRegion, regionSection->PdrBase, regionSection->PdrLimit, pdrIndex);
// Parse complete
// Exit if no bios region found
if (!biosRegion) {
msg(tr("parseInputFile: BIOS region not found"));
return ERR_BIOS_REGION_NOT_FOUND;
}
index = biosIndex;
bios = QByteArray::fromRawData((const char*) biosRegion, calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit));
}
else {
bios = buffer;
}
// We are in the beginning of BIOS space, where firmware volumes are
// Parse BIOS space
return parseBios(bios, index);
}
UINT8* FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index)
{
// Check for empty region or flash image
if (!regionLimit || flashImage.size() <= 0)
return NULL;
// Storing flash image size to unsigned variable, because it can't be negative now and all other values are unsigned
UINT32 flashImageSize = (UINT32) flashImage.size();
// Calculate region offset and size
UINT32 regionOffset = calculateRegionOffset(regionBase);
UINT32 regionSize = calculateRegionSize(regionBase, regionLimit);
// Populate descriptor map
FLASH_DESCRIPTOR_MAP* descriptor_map = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER));
// Determine presence of 2 flash chips
bool twoChips = descriptor_map->NumberOfFlashChips;
// construct region name
//!TODO: make this to regionTypeToQString(const UINT8 type) in descriptor.cpp
QString regionName;
switch (regionSubtype)
{
case GbeRegion:
regionName = "GbE";
break;
case MeRegion:
regionName = "ME";
break;
case BiosRegion:
regionName = "Bios";
break;
case PdrRegion:
regionName = "PDR";
break;
default:
regionName = "Unknown";
msg(tr("parseRegion: Unknown region type"));
};
// Check region base to be in buffer
if (regionOffset >= flashImageSize)
{
msg(tr("parseRegion: %1 region stored in descriptor not found").arg(regionName));
if (twoChips)
msg(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName));
else
msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"));
msg(tr("Absence of %1 region assumed").arg(regionName));
return NULL;
}
// Check region to be fully present in buffer
else if (regionOffset + regionSize > flashImageSize)
{
msg(tr("parseRegion: %1 region stored in descriptor overlaps the end of opened file").arg(regionName));
if (twoChips)
msg(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName));
else
msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"));
msg(tr("Absence of %1 region assumed\n").arg(regionName));
return NULL;
}
// Calculate region address
UINT8* region = calculateAddress16((UINT8*) flashImage.constData(), regionBase);
// Add tree item
QByteArray body = flashImage.mid(regionOffset, regionSize);
index = addTreeItem(RegionItem, regionSubtype, regionOffset, QByteArray(), body, index);
return region;
}
UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent)
{
// Search for first volume
INT32 prevVolumeOffset = findNextVolume(bios);
// No volumes found
if (prevVolumeOffset < 0) {
return ERR_VOLUMES_NOT_FOUND;
}
// First volume is not at the beginning of bios space
if (prevVolumeOffset > 0) {
QByteArray padding = bios.left(prevVolumeOffset);
addTreeItem(PaddingItem, 0, 0, QByteArray(), padding, parent);
}
// Search for and parse all volumes
INT32 volumeOffset;
UINT32 prevVolumeSize;
for (volumeOffset = prevVolumeOffset, prevVolumeSize = 0;
volumeOffset >= 0;
prevVolumeOffset = volumeOffset, prevVolumeSize = getVolumeSize(bios, volumeOffset), volumeOffset = findNextVolume(bios, volumeOffset + prevVolumeSize))
{
// Padding between volumes
if ((UINT32) volumeOffset > prevVolumeOffset + prevVolumeSize) { // Conversion to suppress warning, volumeOffset can't be negative here
UINT32 size = volumeOffset - prevVolumeOffset - prevVolumeSize;
QByteArray padding = bios.mid(prevVolumeOffset + prevVolumeSize, size);
addTreeItem(PaddingItem, 0, prevVolumeOffset + prevVolumeSize, QByteArray(), padding, parent);
}
// Populate volume header
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeOffset);
//Check that volume is fully present in input
if (volumeOffset + volumeHeader->FvLength > bios.size()) {
msg(tr("parseBios: Volume overlaps the end of input buffer"));
return ERR_INVALID_VOLUME;
}
// Check volume revision and alignment
UINT32 alignment;
if (volumeHeader->Revision == 1) {
// Aquire alignment bits
bool alignmentCap = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP;
bool alignment2 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2;
bool alignment4 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4;
bool alignment8 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8;
bool alignment16 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16;
bool alignment32 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32;
bool alignment64 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64;
bool alignment128 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_128;
bool alignment256 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_256;
bool alignment512 = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_512;
bool alignment1k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_1K;
bool alignment2k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_2K;
bool alignment4k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_4K;
bool alignment8k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_8K;
bool alignment16k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_16K;
bool alignment32k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_32K;
bool alignment64k = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_64K;
// Check alignment setup
if (!alignmentCap &&
( alignment2 || alignment4 || alignment8 || alignment16
|| alignment32 || alignment64 || alignment128 || alignment256
|| alignment512 || alignment1k || alignment2k || alignment4k
|| alignment8k || alignment16k || alignment32k || alignment64k))
msg("parseBios: Incompatible revision 1 volume alignment setup");
// Assume that smaller alignment value consumes greater
alignment = 0x01;
if (alignment2)
alignment = 0x02;
else if (alignment4)
alignment = 0x04;
else if (alignment8)
alignment = 0x08;
else if (alignment16)
alignment = 0x10;
else if (alignment32)
alignment = 0x20;
else if (alignment64)
alignment = 0x40;
else if (alignment128)
alignment = 0x80;
else if (alignment256)
alignment = 0x100;
else if (alignment512)
alignment = 0x200;
else if (alignment1k)
alignment = 0x400;
else if (alignment2k)
alignment = 0x800;
else if (alignment4k)
alignment = 0x1000;
else if (alignment8k)
alignment = 0x2000;
else if (alignment16k)
alignment = 0x4000;
else if (alignment32k)
alignment = 0x8000;
else if (alignment64k)
alignment = 0x10000;
// Check alignment
if (volumeOffset % alignment) {
msg(tr("parseBios: Unaligned revision 1 volume"));
}
}
else if (volumeHeader->Revision == 2) {
// Aquire alignment
alignment = pow(2, (volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16);
// Check alignment
if (volumeOffset % alignment) {
msg(tr("parseBios: Unaligned revision 2 volume"));
}
}
else
msg(tr("parseBios: Unknown volume revision (%1)").arg(volumeHeader->Revision));
// Check filesystem GUID to be known
// Do not parse volume with unknown FFS, because parsing will fail
bool parseCurrentVolume = true;
// FFS GUID v1
if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM_GUID) {
// Code can be added here
}
// FFS GUID v2
else if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM2_GUID) {
// Code can be added here
}
// Other GUID
else {
//info = info.append(tr("File system: unknown\n"));
msg(tr("parseBios: Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid)));
parseCurrentVolume = false;
}
// Check attributes
// Determine erase polarity
bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY;
// Check header checksum by recalculating it
if (!calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength)) {
msg(tr("parseBios: Volume header checksum is invalid"));
}
// Check for presence of extended header, only if header revision is not 1
UINT32 headerSize;
if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER*) ((UINT8*) volumeHeader + volumeHeader->ExtHeaderOffset);
headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize;
} else {
headerSize = volumeHeader->HeaderLength;
}
// Adding tree item
QByteArray header = bios.mid(volumeOffset, headerSize);
QByteArray body = bios.mid(volumeOffset + headerSize, volumeHeader->FvLength - headerSize);
QModelIndex index = addTreeItem(VolumeItem, 0, volumeOffset, header, body, parent);
// Parse volume
if (parseCurrentVolume) {
UINT32 result = parseVolume(bios.mid(volumeOffset + headerSize, volumeHeader->FvLength - headerSize), headerSize, volumeHeader->Revision, erasePolarity, index);
if (result)
msg(tr("parseBios: Volume parsing failed (%1)").arg(result));
}
}
return ERR_SUCCESS;
}
INT32 FfsEngine::findNextVolume(const QByteArray & bios, INT32 volumeOffset)
{
if (volumeOffset < 0)
return -1;
INT32 nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset);
if (nextIndex < EFI_FV_SIGNATURE_OFFSET) {
return -1;
}
return nextIndex - EFI_FV_SIGNATURE_OFFSET;
}
UINT32 FfsEngine::getVolumeSize(const QByteArray & bios, INT32 volumeOffset)
{
// Populate volume header
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeOffset);
// Check volume signature
if (QByteArray((const char*) &volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE)
return 0;
return volumeHeader->FvLength;
}
UINT8 FfsEngine::parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent)
{
// Construct empty byte based on erasePolarity value
// Native char type is used because QByteArray.count() takes it
char empty = erasePolarity ? '\xFF' : '\x00';
// Search for and parse all files
INT32 fileOffset = 0;
while (fileOffset >= 0) {
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) (volume.constData() + fileOffset);
QByteArray file = volume.mid(fileOffset, uint24ToUint32(fileHeader->Size));
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
// Check file size to at least sizeof(EFI_FFS_FILE_HEADER)
if (file.size() < sizeof(EFI_FFS_FILE_HEADER))
{
msg(tr("parseVolume: File with invalid size"));
return ERR_INVALID_FILE;
}
// We are at empty space in the end of volume
if (header.count(empty) == header.size()) {
QByteArray body = volume.right(volume.size() - fileOffset);
addTreeItem(PaddingItem, 0, fileOffset, QByteArray(), body, parent);
break;
}
// Check header checksum
QByteArray tempHeader = header;
EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*) (tempHeader.data());
tempFileHeader->IntegrityCheck.Checksum.Header = 0;
tempFileHeader->IntegrityCheck.Checksum.File = 0;
UINT8 calculated = calculateChecksum8((UINT8*) tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
if (fileHeader->IntegrityCheck.Checksum.Header != calculated)
{
msg(tr("parseVolume: %1, stored header checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.Header, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0')));
}
// Check data checksum, if no tail was found
// Data checksum must be calculated
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER);
// Exclude file tail from data checksum calculation
if(revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
bufferSize -= sizeof(UINT16);
calculated = calculateChecksum8((UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize);
if (fileHeader->IntegrityCheck.Checksum.File != calculated) {
msg(tr("parseVolume: %1, stored data checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0')));
}
}
// Data checksum must be one of predefined values
else {
if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM &&fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) {
msg(tr("parseVolume: %1, stored data checksum %2 differs from standard value")
.arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0')));
}
}
// Check file alignment
UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = pow(2, alignmentPower);
if ((volumeBase + fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment) {
msg(tr("parseVolume: %1, unaligned file").arg(guidToQString(fileHeader->Name)));
}
// Get file body
QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER));
// For files in Revision 1 volumes, check for file tail presence
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
{
//Check file tail;
UINT16* tail = (UINT16*) body.right(sizeof(UINT16)).constData();
if (!fileHeader->IntegrityCheck.TailReference == *tail)
msg(tr("parseVolume: %1, file tail value %2 is not a bitwise not of %3 stored in file header")
.arg(guidToQString(fileHeader->Name))
.arg(*tail, 4, 16, QChar('0'))
.arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0')));
// Remove tail from file body
body = body.left(body.size() - sizeof(UINT16));
}
// Parse current file by default
bool parseCurrentFile = true;
// Raw files can hide volumes inside them
// So try to parse them as bios space
bool parseAsBios = false;
// Check file type
//!TODO: add more file specific checks
switch (fileHeader->Type)
{
case EFI_FV_FILETYPE_ALL:
parseAsBios = true;
break;
case EFI_FV_FILETYPE_RAW:
parseAsBios = true;
break;
case EFI_FV_FILETYPE_FREEFORM:
break;
case EFI_FV_FILETYPE_SECURITY_CORE:
break;
case EFI_FV_FILETYPE_PEI_CORE:
break;
case EFI_FV_FILETYPE_DXE_CORE:
break;
case EFI_FV_FILETYPE_PEIM:
break;
case EFI_FV_FILETYPE_DRIVER:
break;
case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
break;
case EFI_FV_FILETYPE_APPLICATION:
break;
case EFI_FV_FILETYPE_SMM:
break;
case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
break;
case EFI_FV_FILETYPE_COMBINED_SMM_DXE:
break;
case EFI_FV_FILETYPE_SMM_CORE:
break;
case EFI_FV_FILETYPE_PAD:
parseCurrentFile = false;
break;
default:
parseCurrentFile = false;
msg(tr("parseVolume: Unknown file type (%1)").arg(fileHeader->Type, 2, 16, QChar('0')));
};
// Check for empty file
if (body.count(empty) == body.size())
{
// No need to parse empty files
parseCurrentFile = false;
}
// Add tree item
QModelIndex index = addTreeItem(FileItem, fileHeader->Type, fileOffset, header, body, parent);
// Parse file
if (parseCurrentFile) {
if (parseAsBios) {
UINT32 result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
msg(tr("parseVolume: Parse file as BIOS failed (%1)").arg(result));
}
else {
UINT32 result = parseFile(body, revision, erasePolarity, index);
if (result)
msg(tr("parseVolume: Parse file as FFS failed (%1)").arg(result));
}
}
// Move to next file
fileOffset += file.size();
fileOffset = ALIGN8(fileOffset);
// Exit from loop if no files left
if (fileOffset >= volume.size())
fileOffset = -1;
}
return ERR_SUCCESS;
}
UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, const QModelIndex & parent)
{
// Search for and parse all sections
INT32 sectionOffset = 0;
while(sectionOffset >= 0)
{
EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (file.constData() + sectionOffset);
UINT32 sectionSize = uint24ToUint32(sectionHeader->Size);
// This declarations must be here because of the nature of switch statement
EFI_COMPRESSION_SECTION* compressedSectionHeader;
EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader;
QByteArray header;
QByteArray body;
UINT32 decompressedSize;
UINT32 scratchSize;
UINT8* decompressed;
UINT8* scratch;
VOID* data;
UINT32 dataSize;
QModelIndex index;
UINT32 result;
UINT32 shittySectionSize;
EFI_COMMON_SECTION_HEADER* shittySectionHeader;
switch (sectionHeader->Type)
{
// Encapsulated sections
case EFI_SECTION_COMPRESSION:
compressedSectionHeader = (EFI_COMPRESSION_SECTION*) sectionHeader;
header = file.mid(sectionOffset, sizeof(EFI_COMPRESSION_SECTION));
// Try to decompress this section
switch (compressedSectionHeader->CompressionType)
{
case EFI_NOT_COMPRESSED:
body = file.mid(sectionOffset + sizeof(EFI_COMPRESSION_SECTION), compressedSectionHeader->UncompressedLength);
index = addTreeItem(SectionItem, CompressionSection, sectionOffset, header, body, parent);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
msg(tr("parseFile: Stored section can not be parsed as file (%1)").arg(result));
break;
case EFI_STANDARD_COMPRESSION:
//Must be Tiano for all revisions, needs checking
body = file.mid(sectionOffset + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
// Get buffer sizes
data = (VOID*) (file.constData() + sectionOffset + sizeof(EFI_COMPRESSION_SECTION));
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION);
if (TianoGetInfo(data, dataSize, &decompressedSize, &scratchSize) != ERR_SUCCESS
|| decompressedSize != compressedSectionHeader->UncompressedLength)
msg(tr("parseFile: TianoGetInfo failed"));
else {
decompressed = new UINT8[decompressedSize];
scratch = new UINT8[scratchSize];
// Decompress section data
if (TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize) != ERR_SUCCESS)
msg(tr("parseFile: TianoDecompress failed"));
else
{
body = QByteArray::fromRawData((const char*) decompressed, decompressedSize);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
msg(tr("parseFile: Compressed section with Tiano compression can not be parsed as file (%1)").arg(result));
}
delete[] decompressed;
delete[] scratch;
}
break;
case EFI_CUSTOMIZED_COMPRESSION:
body = file.mid(sectionOffset + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
// Get buffer sizes
data = (VOID*) (file.constData() + sectionOffset + sizeof(EFI_COMPRESSION_SECTION));
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION);
if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS
|| decompressedSize != compressedSectionHeader->UncompressedLength)
{
// Shitty file with a section header between COMPRESSED_SECTION_HEADER and LZMA_HEADER
// We must determine section header size by checking it's type before we can unpack that non-standard compressed section
shittySectionHeader = (EFI_COMMON_SECTION_HEADER*) data;
shittySectionSize = sizeOfSectionHeaderOfType(shittySectionHeader->Type);
data = (VOID*) (file.constData() + sectionOffset + sizeof(EFI_COMPRESSION_SECTION) + shittySectionSize);
dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION) - shittySectionSize;
if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS)
msg(tr("parseFile: LzmaGetInfo failed"));
}
decompressed = new UINT8[decompressedSize];
// Decompress section data
if (LzmaDecompress(data, dataSize, decompressed) != ERR_SUCCESS)
msg(tr("parseFile: LzmaDecompress failed"));
else
{
body = QByteArray::fromRawData((const char*) decompressed, decompressedSize);
// Parse stored file
result = parseFile(body, revision, erasePolarity, index);
if (result)
msg(tr("parseFile: Compressed section with LZMA compression can not be parsed as file (%1)").arg(result));
}
delete[] decompressed;
break;
default:
body = file.mid(sectionOffset + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
msg(tr("parseFile: Compressed section with unknown compression type found (%1)").arg(compressedSectionHeader->CompressionType));
}
break;
case EFI_SECTION_GUID_DEFINED:
header = file.mid(sectionOffset, sizeof(EFI_GUID_DEFINED_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_GUID_DEFINED_SECTION), sectionSize - sizeof(EFI_GUID_DEFINED_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
// Parse section body as file
guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) (header.constData());
body = file.mid(sectionOffset + guidDefinedSectionHeader->DataOffset, sectionSize - guidDefinedSectionHeader->DataOffset);
result = parseFile(body, revision, erasePolarity, index);
if (result)
msg(tr("parseFile: GUID defined section can not be parsed as file (%1)").arg(result));
break;
case EFI_SECTION_DISPOSABLE:
header = file.mid(sectionOffset, sizeof(EFI_DISPOSABLE_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_DISPOSABLE_SECTION), sectionSize - sizeof(EFI_DISPOSABLE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
// Leaf sections
case EFI_SECTION_PE32:
header = file.mid(sectionOffset, sizeof(EFI_PE32_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_PE32_SECTION), sectionSize - sizeof(EFI_PE32_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_PIC:
header = file.mid(sectionOffset, sizeof(EFI_PIC_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_PIC_SECTION), sectionSize - sizeof(EFI_PIC_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_TE:
header = file.mid(sectionOffset, sizeof(EFI_TE_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_TE_SECTION), sectionSize - sizeof(EFI_TE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_VERSION:
header = file.mid(sectionOffset, sizeof(EFI_VERSION_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_VERSION_SECTION), sectionSize - sizeof(EFI_VERSION_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_USER_INTERFACE:
header = file.mid(sectionOffset, sizeof(EFI_USER_INTERFACE_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_USER_INTERFACE_SECTION), sectionSize - sizeof(EFI_USER_INTERFACE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_COMPATIBILITY16:
header = file.mid(sectionOffset, sizeof(EFI_COMPATIBILITY16_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_COMPATIBILITY16_SECTION), sectionSize - sizeof(EFI_COMPATIBILITY16_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
header = file.mid(sectionOffset, sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION), sectionSize - sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
// Parse section body as BIOS space
result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
msg(tr("parseFile: Firmware volume image can not be parsed (%1)").arg(result));
break;
case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
header = file.mid(sectionOffset, sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION), sectionSize - sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_RAW:
header = file.mid(sectionOffset, sizeof(EFI_RAW_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_RAW_SECTION), sectionSize - sizeof(EFI_RAW_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
// Parse section body as BIOS space
result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND)
msg(tr("parseFile: Raw section can not be parsed as BIOS (%1)").arg(result));
break;
break;
case EFI_SECTION_DXE_DEPEX:
header = file.mid(sectionOffset, sizeof(EFI_DXE_DEPEX_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_DXE_DEPEX_SECTION), sectionSize - sizeof(EFI_DXE_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_PEI_DEPEX:
header = file.mid(sectionOffset, sizeof(EFI_PEI_DEPEX_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_PEI_DEPEX_SECTION), sectionSize - sizeof(EFI_PEI_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
case EFI_SECTION_SMM_DEPEX:
header = file.mid(sectionOffset, sizeof(EFI_SMM_DEPEX_SECTION));
body = file.mid(sectionOffset + sizeof(EFI_SMM_DEPEX_SECTION), sectionSize - sizeof(EFI_SMM_DEPEX_SECTION));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
break;
default:
msg(tr("parseFile: Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0')));
header = file.mid(sectionOffset, sizeof(EFI_COMMON_SECTION_HEADER));
body = file.mid(sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER));
index = addTreeItem(SectionItem, sectionHeader->Type, sectionOffset, header, body, parent);
}
// Move to next section
sectionOffset += uint24ToUint32(sectionHeader->Size);
sectionOffset = ALIGN4(sectionOffset);
// Exit from loop if no sections left
if (sectionOffset >= file.size())
sectionOffset = -1;
}
return ERR_SUCCESS;
}
bool FfsEngine::hasEmptyHeader(const QModelIndex& index) const
{
if (!index.isValid())
return true;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->hasEmptyHeader();
}
bool FfsEngine::hasEmptyBody(const QModelIndex& index) const
{
if (!index.isValid())
return true;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->hasEmptyBody();
}
QModelIndex FfsEngine::addTreeItem(const UINT8 type, const UINT8 subtype, const UINT32 offset, const QByteArray &header, const QByteArray &body, const QModelIndex &parent)
{
TreeItem *parentItem;
int parentColumn = 0;
if (!parent.isValid())
parentItem = rootItem;
else
{
parentItem = static_cast<TreeItem*>(parent.internalPointer());
parentColumn = parent.column();
}
// All information extraction must be here
QString name, typeName, subtypeName, info;
EFI_CAPSULE_HEADER* capsuleHeader;
APTIO_CAPSULE_HEADER* aptioCapsuleHeader;
FLASH_DESCRIPTOR_MAP* descriptorMap;
//FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection;
//FLASH_DESCRIPTOR_REGION_SECTION* regionSection;
//FLASH_DESCRIPTOR_MASTER_SECTION* masterSection;
GBE_MAC* gbeMac;
GBE_VERSION* gbeVersion;
ME_VERSION* meVersion;
INT32 meVersionOffset;
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader;
EFI_FFS_FILE_HEADER* fileHeader;
//EFI_COMMON_SECTION_HEADER* sectionHeader;
switch (type)
{
case RootItem:
// Do not allow to add another root item
return QModelIndex();
break;
case CapsuleItem:
//typeName = tr("Capsule");
switch (subtype)
{
case AptioCapsule:
name = tr("AMI Aptio capsule");
aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) header.constData();
info = tr("Offset: %1\nHeader size: %2\nFlags: %3\nImage size: %4")
.arg(offset, 8, 16, QChar('0'))
.arg(aptioCapsuleHeader->RomImageOffset, 4, 16, QChar('0'))
.arg(aptioCapsuleHeader->CapsuleHeader.Flags, 8, 16, QChar('0'))
.arg(aptioCapsuleHeader->CapsuleHeader.CapsuleImageSize - aptioCapsuleHeader->RomImageOffset, 8, 16, QChar('0'));
//!TODO: more info about Aptio capsule
break;
case UefiCapsule:
name = tr("UEFI capsule");
capsuleHeader = (EFI_CAPSULE_HEADER*) header.constData();
info = tr("Offset: %1\nHeader size: %2\nFlags: %3\nImage size: %4")
.arg(offset, 8, 16, QChar('0'))
.arg(capsuleHeader->HeaderSize, 8, 16, QChar('0'))
.arg(capsuleHeader->Flags, 8, 16, QChar('0'))
.arg(capsuleHeader->CapsuleImageSize, 8, 16, QChar('0'));
break;
default:
name = tr("Unknown capsule");
info = tr("Offset: %1\nGUID: %2\n")
.arg(offset, 8, 16, QChar('0'))
.arg(guidToQString(*(EFI_GUID*)header.constData()));
break;
}
break;
case DescriptorItem:
name = tr("Descriptor");
descriptorMap = (FLASH_DESCRIPTOR_MAP*) body.constData();
info = tr("Flash chips: %1\nRegions: %2\nMasters: %3\nPCH straps:%4\nPROC straps: %5\nICC table entries: %6")
.arg(descriptorMap->NumberOfFlashChips + 1) //
.arg(descriptorMap->NumberOfRegions + 1) // Zero-based numbers in storage
.arg(descriptorMap->NumberOfMasters + 1) //
.arg(descriptorMap->NumberOfPchStraps)
.arg(descriptorMap->NumberOfProcStraps)
.arg(descriptorMap->NumberOfIccTableEntries);
//!TODO: more info about descriptor
break;
case RegionItem:
typeName = tr("Region");
info = tr("Offset: %1\nSize: %2")
.arg(offset, 8, 16, QChar('0'))
.arg(body.size(), 8, 16, QChar('0'));
switch (subtype)
{
case GbeRegion:
name = tr("GbE region");
gbeMac = (GBE_MAC*) body.constData();
gbeVersion = (GBE_VERSION*) (body.constData() + GBE_VERSION_OFFSET);
info += tr("\nMAC: %1:%2:%3:%4:%5:%6\nVersion: %7.%8")
.arg(gbeMac->vendor[0], 2, 16, QChar('0'))
.arg(gbeMac->vendor[1], 2, 16, QChar('0'))
.arg(gbeMac->vendor[2], 2, 16, QChar('0'))
.arg(gbeMac->device[0], 2, 16, QChar('0'))
.arg(gbeMac->device[1], 2, 16, QChar('0'))
.arg(gbeMac->device[2], 2, 16, QChar('0'))
.arg(gbeVersion->major)
.arg(gbeVersion->minor);
break;
case MeRegion:
name = tr("ME region");
meVersionOffset = body.indexOf(ME_VERSION_SIGNATURE);
if (meVersionOffset < 0){
info += tr("\nVersion: unknown");
msg(tr("addTreeItem: ME region version is unknown, it can be damaged"));
}
else {
meVersion = (ME_VERSION*) (body.constData() + meVersionOffset);
info += tr("\nVersion: %1.%2.%3.%4")
.arg(meVersion->major)
.arg(meVersion->minor)
.arg(meVersion->bugfix)
.arg(meVersion->build);
}
break;
case BiosRegion:
name = tr("BIOS region");
break;
case PdrRegion:
name = tr("PDR region");
break;
default:
name = tr("Unknown region");
msg(tr("addTreeItem: Unknown region"));
break;
}
break;
case PaddingItem:
name = tr("Padding");
info = tr("Offset: %1\nSize: %2")
.arg(offset, 8, 16, QChar('0'))
.arg(body.size(), 8, 16, QChar('0'));
break;
case VolumeItem:
typeName = tr("Volume");
// Parse volume header to determine its revision and file system
volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) header.constData();
name = guidToQString(volumeHeader->FileSystemGuid);
subtypeName = tr("Revision %1").arg(volumeHeader->Revision);
info = tr("Offset: %1\nSize: %2\nAttributes: %3\nHeader size: %4")
.arg(offset, 8, 16, QChar('0'))
.arg(volumeHeader->FvLength, 8, 16, QChar('0'))
.arg(volumeHeader->Attributes, 8, 16, QChar('0'))
.arg(volumeHeader->HeaderLength, 4, 16, QChar('0'));
break;
case FileItem:
typeName = tr("File");
// Parse file header to determine its GUID and type
fileHeader = (EFI_FFS_FILE_HEADER*) header.constData();
name = guidToQString(fileHeader->Name);
subtypeName = fileTypeToQString(subtype);
info = tr("Offset: %1\nType: %2\nAttributes: %3\nSize: %4\nState: %5")
.arg(offset, 8, 16, QChar('0'))
.arg(fileHeader->Type, 2, 16, QChar('0'))
.arg(fileHeader->Attributes, 2, 16, QChar('0'))
.arg(uint24ToUint32(fileHeader->Size), 6, 16, QChar('0'))
.arg(fileHeader->State, 2, 16, QChar('0'));
break;
case SectionItem:
typeName = tr("Section");
name = sectionTypeToQString(subtype) + tr(" section");
info = tr("Offset: %1\nSize: %2")
.arg(offset, 8, 16, QChar('0'))
.arg(body.size(), 8, 16, QChar('0'));
//!TODO: add more specific info for all section types with uncommon headers
// Set name of file
if (subtype == UserInterfaceSection)
{
QString text = QString::fromUtf16((const ushort*)body.constData());
setTreeItemText(text, findParentOfType(FileItem, parent));
}
break;
default:
name = tr("Unknown");
info = tr("Offset: %1").arg(offset, 8, 16, QChar('0'));
break;
}
return treeModel->addItem(type, subtype, offset, name, typeName, subtypeName, "", info, header, body, parent);
}
bool FfsEngine::setTreeItemName(const QString &data, const QModelIndex &index)
{
if(!index.isValid())
return false;
return treeModel->setItemName(data, index);
}
bool FfsEngine::setTreeItemText(const QString &data, const QModelIndex &index)
{
if(!index.isValid())
return false;
return treeModel->setItemText(data, index);
}
bool FfsEngine::removeItem(const QModelIndex &index)
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
item->parent()->removeChild(item);
delete item;
return true;
}
QModelIndex FfsEngine::findParentOfType(UINT8 type, const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
TreeItem *item;
QModelIndex parent = index;
for(item = static_cast<TreeItem*>(parent.internalPointer());
item != NULL && item != rootItem && item->type() != type;
item = static_cast<TreeItem*>(parent.internalPointer()))
parent = parent.parent();
if (item != NULL && item != rootItem)
return parent;
return QModelIndex();
}
QByteArray FfsEngine::header(const QModelIndex& index) const
{
if (!index.isValid())
return QByteArray();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->header();
}
QByteArray FfsEngine::body(const QModelIndex& index) const
{
if (!index.isValid())
return QByteArray();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->body();
}
QByteArray FfsEngine::uncompressFile(const QModelIndex& index) const
{
if (!index.isValid())
return QByteArray();
// Check index item to be FFS file
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(item->type() != FileItem)
return QByteArray();
QByteArray file;
UINT32 offset = 0;
// Construct new item body
for (int i = 0; i < item->childCount(); i++)
{
// If section is not compressed, add it to new body as is
TreeItem* sectionItem = item->child(i);
if (sectionItem->subtype() != CompressionSection)
{
QByteArray section = sectionItem->header().append(sectionItem->body());
UINT32 align = ALIGN4(offset) - offset;
file.append(QByteArray(align, '\x00')).append(section);
offset += align + section.size();
}
else {
// Construct new section body by adding all child sections to this new section
QByteArray section;
UINT32 subOffset = 0;
for (int j = 0; j < sectionItem->childCount(); j++)
{
TreeItem* subSectionItem = sectionItem->child(j);
QByteArray subSection = subSectionItem->header().append(subSectionItem->body());
UINT32 align = ALIGN4(subOffset) - subOffset;
section.append(QByteArray(align, '\x00')).append(subSection);
subOffset += align + subSection.size();
}
// Add newly constructed section to file body
EFI_COMPRESSION_SECTION sectionHeader;
sectionHeader.Type = EFI_SECTION_COMPRESSION;
sectionHeader.CompressionType = EFI_NOT_COMPRESSED;
sectionHeader.UncompressedLength = section.size();
uint32ToUint24(section.size() + sizeof(EFI_COMPRESSION_SECTION), sectionHeader.Size);
UINT32 align = ALIGN4(offset) - offset;
file.append(QByteArray(align, '\x00'))
.append(QByteArray((const char*) &sectionHeader, sizeof(EFI_COMPRESSION_SECTION)))
.append(section);
offset += align + section.size();
}
}
QByteArray header = item->header();
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) header.data();
// Correct file data checksum, if needed
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM)
{
UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER);
fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((UINT8*)(file.data() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize);
}
// Add file tail, if needed
if(fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
file.append(!fileHeader->IntegrityCheck.TailReference);
return header.append(file);
}
bool FfsEngine::isCompressedFile(const QModelIndex& index) const
{
if (!index.isValid())
return false;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if(item->type() != FileItem)
return false;
for (int i = 0; i < item->childCount(); i++)
{
if (item->child(i)->subtype() == CompressionSection)
return true;
}
return false;
}