Hello community, here is the log from the commit of package UEFITool for openSUSE:Factory checked in at 2018-03-09 10:46:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/UEFITool (Old) and /work/SRC/openSUSE:Factory/.UEFITool.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "UEFITool" Fri Mar 9 10:46:25 2018 rev:3 rq:584443 version:20180225 Changes: -------- --- /work/SRC/openSUSE:Factory/UEFITool/UEFITool.changes 2017-07-25 11:41:16.188942464 +0200 +++ /work/SRC/openSUSE:Factory/.UEFITool.new/UEFITool.changes 2018-03-09 10:46:27.502975575 +0100 @@ -1,0 +2,11 @@ +Thu Mar 8 14:01:24 UTC 2018 - [email protected] + +- Update to version 20180225: + * fixed rebase PEI files with nonstandard alignment + * Fixed patches for Skylake-X + * multiple fixes + * fix for #99 + * 0.22.1 multiple FFSv3 support fixes, thanks to @osresearch + * add FFSv3 support with large files and large sections + +------------------------------------------------------------------- Old: ---- UEFITool-20170506.tar.xz New: ---- UEFITool-20180225.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ UEFITool.spec ++++++ --- /var/tmp/diff_new_pack.hOQ5u9/_old 2018-03-09 10:46:28.282947481 +0100 +++ /var/tmp/diff_new_pack.hOQ5u9/_new 2018-03-09 10:46:28.282947481 +0100 @@ -1,7 +1,7 @@ # # spec file for package UEFITool # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,12 +17,12 @@ Name: UEFITool -Version: 20170506 +Version: 20180225 Release: 0 Summary: Tools to inspect and work on UEFI BIOSes License: BSD-2-Clause Group: Development/Tools/Other -Url: https://github.com/LongSoft/UEFITool +URL: https://github.com/LongSoft/UEFITool Source0: %{name}-%{version}.tar.xz BuildRequires: pkgconfig BuildRequires: pkgconfig(Qt5Core) @@ -49,17 +49,17 @@ done %install -install -D -p -m 0755 UEFITool \ +install -Dpm 0755 UEFITool \ %{buildroot}/%{_bindir}/UEFITool for i in UEFIExtract UEFIFind UEFIPatch UEFIReplace; do pushd $i - install -D -p -m 0755 $i %{buildroot}/%{_bindir}/$i + install -Dpm 0755 $i %{buildroot}/%{_bindir}/$i popd done %files -%defattr(-,root,root) -%doc LICENSE.md README.rst +%license LICENSE.md +%doc README.rst %{_bindir}/UEFITool %{_bindir}/UEFIExtract %{_bindir}/UEFIFind ++++++ UEFITool-20170506.tar.xz -> UEFITool-20180225.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/UEFIExtract/uefiextract_main.cpp new/UEFITool-20180225/UEFIExtract/uefiextract_main.cpp --- old/UEFITool-20170506/UEFIExtract/uefiextract_main.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/UEFIExtract/uefiextract_main.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -1,6 +1,6 @@ /* uefiextract_main.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2018, LongSoft. 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 @@ -19,8 +19,8 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); + a.setOrganizationName("LongSoft"); + a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIExtract"); UEFIExtract w; @@ -52,7 +52,7 @@ } else { - std::cout << "UEFIExtract 0.4.4" << std::endl << std::endl << + std::cout << "UEFIExtract 0.4.5" << std::endl << std::endl << "Usage: uefiextract imagefile [FileGUID_1 FileGUID_2 ... FileGUID_31]" << std::endl << "Returned value is a bit mask where 0 on position N meant File with GUID_N was found and unpacked, 1 otherwise" << std::endl; return 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/UEFIPatch/patches.txt new/UEFITool-20180225/UEFIPatch/patches.txt --- old/UEFITool-20170506/UEFIPatch/patches.txt 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/UEFIPatch/patches.txt 2018-02-26 00:40:14.000000000 +0100 @@ -34,6 +34,7 @@ # PowerManagement | Sandy Bridge with ME 8.xx, Ivy Bridge 8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 + # PowerManagement | New SB-E/IB-E 8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:0FBA6C24380F:0FBA7424380F @@ -48,5 +49,11 @@ 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:75080D00800000:EB080D00800000 # SiInit | Kaby Lake -299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:81E10080000033C1:9090909090909090 +299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:81E10080000033C1:9090909090909090 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:81E10080000033C1:9090909090909090 + +# PpmInitialize | Skylake-X +3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:742CB9E2000000:752CB9E2000000 + +# CpuInitPei | Skylake-X +01359D99-9446-456D-ADA4-50A711C03ADA 12 P:BE0080000023CE0B:BE0000000023CE0B \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/UEFIPatch/uefipatch_main.cpp new/UEFITool-20180225/UEFIPatch/uefipatch_main.cpp --- old/UEFITool-20170506/UEFIPatch/uefipatch_main.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/UEFIPatch/uefipatch_main.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -1,6 +1,6 @@ /* uefipatch_main.cpp -Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +Copyright (c) 2018, LongSoft. 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 @@ -19,8 +19,8 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); + a.setOrganizationName("LongSoft"); + a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIPatch"); UEFIPatch w; @@ -31,7 +31,7 @@ result = w.patchFromFile(a.arguments().at(1)); } else { - std::cout << "UEFIPatch 0.3.9 - UEFI image file patching utility" << std::endl << std::endl << + std::cout << "UEFIPatch 0.3.13 - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file" << std::endl << std::endl << "Patches will be read from patches.txt file\n"; return ERR_SUCCESS; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/UEFIReplace/uefireplace_main.cpp new/UEFITool-20180225/UEFIReplace/uefireplace_main.cpp --- old/UEFITool-20170506/UEFIReplace/uefireplace_main.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/UEFIReplace/uefireplace_main.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -19,8 +19,8 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); + a.setOrganizationName("LongSoft"); + a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIReplace"); UEFIReplace r; @@ -28,7 +28,7 @@ QStringList args = a.arguments(); if (args.length() < 5) { - std::cout << "UEFIReplace 0.3.9 - UEFI image file replacement utility" << std::endl << std::endl << + std::cout << "UEFIReplace 0.1.1 - UEFI image file replacement utility" << std::endl << std::endl << "Usage: UEFIReplace image_file guid section_type contents_file" << std::endl; return ERR_SUCCESS; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/ffs.cpp new/UEFITool-20180225/ffs.cpp --- old/UEFITool-20170506/ffs.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/ffs.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -30,7 +30,10 @@ const UINT8 ffsAlignmentTable[] = { 0, 4, 7, 9, 10, 12, 15, 16 }; -UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) +const UINT8 ffsAlignment2Table[] = +{ 17, 18, 19, 20, 21, 22, 23, 24 }; + +UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize) { if (!buffer) return 0; @@ -40,7 +43,15 @@ while (bufferSize--) counter += buffer[bufferSize]; - return (UINT8)0x100 - counter; + return counter; +} + +UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) +{ + if (!buffer) + return 0; + + return (UINT8)0x100 - calculateSum8(buffer, bufferSize); } UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize) @@ -120,6 +131,8 @@ case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image"); case EFI_FV_FILETYPE_COMBINED_SMM_DXE: return QObject::tr("Combined SMM/DXE"); case EFI_FV_FILETYPE_SMM_CORE: return QObject::tr("SMM core"); + case EFI_FV_FILETYPE_SMM_STANDALONE: return QObject::tr("SMM standalone"); + case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: return QObject::tr("SMM core standalone"); case EFI_FV_FILETYPE_PAD: return QObject::tr("Pad"); default: return QObject::tr("Unknown"); }; @@ -155,10 +168,10 @@ if (!header) return 0; - const bool extended = false; - /*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { + bool extended = false; + if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { extended = true; - }*/ + } switch (header->Type) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/ffs.h new/UEFITool-20180225/ffs.h --- old/UEFITool-20170506/ffs.h 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/ffs.h 2018-02-26 00:40:14.000000000 +0100 @@ -292,7 +292,7 @@ UINT8 Attributes; UINT8 Size[3]; // Set to 0xFFFFFF UINT8 State; -UINT32 ExtendedSize; +UINT64 ExtendedSize; } EFI_FFS_FILE_HEADER2; // Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear @@ -314,6 +314,8 @@ #define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B #define EFI_FV_FILETYPE_COMBINED_SMM_DXE 0x0C #define EFI_FV_FILETYPE_SMM_CORE 0x0D +#define EFI_FV_FILETYPE_SMM_STANDALONE 0x0E +#define EFI_FV_FILETYPE_SMM_CORE_STANDALONE 0x0F #define EFI_FV_FILETYPE_OEM_MIN 0xC0 #define EFI_FV_FILETYPE_OEM_MAX 0xDF #define EFI_FV_FILETYPE_DEBUG_MIN 0xE0 @@ -326,6 +328,7 @@ #define FFS_ATTRIB_TAIL_PRESENT 0x01 // Valid only for revision 1 volumes #define FFS_ATTRIB_RECOVERY 0x02 // Valid only for revision 1 volumes #define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes +#define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Valid only for revision 2 volumes #define FFS_ATTRIB_FIXED 0x04 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 @@ -333,6 +336,9 @@ // FFS alignment table extern const UINT8 ffsAlignmentTable[]; +// Extended FFS alignment table +extern const UINT8 ffsAlignment2Table[]; + // File states #define EFI_FILE_HEADER_CONSTRUCTION 0x01 #define EFI_FILE_HEADER_VALID 0x02 @@ -340,6 +346,7 @@ #define EFI_FILE_MARKED_FOR_UPDATE 0x08 #define EFI_FILE_DELETED 0x10 #define EFI_FILE_HEADER_INVALID 0x20 +#define EFI_FILE_ERASE_POLARITY 0x80 // Defined as "all other bits must be set to ERASE_POLARITY" in UEFI PI // PEI apriori file const QByteArray EFI_PEI_APRIORI_FILE_GUID @@ -360,7 +367,9 @@ // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); -// FFS file 8bit checksum calculation routine + +// FFS file 8bit checksum calculation routines +extern UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize); extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); //***************************************************************************** @@ -528,7 +537,7 @@ typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2; -//Section routines +// Section routines extern UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header); //***************************************************************************** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/ffsengine.cpp new/UEFITool-20180225/ffsengine.cpp --- old/UEFITool-20170506/ffsengine.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/ffsengine.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -287,15 +287,9 @@ 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; @@ -854,7 +848,7 @@ } else if (volumeHeader->Revision == 2) { // Acquire alignment - alignment = (UINT32)pow(2.0, (int)(volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + alignment = (UINT32)(1UL << ((volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16)); // Check alignment if (volumeOffset % alignment) @@ -877,9 +871,8 @@ if (msgUnknownRevision) msg(tr("parseBios: unknown volume revision %1").arg(volumeHeader->Revision), index); if (msgSizeMismach) - msg(tr("parseBios: volume size stored in header %1h (%2) differs from calculated using block map %3h (%4)") - .hexarg(volumeSize).arg(volumeSize) - .hexarg(bmVolumeSize).arg(bmVolumeSize), + msg(tr("parseBios: volume size stored in header %1h differs from calculated using block map %2h") + .hexarg(volumeSize).arg(bmVolumeSize), index); // Go to next volume @@ -983,15 +976,16 @@ 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 +1057,10 @@ // 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 +1094,42 @@ 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; + } + + 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 EFI_FFS_FILE_HEADER - if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { + // 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 +1169,11 @@ // 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 +1185,7 @@ // 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 +1203,7 @@ 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 +1211,32 @@ 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) - 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; + 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)); } - // 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; + + // 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; // 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 +1247,51 @@ 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()); + } + // Data checksum must be one of predefined values + else if (revision == 1) { + calculatedData = FFS_FIXED_CHECKSUM; + } + else { + calculatedData = FFS_FIXED_CHECKSUM2; + } + + if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) + 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 +1313,27 @@ 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 +1387,18 @@ { 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; + + if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER2)) + return ERR_INVALID_FILE; + + const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset); + sectionSize = sectionHeader2->ExtendedSize; return ERR_SUCCESS; } @@ -1536,7 +1551,7 @@ QString info; QByteArray header; QByteArray body; - UINT32 headerSize; + UINT32 headerSize = sizeOfSectionHeader(sectionHeader); UINT8 result; switch (sectionHeader->Type) { @@ -1547,8 +1562,8 @@ QByteArray decompressed; UINT8 algorithm; const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)sectionHeader; - header = section.left(sizeof(EFI_COMPRESSION_SECTION)); - body = section.mid(sizeof(EFI_COMPRESSION_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); algorithm = COMPRESSION_ALGORITHM_UNKNOWN; // Decompress section result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); @@ -1588,12 +1603,10 @@ bool msgUnknownSignature = false; bool msgUnknownUefiGuidSignature = false; - const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader; - header = section.left(sizeof(EFI_GUID_DEFINED_SECTION)); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - header = section.left(guidDefinedSectionHeader->DataOffset); - guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); - body = section.mid(guidDefinedSectionHeader->DataOffset); + header = section.left(headerSize); + body = section.mid(headerSize); + + const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); QByteArray processed = body; // Get info @@ -1670,7 +1683,7 @@ } } 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 { @@ -1748,8 +1761,8 @@ case EFI_SECTION_DISPOSABLE: { - header = section.left(sizeof(EFI_DISPOSABLE_SECTION)); - body = section.mid(sizeof(EFI_DISPOSABLE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -1772,7 +1785,6 @@ case EFI_SECTION_PEI_DEPEX: case EFI_SECTION_SMM_DEPEX: { bool msgDepexParseFailed = false; - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1800,7 +1812,6 @@ } break; case EFI_SECTION_TE: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1814,7 +1825,11 @@ // Get TE info bool msgInvalidSignature = false; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData(); - UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); + + // Most EFI images today include teFixup in ImageBase value, + // which doesn't follow the UEFI spec, but is so popular that + // only a few images out of thousands are different + UINT32 teFixup = 0; //teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { info += tr("\nSignature: %1h, invalid").hexarg2(teHeader->Signature, 4); msgInvalidSignature = true; @@ -1851,7 +1866,6 @@ case EFI_SECTION_PE32: case EFI_SECTION_PIC: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1947,7 +1961,6 @@ } break; case EFI_SECTION_COMPATIBILITY16: { - headerSize = sizeOfSectionHeader(sectionHeader); header = section.left(headerSize); body = section.mid(headerSize); @@ -1963,8 +1976,8 @@ } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { - header = section.left(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); - body = section.mid(sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)sectionHeader; // Get info @@ -1983,8 +1996,8 @@ } break; case EFI_SECTION_VERSION: { - header = section.left(sizeof(EFI_VERSION_SECTION)); - body = section.mid(sizeof(EFI_VERSION_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)sectionHeader; @@ -2002,8 +2015,8 @@ } break; case EFI_SECTION_USER_INTERFACE: { - header = section.left(sizeof(EFI_USER_INTERFACE_SECTION)); - body = section.mid(sizeof(EFI_USER_INTERFACE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); QString text = QString::fromUtf16((const ushort*)body.constData()); // Get info @@ -2022,8 +2035,8 @@ } break; case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: { - header = section.left(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); - body = section.mid(sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -2045,8 +2058,8 @@ case EFI_SECTION_RAW: { bool parsed = false; - header = section.left(sizeof(EFI_RAW_SECTION)); - body = section.mid(sizeof(EFI_RAW_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") @@ -2100,8 +2113,8 @@ case SCT_SECTION_POSTCODE: case INSYDE_SECTION_POSTCODE: { - header = section.left(sizeof(POSTCODE_SECTION)); - body = section.mid(sizeof(POSTCODE_SECTION)); + header = section.left(headerSize); + body = section.mid(headerSize); const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)sectionHeader; @@ -2118,8 +2131,8 @@ } break; default: - header = section.left(sizeof(EFI_COMMON_SECTION_HEADER)); - body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER)); + header = section.left(headerSize); + body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) @@ -2219,28 +2232,51 @@ 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 > 1 && (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 +2290,17 @@ 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; @@ -2284,12 +2314,17 @@ if (model->type(parent) != Types::File && model->type(parent) != Types::Section) return ERR_INVALID_SECTION; - if (header.size() < (int) sizeof(EFI_COMMON_SECTION_HEADER)) + if ((UINT32)header.size() < sizeof(EFI_COMMON_SECTION_HEADER)) return ERR_INVALID_SECTION; QByteArray newHeader = header; EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)newHeader.data(); + if (uint24ToUint32(commonHeader->Size) == EFI_SECTION2_IS_USED) { + msg(tr("create: creation of large sections not supported yet"), index); + return ERR_NOT_IMPLEMENTED; + } + switch (commonHeader->Type) { case EFI_SECTION_COMPRESSION: { @@ -2428,17 +2463,20 @@ } 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); } @@ -2518,9 +2556,15 @@ extracted.append(model->header(index)); extracted.append(model->body(index)); if (model->type(index) == Types::File) { - //!TODO: add volume revision check, maybe? + UINT8 revision = 2; + QModelIndex parent = model->parent(index); + if (parent.isValid() && model->type(parent) == Types::Volume) { + const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(parent).constData(); + revision = volumeHeader->Revision; + } + const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); - 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; extracted.append(ht).append(ft); @@ -2873,6 +2917,9 @@ 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 +3312,7 @@ 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 +3378,9 @@ 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) { @@ -3354,11 +3405,9 @@ // Normal file // Ensure correct alignment - 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); + UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + alignment = (UINT32)(1UL <<alignmentPower); + UINT32 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 @@ -3496,18 +3545,21 @@ volumeSize = newSize; } } - - // Check new volume size - if ((UINT32)(header.size() + reconstructed.size()) > volumeSize) - { - msg(tr("reconstructVolume: volume grow failed"), index); - return ERR_INVALID_VOLUME; - } } // Use current volume body - else + else { reconstructed = model->body(index); + // BUGBUG: volume size may change during this operation for volumes withour files in them + // but such volumes are fairly rare + } + + // Check new volume size + if ((UINT32)(header.size() + reconstructed.size()) != volumeSize) { + msg(tr("reconstructVolume: volume size can't be changed"), index); + return ERR_INVALID_VOLUME; + } + // Reconstruction successful reconstructed = header.append(reconstructed); @@ -3527,6 +3579,7 @@ volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } + // Store new free space offset, if needed if (model->text(index).contains("AppleFSO ")) { // Get current CRC32 value from volume header @@ -3577,7 +3630,7 @@ 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); @@ -3585,9 +3638,9 @@ } // Check file state - // Invert it first if erase polarity is true + // Check top reserved bit of file state to determine it's original erase polarity UINT8 state = fileHeader->State; - if (erasePolarity == ERASE_POLARITY_TRUE) + if (state & EFI_FILE_ERASE_POLARITY) state = ~state; // Order of this checks must be preserved @@ -3635,6 +3688,10 @@ // 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,8 +3703,22 @@ } // Calculate section base - UINT32 sectionBase = base ? base + sizeof(EFI_FFS_FILE_HEADER) + offset : 0; - + UINT32 sectionBase = base ? base + headerSize + offset : 0; + UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + UINT32 fileAlignment = (UINT32)(1UL << alignmentPower); + UINT32 alignmentBase = base + headerSize; + if (alignmentBase % fileAlignment) { + // File will be unaligned if added as is, so we must add pad file before it + // Determine pad file size + UINT32 size = fileAlignment - (alignmentBase % fileAlignment); + // Required padding is smaller then minimal pad file size + while (size < sizeof(EFI_FFS_FILE_HEADER)) { + size += fileAlignment; + } + // Adjust file base to incorporate pad file that will be added to align it + sectionBase += size; + } + // Reconstruct section QByteArray section; result = reconstructSection(index.child(i, 0), sectionBase, section); @@ -3665,21 +3736,31 @@ offset += section.size(); } } - - // 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); - - // 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); } // Use current file body else reconstructed = model->body(index); + // Correct file size + UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; + 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 = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State); + // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)reconstructed.constData(), reconstructed.size()); @@ -3690,11 +3771,12 @@ 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); } + // Set file state state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; if (erasePolarity == ERASE_POLARITY_TRUE) @@ -3732,6 +3814,10 @@ 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)) { @@ -3830,28 +3916,44 @@ .arg(model->subtype(index)), index); return ERR_INVALID_SECTION; } - - // Correct section size - uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); } // Leaf section - else + else { reconstructed = model->body(index); + } + + // Correct section 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); + } // Rebase PE32 or TE image, if needed if ((model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE) && (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE || - model->subtype(index.parent()) == EFI_FV_FILETYPE_PEIM || - model->subtype(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { + model->subtype(index.parent()) == EFI_FV_FILETYPE_PEIM || + model->subtype(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { UINT16 teFixup = 0; - //TODO: add proper handling - /*if (model->subtype(index) == EFI_SECTION_TE) { - const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); - teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); - }*/ + + // Most EFI images today include teFixup in ImageBase value, + // which doesn't follow the UEFI spec, but is so popular that + // only a few images out of thousands are different + + // There are some heuristics possible here to detect if an entry point is calculated correctly + // or needs a proper fixup, but new_engine already have them and it's better to work on proper + // builder for it than trying to fix this mess + + //if (model->subtype(index) == EFI_SECTION_TE) { + // const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); + // teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); + // if (base) { - result = rebase(reconstructed, base - teFixup + header.size()); + result = rebase(reconstructed, base - teFixup + header.size(), index); if (result) { msg(tr("reconstructSection: executable section rebase failed"), index); return result; @@ -4147,7 +4249,7 @@ return ERR_SUCCESS; } -UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base) +UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base, const QModelIndex & index) { UINT32 delta; // Difference between old and new base addresses UINT32 relocOffset; // Offset of relocation region @@ -4220,6 +4322,9 @@ relocSize = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].Size; // Set new base teHeader->ImageBase = base; + + // Warn the user about possible outcome of incorrect rebase of TE image + msg(tr("rebase: can't determine if TE image base is adjusted or not, rebased TE image may stop working"), index); } else return ERR_UNKNOWN_IMAGE_TYPE; @@ -4249,6 +4354,12 @@ // Run this relocation record while (Reloc < RelocEnd) { + if (*Reloc == 0x0000) { + // Skip last emtpy reloc entry + Reloc += 1; + continue; + } + UINT32 RelocLocation = RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF); if ((UINT32)file.size() < RelocLocation) return ERR_BAD_RELOCATION_ENTRY; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/ffsengine.h new/UEFITool-20180225/ffsengine.h --- old/UEFITool-20170506/ffsengine.h 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/ffsengine.h 2018-02-26 00:40:14.000000000 +0100 @@ -70,7 +70,7 @@ UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); - UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); + UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision = 2, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex()); UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); @@ -84,7 +84,6 @@ UINT8 reconstructIntelImage(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed, bool includeHeader = true); UINT8 reconstructPadding(const QModelIndex& index, QByteArray & reconstructed); - UINT8 reconstructBios(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructVolume(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed); UINT8 reconstructSection(const QModelIndex& index, const UINT32 base, QByteArray & reconstructed); @@ -117,7 +116,6 @@ UINT8 parseDepexSection(const QByteArray & body, QString & parsed); UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); - UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); // Reconstruction helpers @@ -127,7 +125,7 @@ // Rebase routines UINT8 getBase(const QByteArray& file, UINT32& base); UINT8 getEntryPoint(const QByteArray& file, UINT32 &entryPoint); - UINT8 rebase(QByteArray & executable, const UINT32 base); + UINT8 rebase(QByteArray & executable, const UINT32 base, const QModelIndex & index); void rebasePeiFiles(const QModelIndex & index); // Patch routines diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/types.cpp new/UEFITool-20180225/types.cpp --- old/UEFITool-20170506/types.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/types.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -69,7 +69,7 @@ case Types::Image: if (subtype == Subtypes::IntelImage) return QObject::tr("Intel"); - else if (Subtypes::UefiImage) + else if (subtype == Subtypes::UefiImage) return QObject::tr("UEFI"); else return QObject::tr("Unknown subtype"); @@ -153,4 +153,4 @@ default: return QObject::tr("Unknown"); } -} \ No newline at end of file +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/UEFITool-20170506/uefitool.cpp new/UEFITool-20180225/uefitool.cpp --- old/UEFITool-20170506/uefitool.cpp 2017-05-07 02:24:47.000000000 +0200 +++ new/UEFITool-20180225/uefitool.cpp 2018-02-26 00:40:14.000000000 +0100 @@ -17,7 +17,7 @@ UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), -version(tr("0.21.5")) +version(tr("0.22.4")) { clipboard = QApplication::clipboard(); ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.hOQ5u9/_old 2018-03-09 10:46:28.474940565 +0100 +++ /var/tmp/diff_new_pack.hOQ5u9/_new 2018-03-09 10:46:28.474940565 +0100 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/LongSoft/UEFITool.git</param> - <param name="changesrevision">5ba63418bde66681ae81b8f1ae890ab65d746752</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">60d6494841fd5258a222342f53b1f5554fbbaa37</param></service></servicedata> \ No newline at end of file
