From 4a61fcd9d9ef69fcd9ca5c7b343a980840deba74 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 7 Nov 2013 14:46:28 +0100 Subject: [PATCH] Version 0.4.0 - Code is highly refactored - Editing is (partially) working now --- LZMA/LzmaDecompress.h | 4 +- Tiano/EfiTianoDecompress.c | 988 ++++++++-------- Tiano/EfiTianoDecompress.h | 160 +-- basetypes.h | 58 +- descriptor.cpp | 19 + descriptor.h | 13 +- ffs.h | 29 +- ffsengine.cpp | 2233 +++++++++++++++++++++++------------- ffsengine.h | 70 +- treeitem.cpp | 167 ++- treeitem.h | 90 +- treeitemtypes.h | 80 -- treemodel.cpp | 72 +- treemodel.h | 16 +- uefitool.cpp | 175 ++- uefitool.h | 16 +- uefitool.pro | 9 +- uefitool.ui | 106 +- 18 files changed, 2629 insertions(+), 1676 deletions(-) delete mode 100644 treeitemtypes.h diff --git a/LZMA/LzmaDecompress.h b/LZMA/LzmaDecompress.h index 071326f..9ed377c 100644 --- a/LZMA/LzmaDecompress.h +++ b/LZMA/LzmaDecompress.h @@ -17,12 +17,12 @@ #include "../basetypes.h" #include "SDK/C/LzmaDec.h" -#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) - #ifdef __cplusplus extern "C" { #endif +#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) + UINT64 EFIAPI LShiftU64 ( diff --git a/Tiano/EfiTianoDecompress.c b/Tiano/EfiTianoDecompress.c index 8a6a6fd..4c4652f 100644 --- a/Tiano/EfiTianoDecompress.c +++ b/Tiano/EfiTianoDecompress.c @@ -5,18 +5,18 @@ 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, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. Module Name: - Decompress.c +Decompress.c Abstract: - Decompressor. Algorithm Ported from OPSD code (Decomp.asm) - +Decompressor. Algorithm Ported from OPSD code (Decomp.asm) + --*/ #include "EfiTianoDecompress.h" @@ -47,369 +47,369 @@ Abstract: #endif typedef struct { - UINT8 *mSrcBase; // Starting address of compressed data - UINT8 *mDstBase; // Starting address of decompressed data - UINT32 mOutBuf; - UINT32 mInBuf; +UINT8 *mSrcBase; // Starting address of compressed data +UINT8 *mDstBase; // Starting address of decompressed data +UINT32 mOutBuf; +UINT32 mInBuf; - UINT16 mBitCount; - UINT32 mBitBuf; - UINT32 mSubBitBuf; - UINT16 mBlockSize; - UINT32 mCompSize; - UINT32 mOrigSize; +UINT16 mBitCount; +UINT32 mBitBuf; +UINT32 mSubBitBuf; +UINT16 mBlockSize; +UINT32 mCompSize; +UINT32 mOrigSize; - UINT16 mBadTableFlag; +UINT16 mBadTableFlag; - UINT16 mLeft[2 * NC - 1]; - UINT16 mRight[2 * NC - 1]; - UINT8 mCLen[NC]; - UINT8 mPTLen[NPT]; - UINT16 mCTable[4096]; - UINT16 mPTTable[256]; +UINT16 mLeft[2 * NC - 1]; +UINT16 mRight[2 * NC - 1]; +UINT8 mCLen[NC]; +UINT8 mPTLen[NPT]; +UINT16 mCTable[4096]; +UINT16 mPTTable[256]; - // - // The length of the field 'Position Set Code Length Array Size'Block Header. - // For EFI 1.1 de/compression algorithm, mPBit = 4 - // For Tiano de/compression algorithm, mPBit = 5 - // - UINT8 mPBit; +// +// The length of the field 'Position Set Code Length Array Size'Block Header. +// For EFI 1.1 de/compression algorithm, mPBit = 4 +// For Tiano de/compression algorithm, mPBit = 5 +// +UINT8 mPBit; } SCRATCH_DATA; STATIC VOID FillBuf ( - SCRATCH_DATA *Sd, - UINT16 NumOfBits - ) +SCRATCH_DATA *Sd, +UINT16 NumOfBits +) /*++ Routine Description: - Shift mBitBuf NumOfBits left. ReadNumOfBits of bits from source. +Shift mBitBuf NumOfBits left. ReadNumOfBits of bits from source. Arguments: - Sd - The global scratch data - NumOfBits - The number of bits to shift and read. +Sd - The global scratch data +NumOfBits - The number of bits to shift and read. Returns: (VOID) --*/ { - Sd->mBitBuf = (UINT32) (Sd->mBitBuf << NumOfBits); +Sd->mBitBuf = (UINT32) (Sd->mBitBuf << NumOfBits); - while (NumOfBits > Sd->mBitCount) { +while (NumOfBits > Sd->mBitCount) { Sd->mBitBuf |= (UINT32) (Sd->mSubBitBuf << (NumOfBits = (UINT16) (NumOfBits - Sd->mBitCount))); if (Sd->mCompSize > 0) { - // - // Get 1 byte into SubBitBuf - // - Sd->mCompSize--; - Sd->mSubBitBuf = 0; - Sd->mSubBitBuf = Sd->mSrcBase[Sd->mInBuf++]; - Sd->mBitCount = 8; + // + // Get 1 byte into SubBitBuf + // + Sd->mCompSize--; + Sd->mSubBitBuf = 0; + Sd->mSubBitBuf = Sd->mSrcBase[Sd->mInBuf++]; + Sd->mBitCount = 8; } else { - // - // No more bits from the source, just pad zero bit. - // - Sd->mSubBitBuf = 0; - Sd->mBitCount = 8; + // + // No more bits from the source, just pad zero bit. + // + Sd->mSubBitBuf = 0; + Sd->mBitCount = 8; } - } +} - Sd->mBitCount = (UINT16) (Sd->mBitCount - NumOfBits); - Sd->mBitBuf |= Sd->mSubBitBuf >> Sd->mBitCount; +Sd->mBitCount = (UINT16) (Sd->mBitCount - NumOfBits); +Sd->mBitBuf |= Sd->mSubBitBuf >> Sd->mBitCount; } STATIC UINT32 GetBits ( - SCRATCH_DATA *Sd, - UINT16 NumOfBits - ) +SCRATCH_DATA *Sd, +UINT16 NumOfBits +) /*++ Routine Description: - Get NumOfBits of bits from mBitBuf. Fill mBitBuf with subsequent - NumOfBits of bits from source. Returns NumOfBits of bits that are - popped. +Get NumOfBits of bits from mBitBuf. Fill mBitBuf with subsequent +NumOfBits of bits from source. Returns NumOfBits of bits that are +popped. Arguments: - Sd - The global scratch data. - NumOfBits - The number of bits to pop and read. +Sd - The global scratch data. +NumOfBits - The number of bits to pop and read. Returns: - The bits that are popped. +The bits that are popped. --*/ { - UINT32 Bits; +UINT32 Bits; - Bits = (UINT32) (Sd->mBitBuf >> (BITBUFSIZ - NumOfBits)); +Bits = (UINT32) (Sd->mBitBuf >> (BITBUFSIZ - NumOfBits)); - FillBuf (Sd, NumOfBits); +FillBuf (Sd, NumOfBits); - return Bits; +return Bits; } STATIC UINT16 MakeTable ( - SCRATCH_DATA *Sd, - UINT16 NumOfChar, - UINT8 *BitLen, - UINT16 TableBits, - UINT16 *Table - ) +SCRATCH_DATA *Sd, +UINT16 NumOfChar, +UINT8 *BitLen, +UINT16 TableBits, +UINT16 *Table +) /*++ Routine Description: - Creates Huffman Code mapping table according to code length array. +Creates Huffman Code mapping table according to code length array. Arguments: - Sd - The global scratch data - NumOfChar - Number of symbolsthe symbol set - BitLen - Code length array - TableBits - The width of the mapping table - Table - The table - +Sd - The global scratch data +NumOfChar - Number of symbolsthe symbol set +BitLen - Code length array +TableBits - The width of the mapping table +Table - The table + Returns: - - 0 - OK. - BAD_TABLE - The table is corrupted. + +0 - OK. +BAD_TABLE - The table is corrupted. --*/ { - UINT16 Count[17]; - UINT16 Weight[17]; - UINT16 Start[18]; - UINT16 *Pointer; - UINT16 Index3; - UINT16 Index; - UINT16 Len; - UINT16 Char; - UINT16 JuBits; - UINT16 Avail; - UINT16 NextCode; - UINT16 Mask; +UINT16 Count[17]; +UINT16 Weight[17]; +UINT16 Start[18]; +UINT16 *Pointer; +UINT16 Index3; +UINT16 Index; +UINT16 Len; +UINT16 Char; +UINT16 JuBits; +UINT16 Avail; +UINT16 NextCode; +UINT16 Mask; - for (Index = 1; Index <= 16; Index++) { +for (Index = 1; Index <= 16; Index++) { Count[Index] = 0; - } +} - for (Index = 0; Index < NumOfChar; Index++) { +for (Index = 0; Index < NumOfChar; Index++) { Count[BitLen[Index]]++; - } +} - Start[1] = 0; +Start[1] = 0; - for (Index = 1; Index <= 16; Index++) { +for (Index = 1; Index <= 16; Index++) { Start[Index + 1] = (UINT16) (Start[Index] + (Count[Index] << (16 - Index))); - } +} - if (Start[17] != 0) { +if (Start[17] != 0) { /*(1U << 16)*/ return (UINT16) BAD_TABLE; - } +} - JuBits = (UINT16) (16 - TableBits); +JuBits = (UINT16) (16 - TableBits); - for (Index = 1; Index <= TableBits; Index++) { +for (Index = 1; Index <= TableBits; Index++) { Start[Index] >>= JuBits; Weight[Index] = (UINT16) (1U << (TableBits - Index)); - } +} - while (Index <= 16) { +while (Index <= 16) { Weight[Index] = (UINT16) (1U << (16 - Index)); Index++; - } +} - Index = (UINT16) (Start[TableBits + 1] >> JuBits); +Index = (UINT16) (Start[TableBits + 1] >> JuBits); - if (Index != 0) { +if (Index != 0) { Index3 = (UINT16) (1U << TableBits); while (Index != Index3) { - Table[Index++] = 0; + Table[Index++] = 0; } - } +} - Avail = NumOfChar; - Mask = (UINT16) (1U << (15 - TableBits)); +Avail = NumOfChar; +Mask = (UINT16) (1U << (15 - TableBits)); - for (Char = 0; Char < NumOfChar; Char++) { +for (Char = 0; Char < NumOfChar; Char++) { Len = BitLen[Char]; if (Len == 0) { - continue; + continue; } NextCode = (UINT16) (Start[Len] + Weight[Len]); if (Len <= TableBits) { - for (Index = Start[Len]; Index < NextCode; Index++) { - Table[Index] = Char; - } + for (Index = Start[Len]; Index < NextCode; Index++) { + Table[Index] = Char; + } } else { - Index3 = Start[Len]; - Pointer = &Table[Index3 >> JuBits]; - Index = (UINT16) (Len - TableBits); + Index3 = Start[Len]; + Pointer = &Table[Index3 >> JuBits]; + Index = (UINT16) (Len - TableBits); - while (Index != 0) { - if (*Pointer == 0) { - Sd->mRight[Avail] = Sd->mLeft[Avail] = 0; - *Pointer = Avail++; + while (Index != 0) { + if (*Pointer == 0) { + Sd->mRight[Avail] = Sd->mLeft[Avail] = 0; + *Pointer = Avail++; + } + + if (Index3 & Mask) { + Pointer = &Sd->mRight[*Pointer]; + } else { + Pointer = &Sd->mLeft[*Pointer]; + } + + Index3 <<= 1; + Index--; } - if (Index3 & Mask) { - Pointer = &Sd->mRight[*Pointer]; - } else { - Pointer = &Sd->mLeft[*Pointer]; - } - - Index3 <<= 1; - Index--; - } - - *Pointer = Char; + *Pointer = Char; } Start[Len] = NextCode; - } - // - // Succeeds - // - return 0; +} +// +// Succeeds +// +return 0; } STATIC UINT32 DecodeP ( - SCRATCH_DATA *Sd - ) +SCRATCH_DATA *Sd +) /*++ Routine Description: - Decodes a position value. +Decodes a position value. Arguments: - Sd - the global scratch data +Sd - the global scratch data Returns: - The position value decoded. +The position value decoded. --*/ { - UINT16 Val; - UINT32 Mask; - UINT32 Pos; +UINT16 Val; +UINT32 Mask; +UINT32 Pos; - Val = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; +Val = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; - if (Val >= MAXNP) { +if (Val >= MAXNP) { Mask = 1U << (BITBUFSIZ - 1 - 8); do { - if (Sd->mBitBuf & Mask) { - Val = Sd->mRight[Val]; - } else { - Val = Sd->mLeft[Val]; - } + if (Sd->mBitBuf & Mask) { + Val = Sd->mRight[Val]; + } else { + Val = Sd->mLeft[Val]; + } - Mask >>= 1; + Mask >>= 1; } while (Val >= MAXNP); - } - // - // Advance what we have read - // - FillBuf (Sd, Sd->mPTLen[Val]); +} +// +// Advance what we have read +// +FillBuf (Sd, Sd->mPTLen[Val]); - Pos = Val; - if (Val > 1) { +Pos = Val; +if (Val > 1) { Pos = (UINT32) ((1U << (Val - 1)) + GetBits (Sd, (UINT16) (Val - 1))); - } +} - return Pos; +return Pos; } STATIC UINT16 ReadPTLen ( - SCRATCH_DATA *Sd, - UINT16 nn, - UINT16 nbit, - UINT16 Special - ) +SCRATCH_DATA *Sd, +UINT16 nn, +UINT16 nbit, +UINT16 Special +) /*++ Routine Description: - Reads code lengths for the Extra Set or the Position Set +Reads code lengths for the Extra Set or the Position Set Arguments: - Sd - The global scratch data - nn - Number of symbols - nbit - Number of bits needed to represent nn - Special - The special symbol that needs to be taken care of +Sd - The global scratch data +nn - Number of symbols +nbit - Number of bits needed to represent nn +Special - The special symbol that needs to be taken care of Returns: - 0 - OK. - BAD_TABLE - Table is corrupted. +0 - OK. +BAD_TABLE - Table is corrupted. --*/ { - UINT16 Number; - UINT16 CharC; - UINT16 Index; - UINT32 Mask; +UINT16 Number; +UINT16 CharC; +UINT16 Index; +UINT32 Mask; - Number = (UINT16) GetBits (Sd, nbit); +Number = (UINT16) GetBits (Sd, nbit); - if (Number == 0) { +if (Number == 0) { CharC = (UINT16) GetBits (Sd, nbit); for (Index = 0; Index < 256; Index++) { - Sd->mPTTable[Index] = CharC; + Sd->mPTTable[Index] = CharC; } for (Index = 0; Index < nn; Index++) { - Sd->mPTLen[Index] = 0; + Sd->mPTLen[Index] = 0; } return 0; - } +} - Index = 0; +Index = 0; - while (Index < Number) { +while (Index < Number) { CharC = (UINT16) (Sd->mBitBuf >> (BITBUFSIZ - 3)); if (CharC == 7) { - Mask = 1U << (BITBUFSIZ - 1 - 3); - while (Mask & Sd->mBitBuf) { - Mask >>= 1; - CharC += 1; - } + Mask = 1U << (BITBUFSIZ - 1 - 3); + while (Mask & Sd->mBitBuf) { + Mask >>= 1; + CharC += 1; + } } FillBuf (Sd, (UINT16) ((CharC < 7) ? 3 : CharC - 3)); @@ -417,78 +417,78 @@ Returns: Sd->mPTLen[Index++] = (UINT8) CharC; if (Index == Special) { - CharC = (UINT16) GetBits (Sd, 2); - while ((INT16) (--CharC) >= 0) { - Sd->mPTLen[Index++] = 0; - } + CharC = (UINT16) GetBits (Sd, 2); + while ((INT16) (--CharC) >= 0) { + Sd->mPTLen[Index++] = 0; + } } - } +} - while (Index < nn) { +while (Index < nn) { Sd->mPTLen[Index++] = 0; - } +} - return MakeTable (Sd, nn, Sd->mPTLen, 8, Sd->mPTTable); +return MakeTable (Sd, nn, Sd->mPTLen, 8, Sd->mPTTable); } STATIC VOID ReadCLen ( - SCRATCH_DATA *Sd - ) +SCRATCH_DATA *Sd +) /*++ Routine Description: - Reads code lengths for Char&Len Set. +Reads code lengths for Char&Len Set. Arguments: - Sd - the global scratch data +Sd - the global scratch data Returns: (VOID) --*/ { - UINT16 Number; - UINT16 CharC; - UINT16 Index; - UINT32 Mask; +UINT16 Number; +UINT16 CharC; +UINT16 Index; +UINT32 Mask; - Number = (UINT16) GetBits (Sd, CBIT); +Number = (UINT16) GetBits (Sd, CBIT); - if (Number == 0) { +if (Number == 0) { CharC = (UINT16) GetBits (Sd, CBIT); for (Index = 0; Index < NC; Index++) { - Sd->mCLen[Index] = 0; + Sd->mCLen[Index] = 0; } for (Index = 0; Index < 4096; Index++) { - Sd->mCTable[Index] = CharC; + Sd->mCTable[Index] = CharC; } return ; - } +} - Index = 0; - while (Index < Number) { +Index = 0; +while (Index < Number) { CharC = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; if (CharC >= NT) { - Mask = 1U << (BITBUFSIZ - 1 - 8); + Mask = 1U << (BITBUFSIZ - 1 - 8); - do { + do { - if (Mask & Sd->mBitBuf) { - CharC = Sd->mRight[CharC]; - } else { - CharC = Sd->mLeft[CharC]; - } + if (Mask & Sd->mBitBuf) { + CharC = Sd->mRight[CharC]; + } else { + CharC = Sd->mLeft[CharC]; + } - Mask >>= 1; + Mask >>= 1; - } while (CharC >= NT); + } while (CharC >= NT); } // // Advance what we have read @@ -497,499 +497,425 @@ Returns: (VOID) if (CharC <= 2) { - if (CharC == 0) { - CharC = 1; - } else if (CharC == 1) { - CharC = (UINT16) (GetBits (Sd, 4) + 3); - } else if (CharC == 2) { - CharC = (UINT16) (GetBits (Sd, CBIT) + 20); - } + if (CharC == 0) { + CharC = 1; + } else if (CharC == 1) { + CharC = (UINT16) (GetBits (Sd, 4) + 3); + } else if (CharC == 2) { + CharC = (UINT16) (GetBits (Sd, CBIT) + 20); + } - while ((INT16) (--CharC) >= 0) { - Sd->mCLen[Index++] = 0; - } + while ((INT16) (--CharC) >= 0) { + Sd->mCLen[Index++] = 0; + } } else { - Sd->mCLen[Index++] = (UINT8) (CharC - 2); + Sd->mCLen[Index++] = (UINT8) (CharC - 2); } - } +} - while (Index < NC) { +while (Index < NC) { Sd->mCLen[Index++] = 0; - } +} - MakeTable (Sd, NC, Sd->mCLen, 12, Sd->mCTable); +MakeTable (Sd, NC, Sd->mCLen, 12, Sd->mCTable); - return ; +return ; } STATIC UINT16 DecodeC ( - SCRATCH_DATA *Sd - ) +SCRATCH_DATA *Sd +) /*++ Routine Description: - Decode a character/length value. +Decode a character/length value. Arguments: - Sd - The global scratch data. +Sd - The global scratch data. Returns: - The value decoded. +The value decoded. --*/ { - UINT16 Index2; - UINT32 Mask; +UINT16 Index2; +UINT32 Mask; - if (Sd->mBlockSize == 0) { +if (Sd->mBlockSize == 0) { // // Starting a new block // Sd->mBlockSize = (UINT16) GetBits (Sd, 16); Sd->mBadTableFlag = ReadPTLen (Sd, NT, TBIT, 3); if (Sd->mBadTableFlag != 0) { - return 0; + return 0; } ReadCLen (Sd); Sd->mBadTableFlag = ReadPTLen (Sd, MAXNP, Sd->mPBit, (UINT16) (-1)); if (Sd->mBadTableFlag != 0) { - return 0; + return 0; } - } +} - Sd->mBlockSize--; - Index2 = Sd->mCTable[Sd->mBitBuf >> (BITBUFSIZ - 12)]; +Sd->mBlockSize--; +Index2 = Sd->mCTable[Sd->mBitBuf >> (BITBUFSIZ - 12)]; - if (Index2 >= NC) { +if (Index2 >= NC) { Mask = 1U << (BITBUFSIZ - 1 - 12); do { - if (Sd->mBitBuf & Mask) { - Index2 = Sd->mRight[Index2]; - } else { - Index2 = Sd->mLeft[Index2]; - } + if (Sd->mBitBuf & Mask) { + Index2 = Sd->mRight[Index2]; + } else { + Index2 = Sd->mLeft[Index2]; + } - Mask >>= 1; + Mask >>= 1; } while (Index2 >= NC); - } - // - // Advance what we have read - // - FillBuf (Sd, Sd->mCLen[Index2]); +} +// +// Advance what we have read +// +FillBuf (Sd, Sd->mCLen[Index2]); - return Index2; +return Index2; } STATIC VOID Decode ( - SCRATCH_DATA *Sd - ) +SCRATCH_DATA *Sd +) /*++ Routine Description: - Decode the source data and put the resulting data into the destination buffer. +Decode the source data and put the resulting data into the destination buffer. Arguments: - Sd - The global scratch data +Sd - The global scratch data Returns: (VOID) - --*/ +--*/ { - UINT16 BytesRemain; - UINT32 DataIdx; - UINT16 CharC; +UINT16 BytesRemain; +UINT32 DataIdx; +UINT16 CharC; - BytesRemain = (UINT16) (-1); +BytesRemain = (UINT16) (-1); - DataIdx = 0; +DataIdx = 0; - for (;;) { +for (;;) { CharC = DecodeC (Sd); if (Sd->mBadTableFlag != 0) { - return ; + return ; } if (CharC < 256) { - // - // Process an Original character - // - if (Sd->mOutBuf >= Sd->mOrigSize) { - return ; - } else { - Sd->mDstBase[Sd->mOutBuf++] = (UINT8) CharC; - } - - } else { - // - // Process a Pointer - // - CharC = (UINT16) (CharC - (UINT8_MAX + 1 - THRESHOLD)); - - BytesRemain = CharC; - - DataIdx = Sd->mOutBuf - DecodeP (Sd) - 1; - - BytesRemain--; - while ((INT16) (BytesRemain) >= 0) { - Sd->mDstBase[Sd->mOutBuf++] = Sd->mDstBase[DataIdx++]; + // + // Process an Original character + // if (Sd->mOutBuf >= Sd->mOrigSize) { - return ; + return ; + } else { + Sd->mDstBase[Sd->mOutBuf++] = (UINT8) CharC; } - BytesRemain--; - } - } - } + } else { + // + // Process a Pointer + // + CharC = (UINT16) (CharC - (UINT8_MAX + 1 - THRESHOLD)); - return ; + BytesRemain = CharC; + + DataIdx = Sd->mOutBuf - DecodeP (Sd) - 1; + + BytesRemain--; + while ((INT16) (BytesRemain) >= 0) { + Sd->mDstBase[Sd->mOutBuf++] = Sd->mDstBase[DataIdx++]; + if (Sd->mOutBuf >= Sd->mOrigSize) { + return ; + } + + BytesRemain--; + } + } +} + +return ; } UINT32 -GetInfo ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ) +EfiTianoGetInfo ( +VOID *Source, +UINT32 SrcSize, +UINT32 *DstSize, +UINT32 *ScratchSize +) /*++ Routine Description: - The internal implementation of *_DECOMPRESS_PROTOCOL.GetInfo(). +The internal implementation of *_DECOMPRESS_PROTOCOL.GetInfo(). Arguments: - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - DstSize - The size of destination buffer. - ScratchSize - The size of scratch buffer. +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +DstSize - The size of destination buffer. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. +EFI_INVALID_PARAMETER - The source data is corrupted --*/ { - UINT8 *Src; +UINT8 *Src; - *ScratchSize = sizeof (SCRATCH_DATA); +*ScratchSize = sizeof (SCRATCH_DATA); - Src = Source; - if (SrcSize < 8) { +Src = Source; +if (SrcSize < 8) { return ERR_INVALID_PARAMETER; - } +} - *DstSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); - return ERR_SUCCESS; +*DstSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); +return ERR_SUCCESS; } UINT32 Decompress ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize, - UINT8 Version - ) +VOID *Source, +UINT32 SrcSize, +VOID *Destination, +UINT32 DstSize, +VOID *Scratch, +UINT32 ScratchSize, +UINT8 Version +) /*++ Routine Description: - The internal implementation of *_DECOMPRESS_PROTOCOL.Decompress(). +The internal implementation of *_DECOMPRESS_PROTOCOL.Decompress(). Arguments: - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - Destination - The destination buffer to store the decompressed data - DstSize - The size of destination buffer. - Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. - ScratchSize - The size of scratch buffer. - Version - The version of de/compression algorithm. - Version 1 for EFI 1.1 de/compression algorithm. - Version 2 for Tiano de/compression algorithm. +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +Destination - The destination buffer to store the decompressed data +DstSize - The size of destination buffer. +Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. +ScratchSize - The size of scratch buffer. +Version - The version of de/compression algorithm. +Version 1 for EFI 1.1 de/compression algorithm. +Version 2 for Tiano de/compression algorithm. Returns: - EFI_SUCCESS - Decompression is successfull - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - Decompression is successfull +EFI_INVALID_PARAMETER - The source data is corrupted --*/ { - UINT32 Index; - UINT32 CompSize; - UINT32 OrigSize; - UINT32 Status; - SCRATCH_DATA *Sd; - UINT8 *Src; - UINT8 *Dst; +UINT32 Index; +UINT32 CompSize; +UINT32 OrigSize; +UINT32 Status; +SCRATCH_DATA *Sd; +UINT8 *Src; +UINT8 *Dst; - Status = ERR_SUCCESS; - Src = Source; - Dst = Destination; +Status = ERR_SUCCESS; +Src = Source; +Dst = Destination; - if (ScratchSize < sizeof (SCRATCH_DATA)) { +if (ScratchSize < sizeof (SCRATCH_DATA)) { return ERR_INVALID_PARAMETER; - } +} - Sd = (SCRATCH_DATA *) Scratch; +Sd = (SCRATCH_DATA *) Scratch; - if (SrcSize < 8) { +if (SrcSize < 8) { return ERR_INVALID_PARAMETER; - } +} - CompSize = Src[0] + (Src[1] << 8) + (Src[2] << 16) + (Src[3] << 24); - OrigSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); +CompSize = Src[0] + (Src[1] << 8) + (Src[2] << 16) + (Src[3] << 24); +OrigSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); - // - // If compressed file size is 0, return - // - if (OrigSize == 0) { +// +// If compressed file size is 0, return +// +if (OrigSize == 0) { return Status; - } +} - if (SrcSize < CompSize + 8) { +if (SrcSize < CompSize + 8) { return ERR_INVALID_PARAMETER; - } +} - if (DstSize != OrigSize) { +if (DstSize != OrigSize) { return ERR_INVALID_PARAMETER; - } +} - Src = Src + 8; +Src = Src + 8; - for (Index = 0; Index < sizeof (SCRATCH_DATA); Index++) { +for (Index = 0; Index < sizeof (SCRATCH_DATA); Index++) { ((UINT8 *) Sd)[Index] = 0; - } - // - // The length of the field 'Position Set Code Length Array Size'Block Header. - // For EFI 1.1 de/compression algorithm(Version 1), mPBit = 4 - // For Tiano de/compression algorithm(Version 2), mPBit = 5 - // - switch (Version) { - case 1: +} +// +// The length of the field 'Position Set Code Length Array Size'Block Header. +// For EFI 1.1 de/compression algorithm(Version 1), mPBit = 4 +// For Tiano de/compression algorithm(Version 2), mPBit = 5 +// +switch (Version) { +case 1: Sd->mPBit = 4; break; - case 2: +case 2: Sd->mPBit = 5; break; - default: +default: // // Currently, only have 2 versions // return ERR_INVALID_PARAMETER; - } +} - Sd->mSrcBase = Src; - Sd->mDstBase = Dst; - Sd->mCompSize = CompSize; - Sd->mOrigSize = OrigSize; +Sd->mSrcBase = Src; +Sd->mDstBase = Dst; +Sd->mCompSize = CompSize; +Sd->mOrigSize = OrigSize; - // - // Fill the first BITBUFSIZ bits - // - FillBuf (Sd, BITBUFSIZ); +// +// Fill the first BITBUFSIZ bits +// +FillBuf (Sd, BITBUFSIZ); - // - // Decompress it - // - Decode (Sd); +// +// Decompress it +// +Decode (Sd); - if (Sd->mBadTableFlag != 0) { +if (Sd->mBadTableFlag != 0) { // // Something wrong with the source // Status = ERR_INVALID_PARAMETER; - } - - return Status; } -UINT32 -EFIAPI -EfiGetInfo ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ) -/*++ - -Routine Description: - - The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.GetInfo(). - -Arguments: - - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - DstSize - The size of destination buffer. - ScratchSize - The size of scratch buffer. - -Returns: - - EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. - EFI_INVALID_PARAMETER - The source data is corrupted - ---*/ -{ - return GetInfo ( - Source, - SrcSize, - DstSize, - ScratchSize - ); +return Status; } UINT32 EFIAPI EfiDecompress ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize - ) +VOID *Source, +UINT32 SrcSize, +VOID *Destination, +UINT32 DstSize, +VOID *Scratch, +UINT32 ScratchSize +) /*++ Routine Description: - The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.Decompress(). +The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.Decompress(). Arguments: - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - Destination - The destination buffer to store the decompressed data - DstSize - The size of destination buffer. - Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. - ScratchSize - The size of scratch buffer. +This - The protocol instance pointer +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +Destination - The destination buffer to store the decompressed data +DstSize - The size of destination buffer. +Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - Decompression is successfull - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - Decompression is successfull +EFI_INVALID_PARAMETER - The source data is corrupted --*/ { - // - // For EFI 1.1 de/compression algorithm, the version is 1. - // - return Decompress ( - Source, - SrcSize, - Destination, - DstSize, - Scratch, - ScratchSize, - 1 - ); -} - -UINT32 -EFIAPI -TianoGetInfo ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ) -/*++ - -Routine Description: - - The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.GetInfo(). - -Arguments: - - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - DstSize - The size of destination buffer. - ScratchSize - The size of scratch buffer. - -Returns: - - EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. - EFI_INVALID_PARAMETER - The source data is corrupted - ---*/ -{ - return GetInfo ( - Source, - SrcSize, - DstSize, - ScratchSize - ); +// +// For EFI 1.1 de/compression algorithm, the version is 1. +// +return Decompress ( + Source, + SrcSize, + Destination, + DstSize, + Scratch, + ScratchSize, + 1 + ); } UINT32 EFIAPI TianoDecompress ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize - ) +VOID *Source, +UINT32 SrcSize, +VOID *Destination, +UINT32 DstSize, +VOID *Scratch, +UINT32 ScratchSize +) /*++ Routine Description: - The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). +The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). Arguments: - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - Destination - The destination buffer to store the decompressed data - DstSize - The size of destination buffer. - Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. - ScratchSize - The size of scratch buffer. +This - The protocol instance pointer +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +Destination - The destination buffer to store the decompressed data +DstSize - The size of destination buffer. +Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - Decompression is successfull - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - Decompression is successfull +EFI_INVALID_PARAMETER - The source data is corrupted --*/ { - // - // For Tiano de/compression algorithm, the version is 2. - // - return Decompress ( - Source, - SrcSize, - Destination, - DstSize, - Scratch, - ScratchSize, - 2 - ); +// +// For Tiano de/compression algorithm, the version is 2. +// +return Decompress ( + Source, + SrcSize, + Destination, + DstSize, + Scratch, + ScratchSize, + 2 + ); } diff --git a/Tiano/EfiTianoDecompress.h b/Tiano/EfiTianoDecompress.h index 5d4eac6..aa1896f 100644 --- a/Tiano/EfiTianoDecompress.h +++ b/Tiano/EfiTianoDecompress.h @@ -5,19 +5,19 @@ 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, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. Module Name: - Decompress.h +Decompress.h Abstract: - Header file for decompression routine. - Providing both EFI and Tiano decompress algorithms. - +Header file for decompression routine. +Providing both EFI and Tiano decompress algorithms. + --*/ #ifndef _EFITIANODECOMPRESS_H_ @@ -30,33 +30,37 @@ Abstract: #ifdef __cplusplus extern "C" { #endif +typedef struct { + UINT32 CompSize; + UINT32 OrigSize; +} EFI_TIANO_HEADER; UINT32 EFIAPI -EfiGetInfo ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ) +EfiTianoGetInfo ( +VOID *Source, +UINT32 SrcSize, +UINT32 *DstSize, +UINT32 *ScratchSize +) /*++ Routine Description: - The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.GetInfo(). +The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.GetInfo(). Arguments: - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - DstSize - The size of destination buffer. - ScratchSize - The size of scratch buffer. +This - The protocol instance pointer +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +DstSize - The size of destination buffer. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. +EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; @@ -64,63 +68,33 @@ Returns: UINT32 EFIAPI EfiDecompress ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize - ) +VOID *Source, +UINT32 SrcSize, +VOID *Destination, +UINT32 DstSize, +VOID *Scratch, +UINT32 ScratchSize +) /*++ Routine Description: - The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.Decompress(). +The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.Decompress(). Arguments: - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - Destination - The destination buffer to store the decompressed data - DstSize - The size of destination buffer. - Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. - ScratchSize - The size of scratch buffer. +This - The protocol instance pointer +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +Destination - The destination buffer to store the decompressed data +DstSize - The size of destination buffer. +Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - Decompression is successfull - EFI_INVALID_PARAMETER - The source data is corrupted - ---*/ -; - -UINT32 -EFIAPI -TianoGetInfo ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ) -/*++ - -Routine Description: - - The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.GetInfo(). - -Arguments: - - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - DstSize - The size of destination buffer. - ScratchSize - The size of scratch buffer. - -Returns: - - EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - Decompression is successfull +EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; @@ -128,57 +102,37 @@ Returns: UINT32 EFIAPI TianoDecompress ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize - ) +VOID *Source, +UINT32 SrcSize, +VOID *Destination, +UINT32 DstSize, +VOID *Scratch, +UINT32 ScratchSize +) /*++ Routine Description: - The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). +The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). Arguments: - This - The protocol instance pointer - Source - The source buffer containing the compressed data. - SrcSize - The size of source buffer - Destination - The destination buffer to store the decompressed data - DstSize - The size of destination buffer. - Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. - ScratchSize - The size of scratch buffer. +This - The protocol instance pointer +Source - The source buffer containing the compressed data. +SrcSize - The size of source buffer +Destination - The destination buffer to store the decompressed data +DstSize - The size of destination buffer. +Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. +ScratchSize - The size of scratch buffer. Returns: - EFI_SUCCESS - Decompression is successfull - EFI_INVALID_PARAMETER - The source data is corrupted +EFI_SUCCESS - Decompression is successfull +EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; -typedef -UINT32 -(*GETINFO_FUNCTION) ( - VOID *Source, - UINT32 SrcSize, - UINT32 *DstSize, - UINT32 *ScratchSize - ); - -typedef -UINT32 -(*DECOMPRESS_FUNCTION) ( - VOID *Source, - UINT32 SrcSize, - VOID *Destination, - UINT32 DstSize, - VOID *Scratch, - UINT32 ScratchSize - ); - #ifdef __cplusplus } #endif diff --git a/basetypes.h b/basetypes.h index 027427c..883f2b2 100644 --- a/basetypes.h +++ b/basetypes.h @@ -47,7 +47,7 @@ typedef uint16_t CHAR16; #if _MSC_EXTENSIONS // - // Microsoft* compiler requires _EFIAPI useage, __cdecl is Microsoft* specific C. + // Microsoft* compiler requires _EFIAPI usage, __cdecl is Microsoft* specific C. // #define EFIAPI __cdecl #endif @@ -56,22 +56,48 @@ typedef uint16_t CHAR16; #define EFIAPI __attribute__((cdecl)) #endif -#define ERR_SUCCESS 0 -#define ERR_INVALID_PARAMETER 1 -#define ERR_BUFFER_TOO_SMALL 2 -#define ERR_OUT_OF_RESOURCES 3 -#define ERR_OUT_OF_MEMORY 4 -#define ERR_FILE_OPEN 5 -#define ERR_FILE_READ 6 -#define ERR_FILE_WRITE 7 -#define ERR_INVALID_FLASH_DESCRIPTOR 8 -#define ERR_BIOS_REGION_NOT_FOUND 9 -#define ERR_VOLUMES_NOT_FOUND 10 -#define ERR_INVALID_VOLUME 11 -#define ERR_VOLUME_REVISION_NOT_SUPPORTED 12 -#define ERR_UNKNOWN_FFS 13 -#define ERR_INVALID_FILE 14 +#define ERR_SUCCESS 0 +#define ERR_INVALID_PARAMETER 1 +#define ERR_BUFFER_TOO_SMALL 2 +#define ERR_OUT_OF_RESOURCES 3 +#define ERR_OUT_OF_MEMORY 4 +#define ERR_FILE_OPEN 5 +#define ERR_FILE_READ 6 +#define ERR_FILE_WRITE 7 +#define ERR_ITEM_NOT_FOUND 8 +#define ERR_UNKNOWN_ITEM_TYPE 9 +#define ERR_INVALID_FLASH_DESCRIPTOR 10 +#define ERR_INVALID_REGION 11 +#define ERR_EMPTY_REGION 12 +#define ERR_BIOS_REGION_NOT_FOUND 13 +#define ERR_VOLUMES_NOT_FOUND 14 +#define ERR_INVALID_VOLUME 15 +#define ERR_VOLUME_REVISION_NOT_SUPPORTED 16 +#define ERR_VOLUME_GROW_FAILED 17 +#define ERR_UNKNOWN_FFS 18 +#define ERR_INVALID_FILE 19 +#define ERR_INVALID_SECTION 20 +#define ERR_UNKNOWN_SECTION 21 +#define ERR_STANDARD_COMPRESSION_FAILED 22 +#define ERR_CUSTOMIZED_COMPRESSION_FAILED 23 +#define ERR_STANDARD_DECOMPRESSION_FAILED 24 +#define ERR_CUSTOMIZED_DECOMPRESSION_FAILED 25 +#define ERR_UNKNOWN_COMPRESSION_ALGORITHM 26 +#define ERR_NOT_IMPLEMENTED 0xFF +// Compression algorithms +#define COMPRESSION_ALGORITHM_UNKNOWN 0 +#define COMPRESSION_ALGORITHM_NONE 1 +#define COMPRESSION_ALGORITHM_EFI11 2 +#define COMPRESSION_ALGORITHM_TIANO 3 +#define COMPRESSION_ALGORITHM_LZMA 4 +#define COMPRESSION_ALGORITHM_IMLZMA 5 + +// Item add modes +#define ADD_MODE_APPEND 0 +#define ADD_MODE_PREPEND 1 +#define ADD_MODE_INSERT_BEFORE 2 +#define ADD_MODE_INSERT_AFTER 3 // EFI GUID typedef struct{ diff --git a/descriptor.cpp b/descriptor.cpp index c49b8d2..a85a0fb 100644 --- a/descriptor.cpp +++ b/descriptor.cpp @@ -10,7 +10,26 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ +#include #include "descriptor.h" +#include "treeitem.h" + +QString regionTypeToQString(const UINT8 type) +{ + switch (type) + { + case TreeItem::GbeRegion: + return QObject::tr("GbE"); + case TreeItem::MeRegion: + return QObject::tr("ME"); + case TreeItem::BiosRegion: + return QObject::tr("Bios"); + case TreeItem::PdrRegion: + return QObject::tr("PDR"); + default: + return QObject::tr("Unknown"); + }; +} // Calculate address of data structure addressed by descriptor address format // 8 bit base or limit diff --git a/descriptor.h b/descriptor.h index b23503f..3421468 100644 --- a/descriptor.h +++ b/descriptor.h @@ -13,6 +13,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #ifndef __DESCRIPTOR_H__ #define __DESCRIPTOR_H__ +#include #include "basetypes.h" // Make sure we use right packing rules @@ -42,12 +43,12 @@ typedef struct { UINT8 PchStrapsBase; // 0x10 on most machines UINT8 NumberOfPchStraps; // One-based number of UINT32s to read as PCH Straps, min=0, max=255 (1 Kb) UINT8 ProcStrapsBase; // 0x20 on most machines - UINT8 NumberOfProcStraps; // Number of PROC staps to be read, can be 0 or 1 + UINT8 NumberOfProcStraps; // Number of PROC straps to be read, can be 0 or 1 UINT8 IccTableBase; // 0x21 on most machines UINT8 NumberOfIccTableEntries; // 0x00 on most machines UINT8 DmiTableBase; // 0x25 on most machines UINT8 NumberOfDmiTableEntries; // 0x00 on most machines - UINT16 ReservedZero; // Still unknown, zeroes in all descriptors I have seen + UINT16 ReservedZero; // Still unknown, zeros in all descriptors I have seen } FLASH_DESCRIPTOR_MAP; @@ -56,9 +57,9 @@ typedef struct { typedef struct { UINT8 FirstChipDensity : 3; UINT8 SecondFlashDensity : 3; - UINT8 ReservedZero0 : 2; // Still unknown, zeroes in all descriptors I have seen - UINT8 ReservedZero1 : 8; // Still unknown, zeroes in all descriptors I have seen - UINT8 ReservedZero2 : 4; // Still unknown, zeroes in all descriptors I have seen + UINT8 ReservedZero0 : 2; // Still unknown, zeros in all descriptors I have seen + UINT8 ReservedZero1 : 8; // Still unknown, zeros in all descriptors I have seen + UINT8 ReservedZero2 : 4; // Still unknown, zeros in all descriptors I have seen UINT8 FastReadEnabled : 1; UINT8 FastReadFreqency : 3; UINT8 FlashReadStatusFrequency : 3; @@ -153,6 +154,8 @@ typedef struct { // Restore previous packing rules #pragma pack(pop) +extern QString regionTypeToQString(const UINT8 type); + // Calculate address of data structure addressed by descriptor address format // 8 bit base or limit extern UINT8* calculateAddress8(UINT8* baseAddress, const UINT8 baseOrLimit); diff --git a/ffs.h b/ffs.h index 6c3d4c8..076f77b 100644 --- a/ffs.h +++ b/ffs.h @@ -72,7 +72,7 @@ const QByteArray APTIO_CAPSULE_GUID("\x8B\xA6\x3C\x4A\x23\x77\xFB\x48\x80\x3D\x5 // FvBlockMap ends with an entry {0x00000000, 0x00000000} typedef struct { UINT32 NumBlocks; - UINT32 BlockLength; + UINT32 Length; } EFI_FV_BLOCK_MAP_ENTRY; // Volume header @@ -85,7 +85,7 @@ typedef struct { UINT16 HeaderLength; UINT16 Checksum; UINT16 ExtHeaderOffset; //Reserved in Revision 1 - UINT8 Reserved[1]; + UINT8 Reserved; UINT8 Revision; //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; } EFI_FIRMWARE_VOLUME_HEADER; @@ -248,7 +248,7 @@ typedef struct { // UINT12 ExtendedSize; //} EFI_FFS_FILE_HEADER2; -// Standart data checksum, used if FFS_ATTRIB_CHECKSUM is clear +// Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear #define FFS_FIXED_CHECKSUM 0x5A #define FFS_FIXED_CHECKSUM2 0xAA @@ -276,12 +276,12 @@ typedef struct { #define EFI_FV_FILETYPE_FFS_MAX 0xFF // File attributes -// Revision 1 -#define FFS_ATTRIB_TAIL_PRESENT 0x01 +#define FFS_ATTRIB_RESERVED 0x80 // ErasePolarity value can be obtained from that bit #define FFS_ATTRIB_RECOVERY 0x02 -//#define FFS_ATTRIB_HEADER_EXTENSION 0x04 //Must be set to zero in Revision 1 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 +// Revision 1 +#define FFS_ATTRIB_TAIL_PRESENT 0x01 // Revision 2 #define FFS_ATTRIB_LARGE_FILE 0x01 #define FFS_ATTRIB_FIXED 0x04 @@ -301,7 +301,7 @@ extern const UINT8 ffsAlignmentTable[]; const QByteArray EFI_FFS_VOLUME_TOP_FILE_GUID ("\x2E\x06\xA0\x1B\x79\xC7\x82\x45\x85\x66\x33\x6A\xE8\xF7\x8F\x09", 16); -// FFS size convertion routines +// FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(UINT8* ffsSize); // FFS file 8bit checksum calculation routine @@ -324,7 +324,7 @@ typedef struct { //} EFI_COMMON_SECTION_HEADER2; // File section types -#define EFI_SECTION_ALL 0x00 // Imposible attribute for file in the FS +#define EFI_SECTION_ALL 0x00 // Impossible attribute for file in the FS // Encapsulation section types #define EFI_SECTION_COMPRESSION 0x01 @@ -355,8 +355,8 @@ typedef struct { // Compression types #define EFI_NOT_COMPRESSED 0x00 -#define EFI_STANDARD_COMPRESSION 0x01 //Tiano for Revision 2, EFI for Revision 1 -#define EFI_CUSTOMIZED_COMPRESSION 0x02 //LZMA for Revision 2 Tiano for Revision 1 +#define EFI_STANDARD_COMPRESSION 0x01 +#define EFI_CUSTOMIZED_COMPRESSION 0x02 //GUID defined section typedef struct { @@ -376,7 +376,6 @@ typedef struct { UINT8 Size[3]; UINT8 Type; UINT16 BuildNumber; - //CHAR16 VersionString[1]; } EFI_VERSION_SECTION; // Freeform subtype GUID section @@ -386,13 +385,6 @@ typedef struct { EFI_GUID SubTypeGuid; } EFI_FREEFORM_SUBTYPE_GUID_SECTION; -// User interface section -typedef struct { - UINT8 Size[3]; - UINT8 Type; - //CHAR16 FileNameString[1]; -} EFI_USER_INTERFACE_SECTION; - // Other sections typedef EFI_COMMON_SECTION_HEADER EFI_DISPOSABLE_SECTION; typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION; @@ -404,6 +396,7 @@ typedef EFI_COMMON_SECTION_HEADER EFI_PIC_SECTION; typedef EFI_COMMON_SECTION_HEADER EFI_TE_SECTION; typedef EFI_COMMON_SECTION_HEADER EFI_COMPATIBILITY16_SECTION; typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; //Section routines extern UINT32 sizeOfSectionHeaderOfType(const UINT8 type); diff --git a/ffsengine.cpp b/ffsengine.cpp index 68e6248..970afc4 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -15,7 +15,6 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "ffsengine.h" #include "treeitem.h" #include "treemodel.h" -#include "treeitemtypes.h" #include "descriptor.h" #include "ffs.h" #include "gbe.h" @@ -28,7 +27,11 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. FfsEngine::FfsEngine(QObject *parent) : QObject(parent) { - rootItem = new TreeItem(RootItem, 0, 0, tr("Object"), tr("Type"), tr("Subtype"), tr("Text")); + rootItem = new TreeItem(TreeItem::Root); + rootItem->setName("Name"); + rootItem->setTypeName("Type"); + rootItem->setSubtypeName("Subtype"); + rootItem->setText("Text"); treeModel = new TreeModel(rootItem); } @@ -53,42 +56,147 @@ void FfsEngine::msg(const QString & message) text.append(message).append("\n"); } -UINT8 FfsEngine::parseInputFile(const QByteArray & buffer) +QByteArray FfsEngine::header(const QModelIndex& index) const +{ + if (!index.isValid()) + return QByteArray(); + + TreeItem *item = static_cast(index.internalPointer()); + return item->header(); +} + +QByteArray FfsEngine::body(const QModelIndex& index) const +{ + if (!index.isValid()) + return QByteArray(); + + TreeItem *item = static_cast(index.internalPointer()); + return item->body(); +} + +bool FfsEngine::hasEmptyHeader(const QModelIndex& index) const +{ + if (!index.isValid()) + return true; + + TreeItem *item = static_cast(index.internalPointer()); + + return item->hasEmptyHeader(); +} + +bool FfsEngine::hasEmptyBody(const QModelIndex& index) const +{ + if (!index.isValid()) + return true; + + TreeItem *item = static_cast(index.internalPointer()); + + return item->hasEmptyBody(); +} + +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); +} + +QModelIndex FfsEngine::findParentOfType(UINT8 type, const QModelIndex& index) const +{ + if(!index.isValid()) + return QModelIndex(); + + TreeItem *item; + QModelIndex parent = index; + + for(item = static_cast(parent.internalPointer()); + item != NULL && item != rootItem && item->type() != type; + item = static_cast(parent.internalPointer())) + parent = parent.parent(); + if (item != NULL && item != rootItem) + return parent; + + return QModelIndex(); +} + +bool FfsEngine::isOfType(UINT8 type, const QModelIndex & index) const +{ + if (!index.isValid()) + return false; + + TreeItem *item = static_cast(index.internalPointer()); + return (item->type() == type); +} + +bool FfsEngine::isOfSubtype(UINT8 subtype, const QModelIndex & index) const +{ + TreeItem *item = static_cast(index.internalPointer()); + return (item->subtype() == subtype); +} + +// Firmware image parsing +UINT8 FfsEngine::parseInputFile(const QByteArray & buffer) { UINT32 capsuleHeaderSize = 0; FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL; QByteArray flashImage; QByteArray bios; QModelIndex index; + QString name; + QString info; // 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))); + msg(tr("parseInputFile: Input file is smaller then minimum 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)) { + // Get info 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); + name = tr("UEFI capsule"); + info = tr("Header size: %1\nFlags: %2\nImage size: %3") + .arg(capsuleHeader->HeaderSize, 8, 16, QChar('0')) + .arg(capsuleHeader->Flags, 8, 16, QChar('0')) + .arg(capsuleHeader->CapsuleImageSize, 8, 16, QChar('0')); + // Add tree item + index = treeModel->addItem(TreeItem::Capsule, TreeItem::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); } - + // Check buffer for being extended Aptio capsule header else if (buffer.startsWith(APTIO_CAPSULE_GUID)) { + // Get info 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); + name = tr("AMI Aptio capsule"); + info = tr("Header size: %1\nFlags: %2\nImage size: %3") + .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 + // Add tree item + index = treeModel->addItem(TreeItem::Capsule, TreeItem::AptioCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, 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 @@ -98,45 +206,48 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer) // 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")); + msg(tr("parseInputFile: Input file is smaller then minimum descriptor size of 4096 bytes")); return ERR_INVALID_FLASH_DESCRIPTOR; } // Parse descriptor map - descriptorMap = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); + descriptorMap = (FLASH_DESCRIPTOR_MAP*) (descriptor + sizeof(FLASH_DESCRIPTOR_HEADER)); regionSection = (FLASH_DESCRIPTOR_REGION_SECTION*) calculateAddress8(descriptor, descriptorMap->RegionBase); - - // Add tree item + + // Get info 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); + name = tr("Descriptor"); + 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 + // Add tree item + index = treeModel->addItem(TreeItem::Descriptor, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); - // 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) { + // Parse regions + QModelIndex regionIndex; + // Parse non-BIOS regions + parseRegion(flashImage, TreeItem::GbeRegion, regionSection->GbeBase, regionSection->GbeLimit, index, regionIndex); + parseRegion(flashImage, TreeItem::MeRegion, regionSection->MeBase, regionSection->MeLimit, index, regionIndex); + parseRegion(flashImage, TreeItem::PdrRegion, regionSection->PdrBase, regionSection->PdrLimit, index, regionIndex); + // Parse BIOS region + UINT8 result = parseRegion(flashImage, TreeItem::BiosRegion, regionSection->BiosBase, regionSection->BiosLimit, index, regionIndex); + // Exit if no BIOS region found + if (result != ERR_SUCCESS) { 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)); + index = regionIndex; + bios = QByteArray((const char*)(descriptor + calculateRegionOffset(regionSection->BiosBase)), calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit)); } else { bios = buffer; @@ -144,50 +255,31 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & 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) +UINT8 FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, const QModelIndex & parent, QModelIndex & regionIndex) { // Check for empty region or flash image if (!regionLimit || flashImage.size() <= 0) - return NULL; - + return ERR_EMPTY_REGION; + // 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); - + 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")); - }; + + // Construct region name + QString regionName = regionTypeToQString(regionSubtype); // Check region base to be in buffer if (regionOffset >= flashImageSize) @@ -199,7 +291,7 @@ UINT8* FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype 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; + return ERR_INVALID_REGION; } // Check region to be fully present in buffer @@ -212,62 +304,127 @@ UINT8* FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype 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; + return ERR_INVALID_REGION; } - // Calculate region address - UINT8* region = calculateAddress16((UINT8*) flashImage.constData(), regionBase); + // Get info + QByteArray body = flashImage.mid(regionOffset, regionSize); + QString name; + QString info; + GBE_MAC* gbeMac; + GBE_VERSION* gbeVersion; + ME_VERSION* meVersion; + INT32 meVersionOffset; + info = tr("Size: %1") + .arg(body.size(), 8, 16, QChar('0')); + switch (regionSubtype) { + case TreeItem::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 TreeItem::MeRegion: + name = tr("ME region"); + meVersionOffset = body.indexOf(ME_VERSION_SIGNATURE); + if (meVersionOffset < 0){ + info += tr("\nVersion: unknown"); + msg(tr("parseRegion: 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 TreeItem::BiosRegion: + name = tr("BIOS region"); + break; + case TreeItem::PdrRegion: + name = tr("PDR region"); + break; + default: + name = tr("Unknown region"); + msg(tr("insertInTree: Unknown region")); + break; + } // Add tree item - QByteArray body = flashImage.mid(regionOffset, regionSize); - index = addTreeItem(RegionItem, regionSubtype, regionOffset, QByteArray(), body, index); + regionIndex = treeModel->addItem(TreeItem::Region, regionSubtype, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, parent); - return region; -} + return ERR_SUCCESS; +} 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; + UINT32 prevVolumeOffset; + UINT8 result; + result = findNextVolume(bios, 0, prevVolumeOffset); + if (result == ERR_VOLUMES_NOT_FOUND) + { + //msg(tr("No volumes found in BIOS space")); + return result; } - - // First volume is not at the beginning of bios space + // First volume is not at the beginning of BIOS space + QString name; + QString info; if (prevVolumeOffset > 0) { + // Get info QByteArray padding = bios.left(prevVolumeOffset); - addTreeItem(PaddingItem, 0, 0, QByteArray(), padding, parent); + name = tr("Padding"); + info = tr("Size: %2") + .arg(padding.size(), 8, 16, QChar('0')); + // Add tree item + treeModel->addItem(TreeItem::Padding, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, 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)) + UINT32 volumeOffset = prevVolumeOffset; + UINT32 prevVolumeSize = 0; + UINT32 volumeSize = 0; + + while(true) { // 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); + if (volumeOffset > prevVolumeOffset + prevVolumeSize) { + UINT32 paddingSize = volumeOffset - prevVolumeOffset - prevVolumeSize; + QByteArray padding = bios.mid(prevVolumeOffset + prevVolumeSize, paddingSize); + // Get info + name = tr("Padding"); + info = tr("Size: %2") + .arg(padding.size(), 8, 16, QChar('0')); + // Add tree item + treeModel->addItem(TreeItem::Padding, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } - - // Populate volume header - EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeOffset); - + + // Get volume size + result = getVolumeSize(bios, volumeOffset, volumeSize); + if (result) + return result; + //Check that volume is fully present in input - if (volumeOffset + volumeHeader->FvLength > bios.size()) { + if (volumeOffset + volumeSize > (UINT32) bios.size()) { msg(tr("parseBios: Volume overlaps the end of input buffer")); return ERR_INVALID_VOLUME; } - + // Check volume revision and alignment + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeOffset); UINT32 alignment; if (volumeHeader->Revision == 1) { - // Aquire alignment bits + // Acquire 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; @@ -288,13 +445,14 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) // 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"); + ( 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 + //!TODO: refactor this code alignment = 0x01; if (alignment2) alignment = 0x02; @@ -335,9 +493,9 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) } } else if (volumeHeader->Revision == 2) { - // Aquire alignment + // Acquire alignment alignment = pow(2, (volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); - + // Check alignment if (volumeOffset % alignment) { msg(tr("parseBios: Unaligned revision 2 volume")); @@ -345,757 +503,1298 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) } 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)); - } + UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), parent); + if (result) + msg(tr("parseBios: Volume parsing failed (%1)").arg(result)); + + // Go to next volume + prevVolumeOffset = volumeOffset; + prevVolumeSize = volumeSize; + + result = findNextVolume(bios, volumeOffset + prevVolumeSize, volumeOffset); + if (result == ERR_VOLUMES_NOT_FOUND) + break; } - + return ERR_SUCCESS; } -INT32 FfsEngine::findNextVolume(const QByteArray & bios, INT32 volumeOffset) +UINT8 FfsEngine::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UINT32 & nextVolumeOffset) { - if (volumeOffset < 0) - return -1; - - INT32 nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); + int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); if (nextIndex < EFI_FV_SIGNATURE_OFFSET) { - return -1; + return ERR_VOLUMES_NOT_FOUND; } - return nextIndex - EFI_FV_SIGNATURE_OFFSET; + nextVolumeOffset = nextIndex - EFI_FV_SIGNATURE_OFFSET; + return ERR_SUCCESS; } -UINT32 FfsEngine::getVolumeSize(const QByteArray & bios, INT32 volumeOffset) +UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize) { // 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; + return ERR_INVALID_VOLUME; + + volumeSize = volumeHeader->FvLength; + return ERR_SUCCESS; } -UINT8 FfsEngine::parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent) +UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & parent, const UINT8 mode) { - // Construct empty byte based on erasePolarity value - // Native char type is used because QByteArray.count() takes it - char empty = erasePolarity ? '\xFF' : '\x00'; - + // Populate volume header + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (volume.constData()); + + // 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 { + msg(tr("parseBios: Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid))); + parseCurrentVolume = false; + } + + // Check attributes + // Determine value of empty byte + char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; + + // 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; + } + + // Get info + QString name = guidToQString(volumeHeader->FileSystemGuid); + QString info = tr("Size: %1\nRevision: %2\nAttributes: %3\nHeader size: %4") + .arg(volumeHeader->FvLength, 8, 16, QChar('0')) + .arg(volumeHeader->Revision) + .arg(volumeHeader->Attributes, 8, 16, QChar('0')) + .arg(volumeHeader->HeaderLength, 4, 16, QChar('0')); + + // Add tree item + QByteArray header = volume.left(headerSize); + QByteArray body = volume.mid(headerSize, volumeHeader->FvLength - headerSize); + QModelIndex index = treeModel->addItem(TreeItem::Volume, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + // 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)); + UINT32 fileOffset = headerSize; + UINT32 fileSize; + UINT8 result; + while (true) { + result = getFileSize(volume, fileOffset, fileSize); + if (result) + return result; + + // Check file size to be at least sizeof(EFI_FFS_FILE_HEADER) + if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { + msg(tr("parseVolume: File with invalid size")); + return ERR_INVALID_FILE; + } + + QByteArray file = volume.mid(fileOffset, fileSize); 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); + // We are now at empty space in the end of volume + if (header.count(empty) == header.size()) 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 + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) header.constData(); UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; UINT32 alignment = pow(2, alignmentPower); - if ((volumeBase + fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment) { + if ((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)); - } - } + result = parseFile(file, volumeHeader->Revision, empty, index); + if (result) + msg(tr("parseVolume: Parse FFS file failed (%1)").arg(result)); // Move to next file - fileOffset += file.size(); + fileOffset += fileSize; 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)); - } - + if (fileOffset >= (UINT32) volume.size()) 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 +UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize) { - if (!index.isValid()) - return true; - - TreeItem *item = static_cast(index.internalPointer()); - - return item->hasEmptyHeader(); + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) (volume.constData() + fileOffset); + fileSize = uint24ToUint32(fileHeader->Size); + return ERR_SUCCESS; } -bool FfsEngine::hasEmptyBody(const QModelIndex& index) const +UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char empty, const QModelIndex & parent, const UINT8 mode) { - if (!index.isValid()) - return true; + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) file.constData(); - TreeItem *item = static_cast(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 + // Check header checksum + 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((UINT8*) tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + if (fileHeader->IntegrityCheck.Checksum.Header != calculated) { - parentItem = static_cast(parent.internalPointer()); - parentColumn = parent.column(); + 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'))); } - - // 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) + + // 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(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'))); + } + } + + // Get file body + QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER)); + UINT32 fileSize = (UINT32) file.size(); + // For files in Revision 1 volumes, check for file tail presence + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { - case RootItem: - // Do not allow to add another root item - return QModelIndex(); + //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)); + fileSize -= sizeof(UINT16); + } + + // Parse current file by default + bool parseCurrentFile = true; + bool parseAsBios = false; + + // Check file type + //!TODO: add more file specific checks + switch (fileHeader->Type) + { + case EFI_FV_FILETYPE_ALL: + parseAsBios = true; 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; - } + case EFI_FV_FILETYPE_RAW: + parseAsBios = true; 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 + case EFI_FV_FILETYPE_FREEFORM: 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; - } + case EFI_FV_FILETYPE_SECURITY_CORE: 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')); + case EFI_FV_FILETYPE_PEI_CORE: 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')); + case EFI_FV_FILETYPE_DXE_CORE: break; - case FileItem: - typeName = tr("File"); - // Parse file header to determine its GUID and type - fileHeader = (EFI_FFS_FILE_HEADER*) header.constData(); + 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; + } + + // Get info + QString name; + QString info; + if (fileHeader->Type != EFI_FV_FILETYPE_PAD) 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')); + else + name = tr("Padding"); + info = tr("Type: %1\nAttributes: %2\nSize: %3\nState: %4") + .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')); + + // Add tree item + QModelIndex index = treeModel->addItem(TreeItem::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + + if (!parseCurrentFile) + return ERR_SUCCESS; + + // Parse file as BIOS space + UINT8 result; + if (parseAsBios) { + result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND) + msg(tr("parseVolume: Parse file as BIOS failed (%1)").arg(result)); + } + // Parse sections + else { + result = parseSections(body, revision, empty, index); + if (result) + return result; + } + + return ERR_SUCCESS; +} + +UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize) +{ + EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (file.constData() + sectionOffset); + sectionSize = uint24ToUint32(sectionHeader->Size); + return ERR_SUCCESS; +} + +UINT8 FfsEngine::parseSections(const QByteArray & body, const UINT8 revision, const char empty, const QModelIndex & parent) +{ + // Search for and parse all sections + UINT32 sectionOffset = 0; + UINT32 sectionSize; + UINT32 bodySize = body.size(); + UINT8 result; + while (true) { + // Get section size + result = getSectionSize(body, sectionOffset, sectionSize); + if (result) + return result; + + // Parse section + result = parseSection(body.mid(sectionOffset, sectionSize), revision, empty, parent); + if (result) + return result; + + // Move to next section + sectionOffset += sectionSize; + sectionOffset = ALIGN4(sectionOffset); + + // Exit from loop if no sections left + if (sectionOffset >= bodySize) + break; + } + + return ERR_SUCCESS; +} + +UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision, + const char empty, const QModelIndex & parent, const UINT8 mode) +{ + EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (section.constData()); + UINT32 sectionSize = uint24ToUint32(sectionHeader->Size); + QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); + QString info; + QByteArray header; + QByteArray body; + UINT32 headerSize; + QModelIndex index; + UINT8 result; + + switch (sectionHeader->Type) { + // Encapsulated sections + case EFI_SECTION_COMPRESSION: + { + bool parseCurrentSection = true; + QByteArray decompressed; + UINT8 algorithm; + EFI_COMPRESSION_SECTION* compressedSectionHeader = (EFI_COMPRESSION_SECTION*) sectionHeader; + header = section.left(sizeof(EFI_COMPRESSION_SECTION)); + body = section.mid(sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION)); + algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + // Decompress section + result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); + if (result) { + msg(tr("parseFile: Section decompression failed (%1)").arg(result)); + parseCurrentSection = false; + } + + // Get info + info = tr("Type: %1\nSize: %2\nCompression type: %3\nDecompressed size: %4") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')) + .arg(compressionTypeToQString(algorithm)) + .arg(compressedSectionHeader->UncompressedLength, 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); + + // Parse decompressed data + if (parseCurrentSection) { + result = parseSections(decompressed, revision, empty, index); + if (result) + return result; + } + } break; - case SectionItem: - typeName = tr("Section"); - name = sectionTypeToQString(subtype) + tr(" section"); - info = tr("Offset: %1\nSize: %2") - .arg(offset, 8, 16, QChar('0')) + case EFI_SECTION_GUID_DEFINED: + { + bool parseCurrentSection = true; + EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader; + header = section.left(sizeof(EFI_GUID_DEFINED_SECTION)); + guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) (header.constData()); + header = section.left(guidDefinedSectionHeader->DataOffset); + guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) (header.constData()); + body = section.mid(guidDefinedSectionHeader->DataOffset, sectionSize - guidDefinedSectionHeader->DataOffset); + QByteArray decompressed = body; + UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; + // Check if section requires processing + if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { + // Try to decompress section body using both known compression algorithms + algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + // Tiano + result = decompress(body, EFI_STANDARD_COMPRESSION, decompressed, &algorithm); + if (result) { + result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, decompressed, &algorithm); + if (result) { + msg(tr("parseFile: GUID defined section can not be decompressed (%1)").arg(result)); + parseCurrentSection = false; + } + } + + } + + // Get info + name = guidToQString(guidDefinedSectionHeader->SectionDefinitionGuid); + info = tr("Type: %1\nSize: %2\nData offset: %3\nAttributes: %4\nCompression type: %5") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')) + .arg(guidDefinedSectionHeader->DataOffset, 4, 16, QChar('0')) + .arg(guidDefinedSectionHeader->Attributes, 4, 16, QChar('0')) + .arg(compressionTypeToQString(algorithm)); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); + + // Parse decompressed data + if (parseCurrentSection) { + result = parseSections(decompressed, revision, empty, index); + if (result) + return result; + } + } + break; + case EFI_SECTION_DISPOSABLE: + { + header = section.left(sizeof(EFI_DISPOSABLE_SECTION)); + body = section.mid(sizeof(EFI_DISPOSABLE_SECTION), sectionSize - sizeof(EFI_DISPOSABLE_SECTION)); + + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + + // Parse section body + result = parseSections(body, revision, empty, index); + if (result) + return result; + } + break; + + // Leaf sections + case EFI_SECTION_PE32: + case EFI_SECTION_PIC: + case EFI_SECTION_TE: + case EFI_SECTION_VERSION: + case EFI_SECTION_COMPATIBILITY16: + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: + case EFI_SECTION_DXE_DEPEX: + case EFI_SECTION_PEI_DEPEX: + case EFI_SECTION_SMM_DEPEX: + headerSize = sizeOfSectionHeaderOfType(sectionHeader->Type); + header = section.left(headerSize); + body = section.mid(headerSize, sectionSize - headerSize); + + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 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) + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + break; + case EFI_SECTION_USER_INTERFACE: + header = section.left(sizeof(EFI_USER_INTERFACE_SECTION)); + body = section.mid(sizeof(EFI_USER_INTERFACE_SECTION), sectionSize - sizeof(EFI_USER_INTERFACE_SECTION)); + + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + + // Rename parent file { QString text = QString::fromUtf16((const ushort*)body.constData()); - setTreeItemText(text, findParentOfType(FileItem, parent)); + setTreeItemText(text, findParentOfType(TreeItem::File, parent)); + } + 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), sectionSize - sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + + // 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)); + return result; + } + break; + case EFI_SECTION_RAW: + header = section.left(sizeof(EFI_RAW_SECTION)); + body = section.mid(sizeof(EFI_RAW_SECTION), sectionSize - sizeof(EFI_RAW_SECTION)); + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + + // 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)); + return result; } break; default: - name = tr("Unknown"); - info = tr("Offset: %1").arg(offset, 8, 16, QChar('0')); - break; + msg(tr("parseFile: Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0'))); + header = section.left(sizeof(EFI_COMMON_SECTION_HEADER)); + body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER)); + // Get info + info = tr("Type: %1\nSize: %2") + .arg(sectionHeader->Type, 2, 16, QChar('0')) + .arg(body.size(), 8, 16, QChar('0')); + + // Add tree item + index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); + return ERR_UNKNOWN_SECTION; } + return ERR_SUCCESS; +} + +// Operations on tree items +UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, const UINT8 type, const UINT8 mode) +{ + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Only files and sections can now be inserted + if (type == TreeItem::File) { + QModelIndex parent; + if (mode == ADD_MODE_APPEND || mode == ADD_MODE_PREPEND) + parent = index; + else + parent = index.parent(); - return treeModel->addItem(type, subtype, offset, name, typeName, subtypeName, "", info, header, body, parent); -} + // Parent type must be volume + TreeItem * parentItem = static_cast(parent.internalPointer()); + if (parentItem->type() != TreeItem::Volume) { + msg(tr("insertInto: file can't be inserted into something that is not volume")); + return ERR_INVALID_VOLUME; + } -bool FfsEngine::setTreeItemName(const QString &data, const QModelIndex &index) -{ - if(!index.isValid()) - return false; + EFI_FIRMWARE_VOLUME_HEADER* header = (EFI_FIRMWARE_VOLUME_HEADER*) parentItem->header().constData(); + + // Parse file + //!TODO: add check for same GUIDs + UINT8 result = parseFile(object, header->Revision, header->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00', index, mode); + if (result) + return result; + + // Set reconstruct action for all it's parents + for (;parent.isValid(); parent = parent.parent()) + treeModel->setItemAction(TreeItem::Reconstruct, parent); + + } + else if (type == TreeItem::Section) { + return ERR_NOT_IMPLEMENTED; + } + else + return ERR_NOT_IMPLEMENTED; - return treeModel->setItemName(data, index); + return ERR_SUCCESS; } -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(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(parent.internalPointer()); - item != NULL && item != rootItem && item->type() != type; - item = static_cast(parent.internalPointer())) - parent = parent.parent(); - if (item != NULL && item != rootItem) - return parent; - - return QModelIndex(); -} - -QByteArray FfsEngine::header(const QModelIndex& index) const +UINT8 FfsEngine::remove(const QModelIndex & index) { if (!index.isValid()) - return QByteArray(); + return ERR_INVALID_PARAMETER; - TreeItem *item = static_cast(index.internalPointer()); - return item->header(); + // Set action for the item + treeModel->setItemAction(TreeItem::Remove, index); + + // Set reconstruct action for all it's parents + for (QModelIndex parent = index.parent(); parent.isValid(); parent = parent.parent()) + treeModel->setItemAction(TreeItem::Reconstruct, parent); + + return ERR_SUCCESS; } -QByteArray FfsEngine::body(const QModelIndex& index) const + + +// Compression routines +UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm) { - if (!index.isValid()) - return QByteArray(); + UINT8* data; + UINT32 dataSize; + UINT8* decompressed; + UINT32 decompressedSize = 0; + UINT8* scratch; + UINT32 scratchSize = 0; + EFI_TIANO_HEADER* header; - TreeItem *item = static_cast(index.internalPointer()); - return item->body(); + switch (compressionType) + { + case EFI_NOT_COMPRESSED: + decompressedData = compressedData; + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_NONE; + return ERR_SUCCESS; + case EFI_STANDARD_COMPRESSION: + // Get buffer sizes + data = (UINT8*) compressedData.constData(); + dataSize = compressedData.size(); + + // Check header to be valid + header = (EFI_TIANO_HEADER*) data; + if (header->CompSize + sizeof(EFI_TIANO_HEADER) != dataSize) + return ERR_STANDARD_DECOMPRESSION_FAILED; + + // Get info function is the same for both algorithms + if (ERR_SUCCESS != EfiTianoGetInfo(data, dataSize, &decompressedSize, &scratchSize)) + return ERR_STANDARD_DECOMPRESSION_FAILED; + + // Allocate memory + decompressed = new UINT8[decompressedSize]; + scratch = new UINT8[scratchSize]; + + // Decompress section data + if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { + if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + return ERR_STANDARD_DECOMPRESSION_FAILED; + } + else if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_TIANO; + } + else if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_EFI11; + + decompressedData = QByteArray((const char*) decompressed, decompressedSize); + + // Free allocated memory + delete[] decompressed; + delete[] scratch; + + return ERR_SUCCESS; + case EFI_CUSTOMIZED_COMPRESSION: + // Get buffer sizes + data = (UINT8*) compressedData.constData(); + dataSize = compressedData.size(); + + // Get info + if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + + // Allocate memory + decompressed = new UINT8[decompressedSize]; + + // Decompress section data + if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { + // Intel modified LZMA workaround + EFI_COMMON_SECTION_HEADER* shittySectionHeader; + UINT32 shittySectionSize; + // Shitty compressed section 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); + + // Decompress section data once again + data += shittySectionSize; + + // Get info again + if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + + // Decompress section data again + if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; + } + else { + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_IMLZMA; + decompressedData = QByteArray((const char*) decompressed, decompressedSize); + } + } + else { + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_LZMA; + decompressedData = QByteArray((const char*) decompressed, decompressedSize); + } + + // Free memory + delete[] decompressed; + + return ERR_SUCCESS; + default: + msg(tr("decompress: Unknown compression type (%1)").arg(compressionType)); + if (algorithm) + *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; + return ERR_UNKNOWN_COMPRESSION_ALGORITHM; + } } -QByteArray FfsEngine::uncompressFile(const QModelIndex& index) const +UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteArray & compressedData) +{ + UINT8* compressed; + UINT32 compressedSize = 0; + + switch (algorithm) { + case COMPRESSION_ALGORITHM_NONE: + { + compressedData = data; + return ERR_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_EFI11: + { + if (EfiCompress((UINT8*) data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) + return ERR_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (EfiCompress((UINT8*) data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) + return ERR_STANDARD_COMPRESSION_FAILED; + compressedData = QByteArray((const char*) compressed, compressedSize); + delete[] compressed; + return ERR_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_TIANO: + { + if (TianoCompress((UINT8*) data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) + return ERR_STANDARD_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (TianoCompress((UINT8*) data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) + return ERR_STANDARD_COMPRESSION_FAILED; + compressedData = QByteArray((const char*) compressed, compressedSize); + delete[] compressed; + return ERR_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_LZMA: + { + if (LzmaCompress((UINT8*) data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) + return ERR_CUSTOMIZED_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (LzmaCompress((UINT8*) data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) + return ERR_CUSTOMIZED_COMPRESSION_FAILED; + compressedData = QByteArray((const char*) compressed, compressedSize); + delete[] compressed; + return ERR_SUCCESS; + } + break; + case COMPRESSION_ALGORITHM_IMLZMA: + { + QByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER)); + EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) header.constData(); + UINT32 headerSize = sizeOfSectionHeaderOfType(sectionHeader->Type); + header = data.left(headerSize); + QByteArray newData = data.mid(headerSize); + if (LzmaCompress((UINT8*) newData.constData(), newData.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) + return ERR_CUSTOMIZED_COMPRESSION_FAILED; + compressed = new UINT8[compressedSize]; + if (LzmaCompress((UINT8*) newData.constData(), newData.size(), compressed, &compressedSize) != ERR_SUCCESS) + return ERR_CUSTOMIZED_COMPRESSION_FAILED; + compressedData = header.append(QByteArray((const char*) compressed, compressedSize)); + delete[] compressed; + return ERR_SUCCESS; + } + break; + default: + msg(tr("compress: Unknown compression algorithm (%1)").arg(algorithm)); + return ERR_UNKNOWN_COMPRESSION_ALGORITHM; + } +} + + + +// Construction routines +UINT8 FfsEngine::reconstructImage(QByteArray & reconstructed) +{ + QQueue queue; + reconstructed = QByteArray(); + rootItem->setAction(TreeItem::Reconstruct); + UINT8 result = reconstruct(rootItem, queue); + if (result) + return result; + + while (!queue.isEmpty()) + reconstructed.append(queue.dequeue()); + rootItem->setAction(TreeItem::NoAction); + return ERR_SUCCESS; +} + +UINT8 FfsEngine::constructPadFile(const UINT32 size, const UINT8 revision, const char empty, QByteArray & pad) +{ + if (size < sizeof(EFI_FFS_FILE_HEADER)) + return ERR_INVALID_PARAMETER; + + pad = QByteArray(size, empty); + EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*) pad.data(); + uint32ToUint24(size, header->Size); + header->Attributes = 0x00; + header->Type = EFI_FV_FILETYPE_PAD; + header->State = 0xF8; + // Calculate header checksum + header->IntegrityCheck.Checksum.Header = 0; + header->IntegrityCheck.Checksum.File = 0; + header->IntegrityCheck.Checksum.Header = calculateChecksum8((UINT8*) header, sizeof(EFI_FFS_FILE_HEADER) - 1); + // Set data checksum + if (revision == 1) + header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; + else + header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; + + return ERR_SUCCESS; +} + +UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue & queue, const UINT8 revision, char empty) +{ + if (!item) + return ERR_SUCCESS; + + QByteArray reconstructed; + UINT8 result; + + // No action is needed, just return header + body + if (item->action() == TreeItem::NoAction) { + reconstructed = item->header().append(item->body()); + // One special case: file with tail + if (revision == 1 && item->type() == TreeItem::File) { + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) item->header().constData(); + if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { + // Append file tail + reconstructed.append(!fileHeader->IntegrityCheck.TailReference); + } + } + + queue.enqueue(reconstructed); + return ERR_SUCCESS; + } + // Remove item + else if (item->action() == TreeItem::Remove) { + // Root item can't be removed + if (item == rootItem) + return ERR_INVALID_PARAMETER; + // Volume can be removed by replacing all it's contents with empty bytes + if (item->type() == TreeItem::Volume) { + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) item->header().constData(); + empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; + reconstructed.fill(empty, item->header().size() + item->body().size()); + queue.enqueue(reconstructed); + return ERR_SUCCESS; + } + // File can be removed + if (item->type() == TreeItem::File) + // Add nothing to queue + return ERR_SUCCESS; + // Section can be removed + else if (item->type() == TreeItem::Section) + // Add nothing to queue + return ERR_SUCCESS; + // Other item types can't be removed + else + return ERR_NOT_IMPLEMENTED; + } + // Reconstruct item and it's children recursive + else if (item->action() == TreeItem::Reconstruct) { + QQueue childrenQueue; + + switch (item->type()) { + case TreeItem::Capsule: + if (item->subtype() == TreeItem::AptioCapsule) + msg(tr("reconstruct: Aptio extended header checksum and signature are now invalid")); + case TreeItem::Root: + case TreeItem::Descriptor: + case TreeItem::Region: + case TreeItem::Padding: + // Reconstruct item body + if (item->childCount()) { + // Reconstruct item children + for (int i = 0; i < item->childCount(); i++) { + result = reconstruct(item->child(i), childrenQueue); + if (result) + return result; + } + + // No additional checks needed + while (!childrenQueue.isEmpty()) + reconstructed.append(childrenQueue.dequeue()); + } + // Use stored item body + else + reconstructed = item->body(); + + // Enqueue reconstructed item + queue.enqueue(item->header().append(reconstructed)); + break; + + case TreeItem::Volume: + { + //!TODO: add check for weak aligned volumes + QByteArray header = item->header(); + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) header.constData(); + empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; + // Reconstruct volume body + if (item->childCount()) { + // Reconstruct files in volume + for (int i = 0; i < item->childCount(); i++) { + // Reconstruct files + result = reconstruct(item->child(i), childrenQueue, volumeHeader->Revision, empty); + if (result) + return result; + } + + // Remove all pad files, which will be recreated later + foreach(const QByteArray & child, childrenQueue) + { + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) child.constData(); + if (fileHeader->Type == EFI_FV_FILETYPE_PAD) + childrenQueue.removeAll(child); + } + + // Construct new volume body + UINT32 offset = 0; + while (!childrenQueue.isEmpty()) + { + // Align to 8 byte boundary + UINT32 alignment = offset % 8; + if (alignment) { + alignment = 8 - alignment; + offset += alignment; + reconstructed.append(QByteArray(alignment, empty)); + } + + // Get file from queue + QByteArray file = childrenQueue.dequeue(); + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) file.data(); + + // Check alignment + UINT8 alignmentPower; + UINT32 base; + alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + alignment = pow(2, alignmentPower); + base = header.size() + offset + sizeof(EFI_FFS_FILE_HEADER); + if (base % alignment) { + // File will be unaligned if added as is, so we must add pad file before it + // Determine pad file size + UINT32 size = alignment - (base % alignment); + // Required padding is smaler then minimal pad file size + while (size < sizeof(EFI_FFS_FILE_HEADER)) { + size += alignment; + } + // Construct pad file + QByteArray pad; + result = constructPadFile(size, revision, empty, pad); + if (result) + return result; + // Append constructed pad file to volume body + reconstructed.append(pad); + offset += size; + } + + // If this is a last file in volume + if (childrenQueue.isEmpty()) + { + // Last file of the volume can be Volume Top File + if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { + // Determine correct VTF offset + UINT32 vtfOffset = volumeHeader->FvLength - header.size() - file.size(); + if (offset % 8) { + msg(tr("reconstruct: %1: Wrong size of Volume Top File") + .arg(guidToQString(volumeHeader->FileSystemGuid))); + return ERR_INVALID_FILE; + } + // Insert pad file to fill the gap + if (vtfOffset > offset) { + // Determine pad file size + UINT32 size = vtfOffset - offset; + // Construct pad file + QByteArray pad; + result = constructPadFile(size, revision, empty, pad); + if (result) + return result; + // Append constructed pad file to volume body + reconstructed.append(pad); + offset = vtfOffset; + // Ensure that no more files will be in this volume + childrenQueue.clear(); + } + // No more space left in volume + else if (vtfOffset < offset) { + //!TODO: attempt volume grow + msg(tr("reconstruct: %1: can't insert VTF, need additional %2 bytes") + .arg(guidToQString(volumeHeader->FileSystemGuid)) + .arg(offset - vtfOffset, 8, 16, QChar('0'))); + return ERR_INVALID_VOLUME; + } + } + // Fill all following bytes with empty char + else { + INT32 size = volumeHeader->FvLength - header.size() - offset - file.size(); + // Append fill + if (size > 0) + file.append(QByteArray(size, empty)); + } + } + + // Append current file to new volume body + reconstructed.append(file); + // Change current file offset + offset += file.size(); + } + + // Check new body size + if (header.size() + reconstructed.size() > volumeHeader->FvLength) + { + //!TODO: attemt volume grow + msg(tr("reconstruct: Volume grow operation is not yet implemented")); + return ERR_NOT_IMPLEMENTED; + /*// Volumes can be children of RootItem, CapsuleItem, RegionItem, FileItem and SectionItem + + UINT32 sizeToGrow = 0; + UINT8 parentType = item->parent()->type(); + //!TODO: refactor this code to make it work + // First 3 kind of volumes can be grown only if they have padding after them + if (parentType == RootItem || parentType == CapsuleItem || parentType == RegionItem) { + // Find next item + for (int i = 0; i < item->parent()->childCount(); i++) + if (item == item->parent()->child(i) && item->parent()->child(i+1) != NULL) { + TreeItem* pad = item->parent()->child(i+1); + // Check if that item is padding + if (pad->type() == PaddingItem) { + // All it's space can be used for volume growing + sizeToGrow = pad->body().size(); + } + } + } + + // Second 2 kind can just be grown up to UIN32_MAX in size + if (parentType == TreeItem::File || parentType == TreeItem::Section) { + sizeToGrow = UINT32_MAX - header.size() - reconstructed.size(); + } + + // Volume is a child of some other item, this is a bug + else { + msg(tr("reconstructTreeItem: %1: volume is a child of incompatible item") + .arg(guidToQString(volumeHeader->FileSystemGuid))); + return ERR_INVALID_VOLUME; + } + + if (sizeToGrow == 0 || (header.size() + reconstructed.size() - volumeHeader->FvLength) > sizeToGrow) { + msg(tr("reconstruct: %1: volume can not be grown") + .arg(guidToQString(volumeHeader->FileSystemGuid))); + return ERR_VOLUME_GROW_FAILED; + } + + // Adjust new size to be representable by current FvBlockMap + // We assume that all current volumes have only one meaningful FvBlockMap entry + EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*) (header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); + + // Calculate new size + UINT32 size = header.size() + reconstructed.size(); + sizeToGrow = blockMap->Length - size % blockMap->Length; + + // Recalculate number of blocks + blockMap->NumBlocks += sizeToGrow / blockMap->Length + 1; + + // Set new volume size + //!NOTE: this is dangerous and must be checked before adding volume to items tree + volumeHeader->FvLength = 0; + while (blockMap->NumBlocks != 0 || blockMap->Length != 0) + volumeHeader->FvLength += blockMap->NumBlocks * blockMap->Length; + + // Recalculate volume header checksum + volumeHeader->Checksum = 0; + volumeHeader->Checksum = calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength);*/ + } + } + // Use current volume body + else + reconstructed = item->body(); + + // Enqueue reconstructed item + queue.enqueue(header.append(reconstructed)); + } + break; + + case TreeItem::File: + { + QByteArray header = item->header(); + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) header.data(); + + // Reconstruct file body + if (item->childCount()) { + for (int i = 0; i < item->childCount(); i++) { + // Reconstruct sections + result = reconstruct(item->child(i), childrenQueue, revision, empty); + if (result) + return result; + } + + // Construct new file body + UINT32 offset = 0; + while (!childrenQueue.isEmpty()) + { + // Align to 4 byte boundary + UINT8 alignment = offset % 4; + if (alignment) { + alignment = 4 - alignment; + offset += alignment; + reconstructed.append(QByteArray(alignment, empty)); + } + + // Get section from queue + QByteArray section = childrenQueue.dequeue(); + + // Append current section to new file body + reconstructed.append(section); + // Change current file offset + offset += section.size(); + } + + // Correct file size + UINT8 tailSize = 0; + if(revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) + tailSize = sizeof(UINT16); + + 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((UINT8*) fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + + // Recalculate data checksum, if needed + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((UINT8*) reconstructed.constData(), reconstructed.size()); + } + else if (revision == 1) + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; + else + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; + + // Append tail, if needed + if (tailSize) + reconstructed.append(!fileHeader->IntegrityCheck.TailReference); + } + // Use current file body + else + reconstructed = item->body(); + + // Enqueue reconstructed item + queue.enqueue(header.append(reconstructed)); + } + break; + + case TreeItem::Section: + { + QByteArray header = item->header(); + EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*) header.data(); + + // Section with children + if (item->childCount()) { + // Reconstruct section body + for (int i = 0; i < item->childCount(); i++) { + // Reconstruct subsections + result = reconstruct(item->child(i), childrenQueue, revision, empty); + if (result) + return result; + } + + // Construct new section body + UINT32 offset = 0; + while (!childrenQueue.isEmpty()) + { + // Align to 4 byte boundary + UINT8 alignment = offset % 4; + if (alignment) { + alignment = 4 - alignment; + offset += alignment; + reconstructed.append(QByteArray(alignment, empty)); + } + + // Get section from queue + QByteArray section = childrenQueue.dequeue(); + + // Append current subsection to new section body + reconstructed.append(section); + // Change current file offset + offset += section.size(); + } + + // Only this 2 sections can have compressed body + if (item->subtype() == EFI_SECTION_COMPRESSION) { + EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*) header.data(); + // Set new uncompressed size + compessionHeader->UncompressedLength = reconstructed.size(); + // Compress new section body + QByteArray compressed; + result = compress(reconstructed, item->compression(), compressed); + if (result) + return result; + // Correct compression type + if (item->compression() == COMPRESSION_ALGORITHM_NONE) + compessionHeader->CompressionType = EFI_NOT_COMPRESSED; + else if (item->compression() == COMPRESSION_ALGORITHM_LZMA || item->compression() == COMPRESSION_ALGORITHM_IMLZMA) + compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; + else + compessionHeader->CompressionType = EFI_STANDARD_COMPRESSION; + // Replace new section body + reconstructed = compressed; + } + else if (item->subtype() == EFI_SECTION_GUID_DEFINED) { + EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*) header.data(); + // Compress new section body + QByteArray compressed; + result = compress(reconstructed, item->compression(), compressed); + if (result) + return result; + // Check for auth status valid attribute + if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) + { + msg(tr("reconstruct: %1: GUID defined section signature can now become invalid") + .arg(guidToQString(guidDefinedHeader->SectionDefinitionGuid))); + } + // Replace new section body + reconstructed = compressed; + } + else if (item->compression() != COMPRESSION_ALGORITHM_NONE) { + msg(tr("reconstruct: compression required for section of type %1") + .arg(item->subtype())); + return ERR_INVALID_SECTION; + } + + // Correct section size + uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); + } + // Leaf section + else + reconstructed = item->body(); + + // Enqueue reconstructed item + queue.enqueue(header.append(reconstructed)); + } + break; + + default: + msg(tr("reconstruct: Unknown item type (%1)").arg(item->type())); + return ERR_UNKNOWN_ITEM_TYPE; + } + + return ERR_SUCCESS; + } + + //!TODO: implement other actions + return ERR_NOT_IMPLEMENTED; +} + + + +// Will be refactored later +QByteArray FfsEngine::decompressFile(const QModelIndex& index) const { if (!index.isValid()) return QByteArray(); // Check index item to be FFS file TreeItem *item = static_cast(index.internalPointer()); - if(item->type() != FileItem) + if(item->type() != TreeItem::File) return QByteArray(); QByteArray file; UINT32 offset = 0; // Construct new item body - for (int i = 0; i < item->childCount(); i++) - { + 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) - { + if (sectionItem->subtype() != EFI_SECTION_COMPRESSION) { QByteArray section = sectionItem->header().append(sectionItem->body()); UINT32 align = ALIGN4(offset) - offset; file.append(QByteArray(align, '\x00')).append(section); @@ -1114,7 +1813,7 @@ QByteArray FfsEngine::uncompressFile(const QModelIndex& index) const 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; @@ -1127,13 +1826,12 @@ QByteArray FfsEngine::uncompressFile(const QModelIndex& index) const 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) - { + 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); } @@ -1151,12 +1849,11 @@ bool FfsEngine::isCompressedFile(const QModelIndex& index) const return false; TreeItem *item = static_cast(index.internalPointer()); - if(item->type() != FileItem) + if(item->type() != TreeItem::File) return false; - for (int i = 0; i < item->childCount(); i++) - { - if (item->child(i)->subtype() == CompressionSection) + for (int i = 0; i < item->childCount(); i++) { + if (item->child(i)->subtype() == EFI_SECTION_COMPRESSION) return true; } diff --git a/ffsengine.h b/ffsengine.h index ca1d6ff..7a8e529 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -16,8 +16,10 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include "basetypes.h" +#include "treeitem.h" #include "treemodel.h" class TreeModel; @@ -27,41 +29,65 @@ class FfsEngine : public QObject Q_OBJECT public: + // Default constructor and destructor FfsEngine(QObject *parent = 0); ~FfsEngine(void); - + // Returns model for Qt view classes TreeModel* model() const; + // Returns current message QString message() const; + // Firmware image parsing UINT8 parseInputFile(const QByteArray & buffer); - UINT8* parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index); + UINT8 parseRegion(const QByteArray & flashImage, const UINT8 regionSubtype, const UINT16 regionBase, + const UINT16 regionLimit, const QModelIndex & parent, QModelIndex & regionIndex); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); - INT32 findNextVolume(const QByteArray & bios, INT32 volumeOffset = 0); - UINT32 getVolumeSize(const QByteArray & bios, INT32 volumeOffset); - UINT8 parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex()); - UINT8 parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, const QModelIndex & parent = QModelIndex()); + UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); + UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize); + UINT8 parseVolume(const QByteArray & volume, const QModelIndex & parent = QModelIndex(), const UINT8 mode = ADD_MODE_APPEND); + UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); + UINT8 parseFile(const QByteArray & file, const UINT8 revision, const char empty = '\xFF', const QModelIndex & parent = QModelIndex(), const UINT8 mode = ADD_MODE_APPEND); + UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); + UINT8 parseSections(const QByteArray & body, const UINT8 revision, const char empty = '\xFF', const QModelIndex & parent = QModelIndex()); + UINT8 parseSection(const QByteArray & section, const UINT8 revision, const char empty = '\xFF', const QModelIndex & parent = QModelIndex(), const UINT8 mode = ADD_MODE_APPEND); - QModelIndex addTreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0, - const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), - const QModelIndex & parent = QModelIndex()); - bool removeItem(const QModelIndex &index); - - QByteArray header(const QModelIndex& index) const; - bool hasEmptyHeader(const QModelIndex& index) const; - QByteArray body(const QModelIndex& index) const; - bool hasEmptyBody(const QModelIndex& index) const; + // Compression routines + UINT8 decompress(const QByteArray & compressed, const UINT8 compressionType, QByteArray & decompressedSection, UINT8 * algorithm = NULL); + UINT8 compress(const QByteArray & data, const UINT8 algorithm, QByteArray & compressedData); - bool isCompressedFile(const QModelIndex& index) const; - QByteArray uncompressFile(const QModelIndex& index) const; + // Construction routines + UINT8 reconstructImage(QByteArray & reconstructed); + UINT8 constructPadFile(const UINT32 size, const UINT8 revision, const char empty, QByteArray & pad); + UINT8 reconstruct(TreeItem* item, QQueue & queue, const UINT8 revision = 2, char empty = '\xFF'); + + // Operations on tree items + UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 type, const UINT8 mode); + UINT8 remove(const QModelIndex & index); + + // Proxies to model operations + QByteArray header(const QModelIndex & index) const; + bool hasEmptyHeader(const QModelIndex & index) const; + QByteArray body(const QModelIndex & index) const; + bool hasEmptyBody(const QModelIndex & index) const; + + // Item-related operations + bool isOfType(UINT8 type, const QModelIndex & index) const; + bool isOfSubtype(UINT8 subtype, const QModelIndex & index) const; + QModelIndex findParentOfType(UINT8 type, const QModelIndex& index) const; + + // Will be refactored later + bool isCompressedFile(const QModelIndex & index) const; + QByteArray decompressFile(const QModelIndex & index) const; private: - QString text; - TreeItem *rootItem; + QString text; + TreeItem *rootItem; TreeModel *treeModel; + // Adds string to message void msg(const QString & message); - QModelIndex findParentOfType(UINT8 type, const QModelIndex& index) const; - bool setTreeItemName(const QString &data, const QModelIndex &index); - bool setTreeItemText(const QString &data, const QModelIndex &index); + // Internal operations used in insertInTree + bool setTreeItemName(const QString & data, const QModelIndex & index); + bool setTreeItemText(const QString & data, const QModelIndex & index); }; #endif diff --git a/treeitem.cpp b/treeitem.cpp index 42d3da5..0ec2238 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -11,23 +11,97 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ +#include #include "treeitem.h" +#include "ffs.h" +#include "descriptor.h" -TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT32 offset, const QString & name, const QString & typeName, const QString & subtypeName, - const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, TreeItem *parent) +QString itemTypeToQString(const UINT8 type) { - + switch (type) { + case TreeItem::Root: + return QObject::tr("Root"); + case TreeItem::Capsule: + return QObject::tr("Capsule"); + case TreeItem::Descriptor: + return QObject::tr("Flash descriptor"); + case TreeItem::Region: + return QObject::tr("Region"); + case TreeItem::Volume: + return QObject::tr("Volume"); + case TreeItem::Padding: + return QObject::tr("Padding"); + case TreeItem::File: + return QObject::tr("File"); + case TreeItem::Section: + return QObject::tr("Section"); + default: + return QObject::tr("Unknown"); + } +} + +QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) +{ + switch (type) { + case TreeItem::Root: + case TreeItem::Descriptor: + case TreeItem::Padding: + case TreeItem::Volume: + return ""; + case TreeItem::Capsule: + if (subtype == TreeItem::AptioCapsule) + return QObject::tr("Aptio extended"); + else if (subtype == TreeItem::UefiCapsule) + return QObject::tr("UEFI 2.0"); + else + return QObject::tr("Unknown"); + case TreeItem::Region: + return regionTypeToQString(subtype); + case TreeItem::File: + return fileTypeToQString(subtype); + case TreeItem::Section: + return sectionTypeToQString(subtype); + default: + return QObject::tr("Unknown"); + } +} + +QString compressionTypeToQString(UINT8 algorithm) +{ + switch (algorithm) { + case COMPRESSION_ALGORITHM_NONE: + return QObject::tr("None"); + case COMPRESSION_ALGORITHM_EFI11: + return QObject::tr("EFI 1.1"); + case COMPRESSION_ALGORITHM_TIANO: + return QObject::tr("Tiano"); + case COMPRESSION_ALGORITHM_LZMA: + return QObject::tr("LZMA"); + case COMPRESSION_ALGORITHM_IMLZMA: + return QObject::tr("Intel modified LZMA"); + default: + return QObject::tr("Unknown"); + } +} + +TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, + const QString & name, const QString & text, const QString & info, + const QByteArray & header, const QByteArray & body, + TreeItem *parent) +{ + itemAction = NoAction; itemType = type; itemSubtype = subtype; - itemOffset = offset; + itemCompression = compression; itemName = name; - itemTypeName = typeName; - itemSubtypeName = subtypeName; itemText = text; itemInfo = info; itemHeader = header; itemBody = body; parentItem = parent; + + // Set default names + setDefaultNames(); } TreeItem::~TreeItem() @@ -35,19 +109,43 @@ TreeItem::~TreeItem() qDeleteAll(childItems); } +void TreeItem::setDefaultNames() +{ + itemTypeName = itemTypeToQString(itemType); + itemSubtypeName = itemSubtypeToQString(itemType, itemSubtype); +} + void TreeItem::appendChild(TreeItem *item) { childItems.append(item); } -void TreeItem::removeChild(TreeItem *item) +void TreeItem::prependChild(TreeItem *item) { - childItems.removeAll(item); + childItems.prepend(item); +} + +UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem) +{ + int index = childItems.indexOf(item); + if (index == -1) + return ERR_ITEM_NOT_FOUND; + childItems.insert(index, newItem); + return ERR_SUCCESS; +} + +UINT8 TreeItem::insertChildAfter(TreeItem *item, TreeItem *newItem) +{ + int index = childItems.indexOf(item); + if (index == -1) + return ERR_ITEM_NOT_FOUND; + childItems.insert(index + 1, newItem); + return ERR_SUCCESS; } TreeItem *TreeItem::child(int row) { - return childItems.value(row); + return childItems.value(row, NULL); } int TreeItem::childCount() const @@ -57,27 +155,29 @@ int TreeItem::childCount() const int TreeItem::columnCount() const { - return 4; + return 5; } -QString TreeItem::data(int column) const +QVariant TreeItem::data(int column) const { switch(column) { - case 0: //Object + case 0: //Action + if (itemAction == TreeItem::Remove) + return "X"; + if (itemAction == TreeItem::Reconstruct) + return "R"; + return QVariant(); + case 1: //Name return itemName; - break; - case 1: //Type + case 2: //Type return itemTypeName; - break; - case 2: //Subtype + case 3: //Subtype return itemSubtypeName; - break; - case 3: //Text + case 4: //Text return itemText; - break; default: - return ""; + return QVariant(); } } @@ -106,7 +206,7 @@ void TreeItem::setSubtypeName(const QString &text) itemSubtypeName = text; } -QString TreeItem::info() +QString TreeItem::info() const { return itemInfo; } @@ -124,37 +224,46 @@ int TreeItem::row() const return 0; } -UINT8 TreeItem::type() +UINT8 TreeItem::type() const { return itemType; } -UINT8 TreeItem::subtype() +UINT8 TreeItem::subtype() const { return itemSubtype; } -UINT32 TreeItem::offset() +UINT8 TreeItem::compression() const { - return itemOffset; + return itemCompression; } -QByteArray TreeItem::header() +QByteArray TreeItem::header() const { return itemHeader; } -QByteArray TreeItem::body() +QByteArray TreeItem::body() const { return itemBody; } -bool TreeItem::hasEmptyHeader() +bool TreeItem::hasEmptyHeader() const { return itemHeader.isEmpty(); } -bool TreeItem::hasEmptyBody() +bool TreeItem::hasEmptyBody() const { return itemBody.isEmpty(); +} + +UINT8 TreeItem::action() const +{ + return itemAction; +} +void TreeItem::setAction(const UINT8 action) +{ + itemAction = action; } \ No newline at end of file diff --git a/treeitem.h b/treeitem.h index 7bf678b..acbe223 100644 --- a/treeitem.h +++ b/treeitem.h @@ -17,51 +17,107 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include "basetypes.h" +extern QString itemTypeToQString(const UINT8 type); +extern QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype); +extern QString compressionTypeToQString(UINT8 algorithm); + class TreeItem { public: - TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0, const QString & name = QString(), const QString & typeName = QString(), const QString & subtypeName = QString(), - const QString & text = QString(), const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); + // Action types + enum ActionTypes { + NoAction, + Remove, + Reconstruct + }; + + // Item types + enum ItemTypes { + Root, + Capsule, + Descriptor, + Region, + Padding, + Volume, + File, + Section + }; + + // Capsule subtypes + enum CapsuleSubtypes { + AptioCapsule, + UefiCapsule + }; + + // Region subtypes + enum RegionSubtypes { + GbeRegion, + MeRegion, + BiosRegion, + PdrRegion + }; + + // Constructor + TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, + const QString &name = QString(), const QString &text = QString(), const QString &info = QString(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); + // Destructor ~TreeItem(); + // Operations with items void appendChild(TreeItem *item); - void removeChild(TreeItem *item); - + void prependChild(TreeItem *item); + UINT8 insertChildBefore(TreeItem *item, TreeItem *newItem); + UINT8 insertChildAfter(TreeItem *item, TreeItem *newItem); + + // Model support operations TreeItem *child(int row); int childCount() const; int columnCount() const; - QString data(int column) const; + QVariant data(int column) const; int row() const; TreeItem *parent(); - UINT8 type(); - UINT8 subtype(); - UINT32 offset(); - QByteArray header(); - QByteArray body(); - QString info(); - bool hasEmptyHeader(); - bool hasEmptyBody(); + // Reading operations for item parameters + UINT8 type() const; + UINT8 subtype() const; + QByteArray header() const; + bool hasEmptyHeader() const; + QByteArray body() const; + bool hasEmptyBody() const; + QString info() const; + UINT8 compression() const; - void setName(const QString &text); - void setText(const QString &text); + // Actions can also be changed + UINT8 action() const; + void setAction(const UINT8 action); + + // Text values can be changed after item construction void setTypeName(const QString &text); void setSubtypeName(const QString &text); + void setName(const QString &text); + void setText(const QString &text); void setInfo(const QString &text); private: + // Set default names after construction + // They can later be changed by set* methods + void setDefaultNames(); + QList childItems; + UINT8 itemAction; UINT8 itemType; UINT8 itemSubtype; - UINT32 itemOffset; + UINT8 itemCompression; QByteArray itemHeader; QByteArray itemBody; - QString itemName; QString itemTypeName; QString itemSubtypeName; + QString itemName; QString itemText; QString itemInfo; TreeItem *parentItem; diff --git a/treeitemtypes.h b/treeitemtypes.h deleted file mode 100644 index 4cb8fbd..0000000 --- a/treeitemtypes.h +++ /dev/null @@ -1,80 +0,0 @@ -/* treeitemtypes.h - -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, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -*/ - -#ifndef __TREEITEMTYPES_H__ -#define __TREEITEMTYPES_H__ - -// TreeItem types -enum ItemTypes { - RootItem, - CapsuleItem, - DescriptorItem, - RegionItem, - PaddingItem, - VolumeItem, - FileItem, - SectionItem -}; - -// Capsule subtypes -enum CapsuleSubtypes { - AptioCapsule, - UefiCapsule -}; - -// Region subtypes -enum RegionSubtypes { - GbeRegion, - MeRegion, - BiosRegion, - PdrRegion -}; - -// File subtypes -enum FileSubtypes { - RawFile, - FreeformFile, - SecurityCoreFile, - PeiCoreFile, - DxeCoreFile, - PeiModuleFile, - DxeDriverFile, - CombinedPeiDxeFile, - ApplicationFile, - SmmModuleFile, - VolumeImageFile, - CombinedSmmDxeFile, - SmmCoreFile, - PadFile = 0xF0 -}; - -enum SectionSubtypes { - CompressionSection = 0x01, - GuidDefinedSection, - DisposableSection, - Pe32ImageSection = 0x10, - PicImageSection, - TeImageSection, - DxeDepexSection, - VersionSection, - UserInterfaceSection, - Compatibility16Section, - VolumeImageSection, - FreeformSubtypeGuidSection, - RawSection, - PeiDepexSection = 0x1B, - SmmDepexSection -}; - - -#endif \ No newline at end of file diff --git a/treemodel.cpp b/treemodel.cpp index 1207af5..d7c33ce 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -116,46 +116,84 @@ int TreeModel::rowCount(const QModelIndex &parent) const return parentItem->childCount(); } -bool TreeModel::setItemName(const QString &data, const QModelIndex &index) +UINT8 TreeModel::setItemName(const QString &data, const QModelIndex &index) { if(!index.isValid()) - return false; + return ERR_INVALID_PARAMETER; TreeItem *item = static_cast(index.internalPointer()); item->setName(data); emit dataChanged(index, index); - return true; + return ERR_SUCCESS; } -bool TreeModel::setItemText(const QString &data, const QModelIndex &index) +UINT8 TreeModel::setItemText(const QString &data, const QModelIndex &index) { if(!index.isValid()) - return false; + return ERR_INVALID_PARAMETER; TreeItem *item = static_cast(index.internalPointer()); item->setText(data); emit dataChanged(index, index); - return true; + return ERR_SUCCESS; } -QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT32 offset, const QString & name, const QString & typeName, - const QString & subtypeName, const QString & text, const QString & info, - const QByteArray & header, const QByteArray & body, const QModelIndex & parent) +UINT8 TreeModel::setItemAction(const UINT8 action, const QModelIndex &index) { + if(!index.isValid()) + return ERR_INVALID_PARAMETER; + + TreeItem *item = static_cast(index.internalPointer()); + item->setAction(action); + emit dataChanged(index, index); + return ERR_SUCCESS; +} + +QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, + const QString & name, const QString & text, const QString & info, + const QByteArray & header, const QByteArray & body, const QModelIndex & index, + const UINT8 mode) +{ + TreeItem *item; TreeItem *parentItem; int parentColumn = 0; - if (!parent.isValid()) + if (!index.isValid()) parentItem = rootItem; else { - parentItem = static_cast(parent.internalPointer()); - parentColumn = parent.column(); + if (mode == ADD_MODE_APPEND || mode == ADD_MODE_PREPEND) { + parentItem = static_cast(index.internalPointer()); + parentColumn = index.column(); + } + else { + item = static_cast(index.internalPointer()); + parentItem = item->parent(); + parentColumn = index.parent().column(); + } } - emit layoutAboutToBeChanged(); - TreeItem *item = new TreeItem(type, subtype, offset, name, typeName, subtypeName, text, info, header, body, parentItem); - parentItem->appendChild(item); + TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parentItem); + if (mode == ADD_MODE_APPEND) { + emit layoutAboutToBeChanged(); + parentItem->appendChild(newItem); + } + else if (mode == ADD_MODE_PREPEND) { + emit layoutAboutToBeChanged(); + parentItem->prependChild(newItem); + } + else if (mode == ADD_MODE_INSERT_BEFORE) { + emit layoutAboutToBeChanged(); + parentItem->insertChildBefore(item, newItem); + } + else if (mode == ADD_MODE_INSERT_AFTER) { + emit layoutAboutToBeChanged(); + parentItem->insertChildAfter(item, newItem); + } + else + return QModelIndex(); + emit layoutChanged(); - return createIndex(parentItem->childCount() - 1, parentColumn, item); -} \ No newline at end of file + + return createIndex(newItem->row(), parentColumn, newItem); +} diff --git a/treemodel.h b/treemodel.h index 7bc91cf..f1adfb5 100644 --- a/treemodel.h +++ b/treemodel.h @@ -41,13 +41,15 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; - bool setItemName(const QString &data, const QModelIndex &index); - bool setItemText(const QString &data, const QModelIndex &index); - - QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT32 offset = 0, const QString & name = QString(), - const QString & typeName = QString(), const QString & subtypeName = QString(), const QString & text = QString(), - const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & parent = QModelIndex()); - + UINT8 setItemName(const QString &data, const QModelIndex &index); + UINT8 setItemText(const QString &data, const QModelIndex &index); + UINT8 setItemAction(const UINT8 action, const QModelIndex &index); + + QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, + const QString & name = QString(), const QString & text = QString(), const QString & info = QString(), + const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & index = QModelIndex(), + const UINT8 mode = ADD_MODE_APPEND); + private: TreeItem *rootItem; }; diff --git a/uefitool.cpp b/uefitool.cpp index cea9158..cd1df49 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -23,9 +23,15 @@ UEFITool::UEFITool(QWidget *parent) : //Connect connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); - connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(saveAll())); - connect(ui->actionExtractBody, SIGNAL(triggered()), this, SLOT(saveBody())); - connect(ui->actionExtractUncompressed, SIGNAL(triggered()), this, SLOT(saveUncompressedFile())); + connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(extract())); + connect(ui->actionExtractBody, SIGNAL(triggered()), this, SLOT(extractBody())); + connect(ui->actionExtractUncompressed, SIGNAL(triggered()), this, SLOT(extractUncompressed())); + connect(ui->actionInsertInto, SIGNAL(triggered()), this, SLOT(insertInto())); + connect(ui->actionInsertBefore, SIGNAL(triggered()), this, SLOT(insertBefore())); + connect(ui->actionInsertAfter, SIGNAL(triggered()), this, SLOT(insertAfter())); + connect(ui->actionReplace, SIGNAL(triggered()), this, SLOT(replace())); + connect(ui->actionRemove, SIGNAL(triggered()), this, SLOT(remove())); + connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); // Enable Drag-and-Drop actions this->setAcceptDrops(true); @@ -50,10 +56,11 @@ void UEFITool::init() ui->actionExtractBody->setDisabled(true); ui->actionExtractUncompressed->setDisabled(true); ui->actionReplace->setDisabled(true); - ui->actionDelete->setDisabled(true); - ui->actionReplaceWithPadding->setDisabled(true); + ui->actionRemove->setDisabled(true); + ui->actionInsertInto->setDisabled(true); ui->actionInsertBefore->setDisabled(true); ui->actionInsertAfter->setDisabled(true); + ui->actionSaveImageFile->setDisabled(true); // Make new ffsEngine ffsEngine = new FfsEngine(this); @@ -64,7 +71,6 @@ void UEFITool::init() connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(populateUi(const QModelIndex &))); - resizeTreeViewColums(); } void UEFITool::populateUi(const QModelIndex ¤t/*, const QModelIndex &previous*/) @@ -75,9 +81,151 @@ void UEFITool::populateUi(const QModelIndex ¤t/*, const QModelIndex &previ ui->actionExtract->setDisabled(ffsEngine->hasEmptyBody(current) && ffsEngine->hasEmptyHeader(current)); ui->actionExtractBody->setDisabled(ffsEngine->hasEmptyHeader(current)); ui->actionExtractUncompressed->setEnabled(ffsEngine->isCompressedFile(current)); + ui->actionRemove->setEnabled(ffsEngine->isOfType(TreeItem::Volume, current) + || ffsEngine->isOfType(TreeItem::File, current) + || ffsEngine->isOfType(TreeItem::Section, current)); + ui->actionInsertInto->setEnabled(ffsEngine->isOfType(TreeItem::Volume, current)); + ui->actionInsertBefore->setEnabled(ffsEngine->isOfType(TreeItem::File, current)); + ui->actionInsertAfter->setEnabled(ffsEngine->isOfType(TreeItem::File, current)); + ui->actionReplace->setEnabled(ffsEngine->isOfType(TreeItem::File, current)); } -void UEFITool::resizeTreeViewColums(/*const QModelIndex &index*/) +void UEFITool::remove() +{ + UINT8 result = ffsEngine->remove(currentIndex); + if (result) { + ui->debugEdit->setPlainText(ffsEngine->message()); + } + else + ui->actionSaveImageFile->setEnabled(true); + resizeTreeViewColums(); + +} + +void UEFITool::insertInto() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"),".","FFS file (*.ffs *.bin);;All files (*.*)"); + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) + { + ui->statusBar->showMessage(tr("Please select existing FFS file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for reading")); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + UINT8 result = ffsEngine->insert(currentIndex, buffer, TreeItem::File, ADD_MODE_PREPEND); + if (result) + ui->statusBar->showMessage(tr("FFS file can't be inserted (%1)").arg(result)); + else + ui->actionSaveImageFile->setEnabled(true); + resizeTreeViewColums(); +} + +void UEFITool::insertBefore() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"),".","FFS file (*.ffs *.bin);;All files (*.*)"); + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) + { + ui->statusBar->showMessage(tr("Please select existing FFS file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for reading")); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + UINT8 result = ffsEngine->insert(currentIndex, buffer, TreeItem::File, ADD_MODE_INSERT_BEFORE); + if (result) + ui->statusBar->showMessage(tr("FFS file can't be inserted (%1)").arg(result)); + else + ui->actionSaveImageFile->setEnabled(true); + resizeTreeViewColums(); +} + +void UEFITool::insertAfter() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"),".","FFS file (*.ffs *.bin);;All files (*.*)"); + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) + { + ui->statusBar->showMessage(tr("Please select existing FFS file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for reading")); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + UINT8 result = ffsEngine->insert(currentIndex, buffer, TreeItem::File, ADD_MODE_INSERT_AFTER); + if (result) + ui->statusBar->showMessage(tr("FFS file can't be inserted (%1)").arg(result)); + else + ui->actionSaveImageFile->setEnabled(true); + resizeTreeViewColums(); +} + +void UEFITool::replace() +{ + remove(); + insertAfter(); +} + +void UEFITool::saveImageFile() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"),".","BIOS image file (*.rom *.bin *.cap *.fd *.fwh);;All files (*.*)"); + + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::WriteOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for writing")); + return; + } + + QByteArray reconstructed; + UINT8 result = ffsEngine->reconstructImage(reconstructed); + if (result) + { + ui->statusBar->showMessage(tr("Reconstruction failed (%1)").arg(result)); + ui->debugEdit->setPlainText(ffsEngine->message()); + return; + } + + outputFile.write(reconstructed); + outputFile.close(); + ui->statusBar->showMessage(tr("Reconstructed image written")); + ui->debugEdit->setPlainText(ffsEngine->message()); +} + +void UEFITool::resizeTreeViewColums() { int count = ffsEngine->model()->columnCount(); for(int i = 0; i < count; i++) @@ -95,7 +243,7 @@ void UEFITool::openImageFile(QString path) QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) { - ui->statusBar->showMessage(tr("Please select existing BIOS image file.")); + ui->statusBar->showMessage(tr("Please select existing BIOS image file")); return; } @@ -104,7 +252,7 @@ void UEFITool::openImageFile(QString path) if (!inputFile.open(QFile::ReadOnly)) { - ui->statusBar->showMessage(tr("Can't open file for reading. Check file permissions.")); + ui->statusBar->showMessage(tr("Can't open file for reading")); return; } @@ -119,10 +267,11 @@ void UEFITool::openImageFile(QString path) ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); ui->debugEdit->appendPlainText(ffsEngine->message()); + resizeTreeViewColums(); } -void UEFITool::saveAll() +void UEFITool::extract() { QString path = QFileDialog::getSaveFileName(this, tr("Save selected item to binary file"),".","Binary files (*.bin);;All files (*.*)"); @@ -138,7 +287,7 @@ void UEFITool::saveAll() outputFile.close(); } -void UEFITool::saveBody() +void UEFITool::extractBody() { QString path = QFileDialog::getSaveFileName(this, tr("Save selected item without header to binary file"),".","Binary files (*.bin);;All files (*.*)"); @@ -154,7 +303,7 @@ void UEFITool::saveBody() outputFile.close(); } -void UEFITool::saveUncompressedFile() +void UEFITool::extractUncompressed() { QString path = QFileDialog::getSaveFileName(this, tr("Save selected FFS file as uncompressed to binary file"),".","FFS files (*.ffs);;All files (*.*)"); @@ -166,7 +315,7 @@ void UEFITool::saveUncompressedFile() return; } - outputFile.write(ffsEngine->uncompressFile(currentIndex)); + outputFile.write(ffsEngine->decompressFile(currentIndex)); outputFile.close(); } diff --git a/uefitool.h b/uefitool.h index 1d0d771..85df7e8 100644 --- a/uefitool.h +++ b/uefitool.h @@ -46,11 +46,17 @@ public: private slots: void init(); void openImageFile(); - void populateUi(const QModelIndex ¤t/*, const QModelIndex &previous*/); - void resizeTreeViewColums(/*const QModelIndex &index*/); - void saveAll(); - void saveBody(); - void saveUncompressedFile(); + void saveImageFile(); + void populateUi(const QModelIndex ¤t); + void resizeTreeViewColums(); + void extract(); + void extractBody(); + void extractUncompressed(); + void insertInto(); + void insertBefore(); + void insertAfter(); + void replace(); + void remove(); private: Ui::UEFITool * ui; diff --git a/uefitool.pro b/uefitool.pro index bc6a2a7..7d058fe 100644 --- a/uefitool.pro +++ b/uefitool.pro @@ -8,7 +8,7 @@ SOURCES += main.cpp \ uefitool.cpp \ descriptor.cpp \ ffs.cpp \ - ffsengine.cpp \ + ffsengine.cpp \ treeitem.cpp \ treemodel.cpp \ LZMA/LzmaCompress.c \ @@ -22,12 +22,11 @@ SOURCES += main.cpp \ HEADERS += uefitool.h \ basetypes.h \ descriptor.h \ - gbe.h \ - me.h \ + gbe.h \ + me.h \ ffs.h \ - ffsengine.h \ + ffsengine.h \ treeitem.h \ - treeitemtypes.h \ treemodel.h \ LZMA/LzmaCompress.h \ LZMA/LzmaDecompress.h \ diff --git a/uefitool.ui b/uefitool.ui index 02df6a3..a611da3 100644 --- a/uefitool.ui +++ b/uefitool.ui @@ -7,7 +7,7 @@ 0 0 900 - 700 + 600 @@ -20,7 +20,7 @@ true - UEFITool 0.3.0 + UEFITool 0.4.0 @@ -30,6 +30,12 @@ + + 5 + + + 5 + @@ -42,6 +48,12 @@ Structure + + 5 + + + 5 + @@ -80,7 +92,7 @@ - 216 + 220 16777215 @@ -89,7 +101,10 @@ - 3 + 5 + + + 5 @@ -99,12 +114,6 @@ 0 - - - 200 - 16777215 - - Consolas @@ -137,6 +146,12 @@ Debug + + 5 + + + 5 + @@ -148,7 +163,7 @@ 16777215 - 100 + 80 @@ -194,18 +209,19 @@ false + + - - + @@ -213,7 +229,7 @@ false - Insert after + Insert after... Insert an object from file after selected object @@ -227,10 +243,10 @@ false - Insert before + Insert before... - Insert an object from file before selected object + Insert object from file before selected object Ctrl+Shift+I @@ -255,7 +271,7 @@ false - Extract + Extract... Extract selected object to file @@ -269,7 +285,7 @@ false - Extract body + Extract body... Extract selected object without header to file @@ -283,7 +299,7 @@ false - Extract uncompressed + Extract uncompressed... Extract selected FFS file uncompressing it @@ -292,37 +308,23 @@ Ctrl+Alt+E - + false - Delete + Remove - Delete selected object + Remove selected object - Ctrl+D - - - - - false - - - Replace with padding - - - Delete an object by replacing it with padding - - - Ctrl+Shift+D + Ctrl+Del - Open image file + Open image file... Open image file @@ -331,6 +333,34 @@ Ctrl+O + + + false + + + Insert into... + + + Insert object from file into selected object + + + Ctrl+Ins + + + + + false + + + Save image file... + + + Save modified image file + + + Ctrl+S + +