add FFSv3 support with large files and large sections

This commit is contained in:
Alex Matrosov 2017-12-10 16:54:40 -08:00
parent 75225ecc28
commit 73019876cf
6 changed files with 212 additions and 133 deletions

View file

@ -287,15 +287,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in
const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase);
// Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency
UINT8 descriptorVersion = 0;
if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor
UINT8 descriptorVersion = 2; // Skylake+ by default
if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor
descriptorVersion = 1;
else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor
descriptorVersion = 2;
else {
msg(tr("parseIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// ME region
QByteArray me;
@ -983,15 +977,16 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
else
headerSize = volumeHeader->HeaderLength;
// Sanity check after some new crazy MSI images
// Sanity check after some crazy MSI images
headerSize = ALIGN8(headerSize);
// Check for volume structure to be known
bool volumeIsUnknown = true;
// Check for FFS v2 volume
if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) {
volumeIsUnknown = false;
// Check for FFS v2/v3 volume
UINT8 subtype = Subtypes::UnknownVolume;
if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))){
subtype = Subtypes::Ffs2Volume;
}
else if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) {
subtype = Subtypes::Ffs3Volume;
}
// Check attributes
@ -1063,10 +1058,10 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Add tree item
QByteArray header = volume.left(headerSize);
QByteArray body = volume.mid(headerSize, volumeSize - headerSize);
index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode);
index = model->addItem(Types::Volume, subtype, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode);
// Show messages
if (volumeIsUnknown) {
if (subtype == Subtypes::UnknownVolume) {
msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index);
// Do not parse unknown volumes
return ERR_SUCCESS;
@ -1100,19 +1095,42 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
break;
}
result = getFileSize(volume, fileOffset, fileSize);
if (result)
return result;
QByteArray tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER));
const EFI_FFS_FILE_HEADER* tempFileHeader = (const EFI_FFS_FILE_HEADER*)tempFile.constData();
UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER);
fileSize = uint24ToUint32(tempFileHeader->Size);
if (volumeHeader->Revision > 1 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
// Check if it's possibly the latest file in the volume
if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER2)) {
// No files are possible after this point
// All the rest is either free space or non-UEFI data
QByteArray rest = volume.right(volumeSize - fileOffset);
if (rest.count(empty) == rest.size()) { // It's a free space
model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index);
}
else { //It's non-UEFI data
QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index);
msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex);
}
// Exit from loop
break;
}
// Check file size to be at least size of EFI_FFS_FILE_HEADER
if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) {
fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2);
tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER2));
const EFI_FFS_FILE_HEADER2* tempFileHeader2 = (const EFI_FFS_FILE_HEADER2*)tempFile.constData();
fileSize = (UINT32)tempFileHeader2->ExtendedSize;
}
// Check file size to be at least size of the header
if (fileSize < fileHeaderSize) {
msg(tr("parseVolume: volume has FFS file with invalid size"), index);
return ERR_INVALID_FILE;
}
QByteArray file = volume.mid(fileOffset, fileSize);
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
QByteArray header = file.left(fileHeaderSize);
// If we are at empty space in the end of volume
if (header.count(empty) == header.size()) {
// Check free space to be actually free
@ -1152,8 +1170,11 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Check file alignment
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData();
UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = (UINT32)pow(2.0, alignmentPower);
if ((fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment)
if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT2))
alignmentPower = ffsAlignment2Table[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = (UINT32)(1UL << alignmentPower);
if ((fileOffset + fileHeaderSize) % alignment)
msgUnalignedFile = true;
// Check file GUID
@ -1165,7 +1186,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Parse file
QModelIndex fileIndex;
result = parseFile(file, fileIndex, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index);
result = parseFile(file, fileIndex, volumeHeader->Revision, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index);
if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME)
msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index);
@ -1183,16 +1204,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
return ERR_SUCCESS;
}
UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize)
{
if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_VOLUME;
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset);
fileSize = uint24ToUint32(fileHeader->Size);
return ERR_SUCCESS;
}
UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode)
UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode)
{
bool msgInvalidHeaderChecksum = false;
bool msgInvalidDataChecksum = false;
@ -1200,43 +1212,32 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
bool msgInvalidType = false;
// Populate file header
if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_FILE;
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData();
// Check file state
// Construct empty byte for this file
char empty = erasePolarity ? '\xFF' : '\x00';
// Check header checksum
// Get file header
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
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((const UINT8*)tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
if (fileHeader->IntegrityCheck.Checksum.Header != calculated)
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2))
return ERR_INVALID_FILE;
header = file.left(sizeof(EFI_FFS_FILE_HEADER2));
}
// Check header checksum
UINT8 calculatedHeader = 0x100 -(calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State);
if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader)
msgInvalidHeaderChecksum = true;
// Check data checksum
// 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 (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
bufferSize -= sizeof(UINT16);
calculated = calculateChecksum8((const UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize);
if (fileHeader->IntegrityCheck.Checksum.File != calculated)
msgInvalidDataChecksum = true;
}
// Data checksum must be one of predefined values
else if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2)
msgInvalidDataChecksum = true;
// Get file body
QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER));
QByteArray body = file.mid(header.size());
// Check for file tail presence
QByteArray tail;
if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
{
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
//Check file tail;
tail = body.right(sizeof(UINT16));
UINT16 tailValue = *(UINT16*)tail.constData();
@ -1247,48 +1248,47 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
body = body.left(body.size() - sizeof(UINT16));
}
// Check data checksum
// Data checksum must be calculated
UINT8 calculatedData = 0;
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
calculatedData = calculateChecksum8((const UINT8*)body.constData(), body.size());
if (fileHeader->IntegrityCheck.Checksum.File != calculatedData)
msgInvalidDataChecksum = true;
}
// Data checksum must be one of predefined values
else if ((revision == 1 && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM)
|| fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2)
msgInvalidDataChecksum = true;
// Parse current file by default
bool parseCurrentFile = true;
bool parseCurrentFile = false;
bool parseAsBios = false;
// Check file type
switch (fileHeader->Type)
{
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_SMM_STANDALONE:
case EFI_FV_FILETYPE_SMM_CORE_STANDALONE:
case EFI_FV_FILETYPE_PAD:
parseCurrentFile = true;
break;
default:
msgInvalidType = true;
parseCurrentFile = false;
};
// Check for empty file
@ -1310,25 +1310,27 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
else
name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file");
info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h")
info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h\nHeader checksum: %11h\nData checksum: %12h")
.arg(guidToQString(fileHeader->Name))
.hexarg2(fileHeader->Type, 2)
.hexarg2(fileHeader->Attributes, 2)
.hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size())
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(fileHeader->State, 2);
.hexarg2(fileHeader->State, 2)
.hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2)
.hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2);
// Add tree item
index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode);
// Show messages
if (msgInvalidHeaderChecksum)
msg(tr("parseFile: invalid header checksum"), index);
msg(tr("parseFile: invalid header checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2).hexarg2(calculatedHeader, 2), index);
if (msgInvalidDataChecksum)
msg(tr("parseFile: invalid data checksum"), index);
msg(tr("parseFile: invalid data checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2).hexarg2(calculatedData, 2), index);
if (msgInvalidTailValue)
msg(tr("parseFile: invalid tail value"), index);
msg(tr("parseFile: invalid tail value %1h").hexarg(*(UINT16*)tail.data()), index);
if (msgInvalidType)
msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index);
@ -1382,8 +1384,15 @@ UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOff
{
if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER))
return ERR_INVALID_FILE;
const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset);
sectionSize = uint24ToUint32(sectionHeader->Size);
// This may introduce a very rare error with a non-extended section of size equal to 0xFFFFFF
if (sectionSize != 0xFFFFFF)
return ERR_SUCCESS;
const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset);
sectionSize = sectionHeader2->ExtendedSize;
return ERR_SUCCESS;
}
@ -1670,7 +1679,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c
}
}
else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
info += tr("\nSignature type: PCKS7");
info += tr("\nSignature type: PKCS7");
// TODO: show signature info in Information panel
}
else {
@ -2219,28 +2228,51 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte
if (header.size() != sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_FILE;
QByteArray newHeader = header;
QByteArray newBody = body;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data();
QByteArray newObject = header + body;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newObject.data();
// Determine correct file header size
bool largeFile = false;
UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER);
if (revision == 2 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
largeFile = true;
headerSize = sizeof(EFI_FFS_FILE_HEADER2);
}
QByteArray newHeader = newObject.left(headerSize);
QByteArray newBody = newObject.mid(headerSize);
// Check if the file has a tail
UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0;
UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0;
if (tailSize) {
// Remove the tail, it will then be added back for revision 1 volumes
newBody = newBody.left(newBody.size() - tailSize);
// Remove the attribute for rev2+ volumes
if (revision != 1) {
fileHeader->Attributes &= ~(FFS_ATTRIB_TAIL_PRESENT);
}
}
// Correct file size
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + newBody.size() + tailSize, fileHeader->Size);
if (!largeFile) {
if (newBody.size() >= 0xFFFFFF) {
return ERR_INVALID_FILE;
}
uint32ToUint24(headerSize + newBody.size() + tailSize, fileHeader->Size);
}
else {
uint32ToUint24(0xFFFFFF, fileHeader->Size);
EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)newHeader.data();
fileHeader2->ExtendedSize = headerSize + newBody.size() + tailSize;
}
// Set file state
UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION;
if (erasePolarity)
state = ~state;
fileHeader->State = state;
// Recalculate header checksum
fileHeader->IntegrityCheck.Checksum.Header = 0;
fileHeader->IntegrityCheck.Checksum.File = 0;
fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)newHeader.constData(), headerSize) - fileHeader->State);
// Recalculate data checksum, if needed
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM)
@ -2254,23 +2286,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte
created.append(newBody);
// Append tail, if needed
if (revision ==1 && tailSize) {
if (revision == 1 && tailSize) {
UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header;
UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File;
created.append(ht).append(ft);
}
// Set file state
UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION;
if (erasePolarity)
state = ~state;
fileHeader->State = state;
// Prepend header
created.prepend(newHeader);
// Parse file
result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode);
result = parseFile(created, fileIndex, revision, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode);
if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME)
return result;
@ -2428,17 +2454,20 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
}
else if (model->type(parent) == Types::File) {
type = Types::Section;
const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeader(commonHeader);
}
else if (model->type(parent) == Types::Section) {
type = Types::Section;
const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeader(commonHeader);
}
else
return ERR_NOT_IMPLEMENTED;
if ((UINT32)object.size() < headerSize)
return ERR_BUFFER_TOO_SMALL;
return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, Actions::Insert);
}
@ -2873,6 +2902,9 @@ UINT8 FfsEngine::constructPadFile(const QByteArray &guid, const UINT32 size, con
if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN)
return ERR_INVALID_PARAMETER;
if (size >= 0xFFFFFF) // TODO: large file support
return ERR_INVALID_PARAMETER;
pad = QByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00');
pad.prepend(guid);
EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data();
@ -3265,6 +3297,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){
QModelIndex peiFile = index.child(i, 0);
UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER);
// BUGBUG: this parsing is bad and doesn't support large files, but it needs to be performed only for very old images with uncompressed DXE volumes, so whatever
// Search for PE32 or TE section
for (int j = 0; j < model->rowCount(peiFile); j++) {
if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 ||
@ -3330,6 +3363,9 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
continue;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data();
UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER);
if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE))
fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2);
// Pad file
if (fileHeader->Type == EFI_FV_FILETYPE_PAD) {
@ -3357,8 +3393,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
UINT8 alignmentPower;
UINT32 alignmentBase;
alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
alignment = (UINT32)pow(2.0, alignmentPower);
alignmentBase = header.size() + offset + sizeof(EFI_FFS_FILE_HEADER);
alignment = (UINT32)(1UL <<alignmentPower);
alignmentBase = header.size() + offset + fileHeaderSize;
if (alignmentBase % alignment) {
// File will be unaligned if added as is, so we must add pad file before it
// Determine pad file size
@ -3577,7 +3613,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
model->action(index) == Actions::Rebuild) {
QByteArray header = model->header(index);
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data();
// Check erase polarity
if (erasePolarity == ERASE_POLARITY_UNKNOWN) {
msg(tr("reconstructFile: unknown erase polarity"), index);
@ -3635,6 +3671,10 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
// File contains sections
else {
UINT32 offset = 0;
UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER);
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
headerSize = sizeof(EFI_FFS_FILE_HEADER2);
}
for (int i = 0; i < model->rowCount(index); i++) {
// Align to 4 byte boundary
@ -3646,7 +3686,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
}
// Calculate section base
UINT32 sectionBase = base ? base + sizeof(EFI_FFS_FILE_HEADER) + offset : 0;
UINT32 sectionBase = base ? base + headerSize + offset : 0;
// Reconstruct section
QByteArray section;
@ -3668,13 +3708,22 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
// Correct file size
UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0;
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size);
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
uint32ToUint24(EFI_SECTION2_IS_USED, fileHeader->Size);
EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*) fileHeader;
fileHeader2->ExtendedSize = sizeof(EFI_FFS_FILE_HEADER2) + reconstructed.size() + tailSize;
} else {
if (sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize > 0xFFFFFF) {
msg(tr("reconstructFile: resulting file size is too big"), index);
return ERR_INVALID_FILE;
}
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size);
}
// Recalculate header checksum
fileHeader->IntegrityCheck.Checksum.Header = 0;
fileHeader->IntegrityCheck.Checksum.File = 0;
fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State);
}
// Use current file body
else
@ -3690,7 +3739,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2;
// Append tail, if needed
if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header;
UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File;
reconstructed.append(ht).append(ft);
@ -3732,6 +3781,10 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base,
model->action(index) == Actions::Rebase) {
QByteArray header = model->header(index);
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data();
bool extended = false;
if(uint24ToUint32(commonHeader->Size) == 0xFFFFFF) {
extended = true;
}
// Reconstruct section with children
if (model->rowCount(index)) {
@ -3832,7 +3885,13 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base,
}
// Correct section size
uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size);
if (extended) {
EFI_COMMON_SECTION_HEADER2 * extHeader = (EFI_COMMON_SECTION_HEADER2*) commonHeader;
extHeader->ExtendedSize = header.size() + reconstructed.size();
uint32ToUint24(0xFFFFFF, commonHeader->Size);
} else {
uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size);
}
}
// Leaf section
else