Version 0.6.0

- Debug window upgrade
- Volume grow for non-root volumes
- Minor bugfix
This commit is contained in:
Nikolaj Schlej 2013-11-15 11:48:14 +01:00
parent 6ff5119048
commit 3c90bc4a61
12 changed files with 368 additions and 200 deletions

47
debuglistitem.cpp Normal file
View File

@ -0,0 +1,47 @@
/* debuglistitem.cpp
Copyright (c) 2013, Nikolaj Schlej. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include "debuglistitem.h"
DebugListItem::DebugListItem(QListWidget * parent, int type, const QModelIndex & index)
: QListWidgetItem(parent, type)
{
itemIndex = index;
}
DebugListItem::DebugListItem(const QString & text, QListWidget * parent, int type, const QModelIndex & index)
: QListWidgetItem(text, parent, type)
{
itemIndex = index;
}
DebugListItem::DebugListItem(const QIcon & icon, const QString & text, QListWidget * parent, int type, const QModelIndex & index)
: QListWidgetItem(icon, text, parent, type)
{
itemIndex = index;
}
DebugListItem::~DebugListItem()
{
}
QModelIndex DebugListItem::index() const
{
return itemIndex;
}
void DebugListItem::setIndex(QModelIndex & index)
{
itemIndex = index;
}

37
debuglistitem.h Normal file
View File

@ -0,0 +1,37 @@
/* debuglistitem.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 __DEBUGLISTITEM_H__
#define __DEBUGLISTITEM_H__
#include <QModelIndex>
#include <QListWidgetItem>
#include "basetypes.h"
class DebugListItem : public QListWidgetItem
{
public:
DebugListItem(QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex());
DebugListItem(const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex());
DebugListItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex());
~DebugListItem();
QModelIndex index() const;
void setIndex(QModelIndex & index);
private:
QModelIndex itemIndex;
};
#endif

View File

@ -46,14 +46,14 @@ TreeModel* FfsEngine::model() const
return treeModel; return treeModel;
} }
QString FfsEngine::message() const void FfsEngine::msg(const QString & message, const QModelIndex index)
{ {
return text; debugItems.enqueue(new DebugListItem(message, NULL, 0, index));
} }
void FfsEngine::msg(const QString & message) QQueue<DebugListItem*> FfsEngine::debugMessage()
{ {
text.append(message).append("\n"); return debugItems;
} }
QByteArray FfsEngine::header(const QModelIndex& index) const QByteArray FfsEngine::header(const QModelIndex& index) const
@ -148,9 +148,11 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
{ {
UINT32 capsuleHeaderSize = 0; UINT32 capsuleHeaderSize = 0;
FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL; FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL;
QModelIndex index;
QByteArray flashImage; QByteArray flashImage;
QByteArray bios; QByteArray bios;
QModelIndex index; QByteArray header;
QByteArray body;
QString name; QString name;
QString info; QString info;
@ -166,8 +168,8 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
// Get info // Get info
EFI_CAPSULE_HEADER* capsuleHeader = (EFI_CAPSULE_HEADER*) buffer.constData(); EFI_CAPSULE_HEADER* capsuleHeader = (EFI_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = capsuleHeader->HeaderSize; capsuleHeaderSize = capsuleHeader->HeaderSize;
QByteArray header = buffer.left(capsuleHeaderSize); header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); body = buffer.right(buffer.size() - capsuleHeaderSize);
name = tr("UEFI capsule"); name = tr("UEFI capsule");
info = tr("Header size: %1\nFlags: %2\nImage size: %3") info = tr("Header size: %1\nFlags: %2\nImage size: %3")
.arg(capsuleHeader->HeaderSize, 8, 16, QChar('0')) .arg(capsuleHeader->HeaderSize, 8, 16, QChar('0'))
@ -182,8 +184,8 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
// Get info // Get info
APTIO_CAPSULE_HEADER* aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) buffer.constData(); APTIO_CAPSULE_HEADER* aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) buffer.constData();
capsuleHeaderSize = aptioCapsuleHeader->RomImageOffset; capsuleHeaderSize = aptioCapsuleHeader->RomImageOffset;
QByteArray header = buffer.left(capsuleHeaderSize); header = buffer.left(capsuleHeaderSize);
QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); body = buffer.right(buffer.size() - capsuleHeaderSize);
name = tr("AMI Aptio capsule"); name = tr("AMI Aptio capsule");
info = tr("Header size: %1\nFlags: %2\nImage size: %3") info = tr("Header size: %1\nFlags: %2\nImage size: %3")
.arg(aptioCapsuleHeader->RomImageOffset, 4, 16, QChar('0')) .arg(aptioCapsuleHeader->RomImageOffset, 4, 16, QChar('0'))
@ -193,6 +195,14 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
// Add tree item // Add tree item
index = treeModel->addItem(TreeItem::Capsule, TreeItem::AptioCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); index = treeModel->addItem(TreeItem::Capsule, TreeItem::AptioCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body);
} }
else {
// Add tree item
name = tr("UEFI image");
info = tr("Size: %1")
.arg(buffer.size(), 8, 16, QChar('0'));
// Add tree item
index = treeModel->addItem(TreeItem::Image, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), buffer);
}
// Skip capsule header to have flash chip image // Skip capsule header to have flash chip image
flashImage = buffer.right(buffer.size() - capsuleHeaderSize); flashImage = buffer.right(buffer.size() - capsuleHeaderSize);
@ -230,7 +240,7 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer)
.arg(descriptorMap->NumberOfIccTableEntries); .arg(descriptorMap->NumberOfIccTableEntries);
//!TODO: more info about descriptor //!TODO: more info about descriptor
// Add tree item // Add tree item
index = treeModel->addItem(TreeItem::Descriptor, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); index = treeModel->addItem(TreeItem::Descriptor, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, index);
// Parse regions // Parse regions
QModelIndex regionIndex; QModelIndex regionIndex;
@ -284,26 +294,26 @@ UINT8 FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype,
// Check region base to be in buffer // Check region base to be in buffer
if (regionOffset >= flashImageSize) if (regionOffset >= flashImageSize)
{ {
msg(tr("parseRegion: %1 region stored in descriptor not found").arg(regionName)); msg(tr("parseRegion: %1 region stored in descriptor not found").arg(regionName), parent);
if (twoChips) if (twoChips)
msg(tr("Two flash chips installed, so it could be in another flash chip\n" msg(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName)); "Make a dump from another flash chip and open it to view information about %1 region").arg(regionName), parent);
else else
msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump")); msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"), parent);
msg(tr("Absence of %1 region assumed").arg(regionName)); msg(tr("Absence of %1 region assumed").arg(regionName), parent);
return ERR_INVALID_REGION; return ERR_INVALID_REGION;
} }
// Check region to be fully present in buffer // Check region to be fully present in buffer
else if (regionOffset + regionSize > flashImageSize) else if (regionOffset + regionSize > flashImageSize)
{ {
msg(tr("parseRegion: %1 region stored in descriptor overlaps the end of opened file").arg(regionName)); msg(tr("parseRegion: %1 region stored in descriptor overlaps the end of opened file").arg(regionName), parent);
if (twoChips) if (twoChips)
msg(tr("Two flash chips installed, so it could be in another flash chip\n" msg(tr("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %1 region").arg(regionName)); "Make a dump from another flash chip and open it to view information about %1 region").arg(regionName), parent);
else else
msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump")); msg(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump"), parent);
msg(tr("Absence of %1 region assumed\n").arg(regionName)); msg(tr("Absence of %1 region assumed\n").arg(regionName), parent);
return ERR_INVALID_REGION; return ERR_INVALID_REGION;
} }
@ -337,7 +347,7 @@ UINT8 FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype,
meVersionOffset = body.indexOf(ME_VERSION_SIGNATURE); meVersionOffset = body.indexOf(ME_VERSION_SIGNATURE);
if (meVersionOffset < 0){ if (meVersionOffset < 0){
info += tr("\nVersion: unknown"); info += tr("\nVersion: unknown");
msg(tr("parseRegion: ME region version is unknown, it can be damaged")); msg(tr("parseRegion: ME region version is unknown, it can be damaged"), parent);
} }
else { else {
meVersion = (ME_VERSION*) (body.constData() + meVersionOffset); meVersion = (ME_VERSION*) (body.constData() + meVersionOffset);
@ -356,7 +366,7 @@ UINT8 FfsEngine::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype,
break; break;
default: default:
name = tr("Unknown region"); name = tr("Unknown region");
msg(tr("insertInTree: Unknown region")); msg(tr("parseRegion: Unknown region"), parent);
break; break;
} }
@ -415,7 +425,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent)
//Check that volume is fully present in input //Check that volume is fully present in input
if (volumeOffset + volumeSize > (UINT32) bios.size()) { if (volumeOffset + volumeSize > (UINT32) bios.size()) {
msg(tr("parseBios: Volume overlaps the end of input buffer")); msg(tr("parseBios: Volume overlaps the end of input buffer"), parent);
return ERR_INVALID_VOLUME; return ERR_INVALID_VOLUME;
} }
@ -448,7 +458,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent)
|| alignment32 || alignment64 || alignment128 || alignment256 || alignment32 || alignment64 || alignment128 || alignment256
|| alignment512 || alignment1k || alignment2k || alignment4k || alignment512 || alignment1k || alignment2k || alignment4k
|| alignment8k || alignment16k || alignment32k || alignment64k)) || alignment8k || alignment16k || alignment32k || alignment64k))
msg("parseBios: Incompatible revision 1 volume alignment setup"); msg("parseBios: Incompatible revision 1 volume alignment setup", parent);
// Assume that smaller alignment value consumes greater // Assume that smaller alignment value consumes greater
//!TODO: refactor this code //!TODO: refactor this code
@ -488,7 +498,7 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent)
// Check alignment // Check alignment
if (volumeOffset % alignment) { if (volumeOffset % alignment) {
msg(tr("parseBios: Unaligned revision 1 volume")); msg(tr("parseBios: Unaligned revision 1 volume"), parent);
} }
} }
else if (volumeHeader->Revision == 2) { else if (volumeHeader->Revision == 2) {
@ -497,16 +507,16 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent)
// Check alignment // Check alignment
if (volumeOffset % alignment) { if (volumeOffset % alignment) {
msg(tr("parseBios: Unaligned revision 2 volume")); msg(tr("parseBios: Unaligned revision 2 volume"), parent);
} }
} }
else else
msg(tr("parseBios: Unknown volume revision (%1)").arg(volumeHeader->Revision)); msg(tr("parseBios: Unknown volume revision (%1)").arg(volumeHeader->Revision), parent);
// Parse volume // Parse volume
UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), parent); UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), parent);
if (result) if (result)
msg(tr("parseBios: Volume parsing failed (%1)").arg(result)); msg(tr("parseBios: Volume parsing failed (%1)").arg(result), parent);
// Go to next volume // Go to next volume
prevVolumeOffset = volumeOffset; prevVolumeOffset = volumeOffset;
@ -553,7 +563,17 @@ UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UIN
if (QByteArray((const char*) &volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE) if (QByteArray((const char*) &volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE)
return ERR_INVALID_VOLUME; return ERR_INVALID_VOLUME;
volumeSize = volumeHeader->FvLength; // Use BlockMap to determine volume size
EFI_FV_BLOCK_MAP_ENTRY* entry = (EFI_FV_BLOCK_MAP_ENTRY*) (bios.constData() + volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER));
volumeSize = 0;
while(entry->NumBlocks != 0 && entry->Length != 0) {
if ((void*) entry > bios.constData() + bios.size()) {
return ERR_INVALID_VOLUME;
}
volumeSize += entry->NumBlocks * entry->Length;
entry += 1;
}
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -575,7 +595,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
} }
// Other GUID // Other GUID
else { else {
msg(tr("parseBios: Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid))); msg(tr("parseBios: Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid)), parent);
parseCurrentVolume = false; parseCurrentVolume = false;
} }
@ -585,7 +605,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
// Check header checksum by recalculating it // Check header checksum by recalculating it
if (!calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength)) { if (!calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength)) {
msg(tr("parseBios: Volume header checksum is invalid")); msg(tr("parseBios: Volume header checksum is invalid"), parent);
} }
// Check for presence of extended header, only if header revision is greater then 1 // Check for presence of extended header, only if header revision is greater then 1
@ -597,17 +617,33 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
headerSize = volumeHeader->HeaderLength; headerSize = volumeHeader->HeaderLength;
} }
// Get volume size
UINT8 result;
UINT32 volumeSize;
result = getVolumeSize(volume, 0, volumeSize);
if (result)
return result;
// Check reported size
if (volumeSize != volumeHeader->FvLength) {
msg(tr("%1: volume size stored in header %2 differs from calculated size %3")
.arg(guidToQString(volumeHeader->FileSystemGuid))
.arg(volumeHeader->FvLength, 8, 16, QChar('0'))
.arg(volumeSize, 8, 16, QChar('0')), parent);
}
// Get info // Get info
QString name = guidToQString(volumeHeader->FileSystemGuid); QString name = guidToQString(volumeHeader->FileSystemGuid);
QString info = tr("Size: %1\nRevision: %2\nAttributes: %3\nHeader size: %4") QString info = tr("Size: %1\nRevision: %2\nAttributes: %3\nHeader size: %4")
.arg(volumeHeader->FvLength, 8, 16, QChar('0')) .arg(volumeSize, 8, 16, QChar('0'))
.arg(volumeHeader->Revision) .arg(volumeHeader->Revision)
.arg(volumeHeader->Attributes, 8, 16, QChar('0')) .arg(volumeHeader->Attributes, 8, 16, QChar('0'))
.arg(volumeHeader->HeaderLength, 4, 16, QChar('0')); .arg(volumeHeader->HeaderLength, 4, 16, QChar('0'));
// Add tree item // Add tree item
QByteArray header = volume.left(headerSize); QByteArray header = volume.left(headerSize);
QByteArray body = volume.mid(headerSize, volumeHeader->FvLength - headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize);
QModelIndex index = treeModel->addItem(TreeItem::Volume, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); QModelIndex index = treeModel->addItem(TreeItem::Volume, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode);
// Do not parse volumes with unknown FS // Do not parse volumes with unknown FS
@ -617,7 +653,6 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
// Search for and parse all files // Search for and parse all files
UINT32 fileOffset = headerSize; UINT32 fileOffset = headerSize;
UINT32 fileSize; UINT32 fileSize;
UINT8 result;
QQueue<QByteArray> files; QQueue<QByteArray> files;
while (true) { while (true) {
@ -627,7 +662,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
// Check file size to be at least sizeof(EFI_FFS_FILE_HEADER) // Check file size to be at least sizeof(EFI_FFS_FILE_HEADER)
if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) { if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) {
msg(tr("parseVolume: File with invalid size")); msg(tr("parseVolume: File with invalid size"), index);
return ERR_INVALID_FILE; return ERR_INVALID_FILE;
} }
@ -643,12 +678,12 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = pow(2, alignmentPower); UINT32 alignment = pow(2, alignmentPower);
if ((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))); msg(tr("parseVolume: %1, unaligned file").arg(guidToQString(fileHeader->Name)), index);
} }
// Check file GUID // Check file GUID
if (fileHeader->Type != EFI_FV_FILETYPE_PAD && files.indexOf(header.left(sizeof(EFI_GUID))) != -1) if (fileHeader->Type != EFI_FV_FILETYPE_PAD && files.indexOf(header.left(sizeof(EFI_GUID))) != -1)
msg(tr("%1: file with duplicate GUID").arg(guidToQString(fileHeader->Name))); msg(tr("%1: file with duplicate GUID").arg(guidToQString(fileHeader->Name)), index);
// Add file GUID to queue // Add file GUID to queue
files.enqueue(header.left(sizeof(EFI_GUID))); files.enqueue(header.left(sizeof(EFI_GUID)));
@ -656,7 +691,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par
// Parse file // Parse file
result = parseFile(file, volumeHeader->Revision, empty, index); result = parseFile(file, volumeHeader->Revision, empty, index);
if (result) if (result)
msg(tr("parseVolume: Parse FFS file failed (%1)").arg(result)); msg(tr("parseVolume: Parse FFS file failed (%1)").arg(result), index);
// Move to next file // Move to next file
fileOffset += fileSize; fileOffset += fileSize;
@ -693,7 +728,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
msg(tr("parseVolume: %1, stored header checksum %2 differs from calculated %3") msg(tr("parseVolume: %1, stored header checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name)) .arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.Header, 2, 16, QChar('0')) .arg(fileHeader->IntegrityCheck.Checksum.Header, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0'))); .arg(calculated, 2, 16, QChar('0')), parent);
} }
// Check data checksum // Check data checksum
@ -708,7 +743,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
msg(tr("parseVolume: %1, stored data checksum %2 differs from calculated %3") msg(tr("parseVolume: %1, stored data checksum %2 differs from calculated %3")
.arg(guidToQString(fileHeader->Name)) .arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0')) .arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0'))
.arg(calculated, 2, 16, QChar('0'))); .arg(calculated, 2, 16, QChar('0')), parent);
} }
} }
// Data checksum must be one of predefined values // Data checksum must be one of predefined values
@ -716,7 +751,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) { 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") msg(tr("parseVolume: %1, stored data checksum %2 differs from standard value")
.arg(guidToQString(fileHeader->Name)) .arg(guidToQString(fileHeader->Name))
.arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0'))); .arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0')), parent);
} }
} }
@ -732,7 +767,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
msg(tr("parseVolume: %1, file tail value %2 is not a bitwise not of %3 stored in file header") msg(tr("parseVolume: %1, file tail value %2 is not a bitwise not of %3 stored in file header")
.arg(guidToQString(fileHeader->Name)) .arg(guidToQString(fileHeader->Name))
.arg(*tail, 4, 16, QChar('0')) .arg(*tail, 4, 16, QChar('0'))
.arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0'))); .arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0')), parent);
// Remove tail from file body // Remove tail from file body
body = body.left(body.size() - sizeof(UINT16)); body = body.left(body.size() - sizeof(UINT16));
@ -782,7 +817,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
break; break;
default: default:
parseCurrentFile = false; parseCurrentFile = false;
msg(tr("parseVolume: Unknown file type (%1)").arg(fileHeader->Type, 2, 16, QChar('0'))); msg(tr("parseVolume: Unknown file type (%1)").arg(fileHeader->Type, 2, 16, QChar('0')), parent);
}; };
// Check for empty file // Check for empty file
@ -815,7 +850,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const char e
if (parseAsBios) { if (parseAsBios) {
result = parseBios(body, index); result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND) if (result && result != ERR_VOLUMES_NOT_FOUND)
msg(tr("parseVolume: Parse file as BIOS failed (%1)").arg(result)); msg(tr("parseVolume: Parse file as BIOS failed (%1)").arg(result), index);
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -874,8 +909,8 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
QByteArray header; QByteArray header;
QByteArray body; QByteArray body;
UINT32 headerSize; UINT32 headerSize;
QModelIndex index;
UINT8 result; UINT8 result;
QModelIndex index;
switch (sectionHeader->Type) { switch (sectionHeader->Type) {
// Encapsulated sections // Encapsulated sections
@ -891,7 +926,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
// Decompress section // Decompress section
result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm);
if (result) { if (result) {
msg(tr("parseFile: Section decompression failed (%1)").arg(result)); msg(tr("parseFile: Section decompression failed (%1)").arg(result), parent);
parseCurrentSection = false; parseCurrentSection = false;
} }
@ -933,7 +968,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
if (result) { if (result) {
result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, decompressed, &algorithm); result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, decompressed, &algorithm);
if (result) { if (result) {
msg(tr("parseFile: GUID defined section can not be decompressed (%1)").arg(result)); msg(tr("parseFile: GUID defined section can not be decompressed (%1)").arg(result), parent);
parseCurrentSection = false; parseCurrentSection = false;
} }
} }
@ -1035,7 +1070,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
// Parse section body as BIOS space // Parse section body as BIOS space
result = parseBios(body, index); result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND) { if (result && result != ERR_VOLUMES_NOT_FOUND) {
msg(tr("parseFile: Firmware volume image can not be parsed (%1)").arg(result)); msg(tr("parseFile: Firmware volume image can not be parsed (%1)").arg(result), index);
return result; return result;
} }
break; break;
@ -1054,12 +1089,11 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
// Parse section body as BIOS space // Parse section body as BIOS space
result = parseBios(body, index); result = parseBios(body, index);
if (result && result != ERR_VOLUMES_NOT_FOUND) { if (result && result != ERR_VOLUMES_NOT_FOUND) {
msg(tr("parseFile: Raw section can not be parsed as BIOS (%1)").arg(result)); msg(tr("parseFile: Raw section can not be parsed as BIOS (%1)").arg(result), index);
return result; return result;
} }
break; break;
default: default:
msg(tr("parseFile: Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0')));
header = section.left(sizeof(EFI_COMMON_SECTION_HEADER)); header = section.left(sizeof(EFI_COMMON_SECTION_HEADER));
body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER)); body = section.mid(sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER));
// Get info // Get info
@ -1069,7 +1103,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const UINT8 revision,
// Add tree item // Add tree item
index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); index = treeModel->addItem(TreeItem::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode);
return ERR_UNKNOWN_SECTION; msg(tr("parseFile: Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0')), index);
} }
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -1091,7 +1125,7 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
// Parent type must be volume // Parent type must be volume
TreeItem * parentItem = static_cast<TreeItem*>(parent.internalPointer()); TreeItem * parentItem = static_cast<TreeItem*>(parent.internalPointer());
if (parentItem->type() != TreeItem::Volume) { if (parentItem->type() != TreeItem::Volume) {
msg(tr("insert: file can't be inserted into something that is not volume")); msg(tr("insert: file can't be inserted into something that is not volume"), parent);
return ERR_INVALID_VOLUME; return ERR_INVALID_VOLUME;
} }
@ -1122,7 +1156,7 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
parentItem->subtype() == EFI_SECTION_DISPOSABLE))) { parentItem->subtype() == EFI_SECTION_DISPOSABLE))) {
QModelIndex volumeIndex = findParentOfType(TreeItem::Volume, parent); QModelIndex volumeIndex = findParentOfType(TreeItem::Volume, parent);
if (!volumeIndex.isValid()) { if (!volumeIndex.isValid()) {
msg(tr("insert: Parent volume not found")); msg(tr("insert: Parent volume not found"), parent);
return ERR_INVALID_VOLUME; return ERR_INVALID_VOLUME;
} }
@ -1139,7 +1173,7 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
treeModel->setItemAction(TreeItem::Reconstruct, parent); treeModel->setItemAction(TreeItem::Reconstruct, parent);
} }
else { else {
msg(tr("insert: section can't be inserted into something that is not file or encapsulation section")); msg(tr("insert: section can't be inserted into something that is not file or encapsulation section"), parent);
return ERR_INVALID_FILE; return ERR_INVALID_FILE;
} }
} }
@ -1164,8 +1198,6 @@ UINT8 FfsEngine::remove(const QModelIndex & index)
return ERR_SUCCESS; return ERR_SUCCESS;
} }
// Compression routines // Compression routines
UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm) UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm)
{ {
@ -1203,7 +1235,9 @@ UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compr
scratch = new UINT8[scratchSize]; scratch = new UINT8[scratchSize];
// Decompress section data // Decompress section data
// Try EFI1.1 decompression first
if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) {
// Not EFI 1.1, try Tiano
if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) {
if (algorithm) if (algorithm)
*algorithm = COMPRESSION_ALGORITHM_UNKNOWN; *algorithm = COMPRESSION_ALGORITHM_UNKNOWN;
@ -1212,9 +1246,34 @@ UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compr
else if (algorithm) else if (algorithm)
*algorithm = COMPRESSION_ALGORITHM_TIANO; *algorithm = COMPRESSION_ALGORITHM_TIANO;
} }
else if (algorithm) else {
// Possible EFI 1.1
// Try decompressing it as Tiano
UINT8* tianoDecompressed = new UINT8[decompressedSize];
UINT8* tianoScratch = new UINT8[scratchSize];
if (ERR_SUCCESS != TianoDecompress(data, dataSize, tianoDecompressed, decompressedSize, tianoScratch, scratchSize)) {
// Not Tiano, definitely EFI 1.1
if (algorithm)
*algorithm = COMPRESSION_ALGORITHM_EFI11; *algorithm = COMPRESSION_ALGORITHM_EFI11;
}
else {
// Both algorithms work
if(memcmp(decompressed, tianoDecompressed, decompressedSize)) {
// If decompressed data are different - it's Tiano for sure
delete decompressed;
delete scratch;
decompressed = tianoDecompressed;
scratch = tianoScratch;
if (algorithm)
*algorithm = COMPRESSION_ALGORITHM_TIANO;
}
else {
// Data are same - it's EFI 1.1
if (algorithm)
*algorithm = COMPRESSION_ALGORITHM_EFI11;
}
}
}
decompressedData = QByteArray((const char*) decompressed, decompressedSize); decompressedData = QByteArray((const char*) decompressed, decompressedSize);
// Free allocated memory // Free allocated memory
@ -1352,8 +1411,6 @@ UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, QByteA
} }
} }
// Construction routines // Construction routines
UINT8 FfsEngine::reconstructImage(QByteArray & reconstructed) UINT8 FfsEngine::reconstructImage(QByteArray & reconstructed)
{ {
@ -1380,7 +1437,7 @@ UINT8 FfsEngine::constructPadFile(const UINT32 size, const UINT8 revision, const
uint32ToUint24(size, header->Size); uint32ToUint24(size, header->Size);
header->Attributes = 0x00; header->Attributes = 0x00;
header->Type = EFI_FV_FILETYPE_PAD; header->Type = EFI_FV_FILETYPE_PAD;
header->State = 0xF8; header->State = 0xF8; //TODO: Check state of pad file in section with empty = 0x00
// Calculate header checksum // Calculate header checksum
header->IntegrityCheck.Checksum.Header = 0; header->IntegrityCheck.Checksum.Header = 0;
header->IntegrityCheck.Checksum.File = 0; header->IntegrityCheck.Checksum.File = 0;
@ -1451,6 +1508,7 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
if (item->subtype() == TreeItem::AptioCapsule) if (item->subtype() == TreeItem::AptioCapsule)
msg(tr("reconstruct: Aptio extended header checksum and signature are now invalid")); msg(tr("reconstruct: Aptio extended header checksum and signature are now invalid"));
case TreeItem::Root: case TreeItem::Root:
case TreeItem::Image:
case TreeItem::Descriptor: case TreeItem::Descriptor:
case TreeItem::Region: case TreeItem::Region:
case TreeItem::Padding: case TreeItem::Padding:
@ -1491,7 +1549,7 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
return result; return result;
} }
// Remove all pad files, which will be recreated later // Remove all pad files, they will be recreated later
foreach(const QByteArray & child, childrenQueue) foreach(const QByteArray & child, childrenQueue)
{ {
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) child.constData(); EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) child.constData();
@ -1499,6 +1557,12 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
childrenQueue.removeAll(child); childrenQueue.removeAll(child);
} }
// Get volume size
UINT32 volumeSize;
result = getVolumeSize(header, 0, volumeSize);
if (result)
return result;
// Construct new volume body // Construct new volume body
UINT32 offset = 0; UINT32 offset = 0;
while (!childrenQueue.isEmpty()) while (!childrenQueue.isEmpty())
@ -1539,14 +1603,14 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
offset += size; offset += size;
} }
// If this is a last file in volume // If this is the last file in volume
if (childrenQueue.isEmpty()) if (childrenQueue.isEmpty())
{ {
// Last file of the volume can be Volume Top File // Last file of the volume can be Volume Top File
if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) {
// Determine correct VTF offset // Determine correct VTF offset
UINT32 vtfOffset = volumeHeader->FvLength - header.size() - file.size(); UINT32 vtfOffset = volumeSize - header.size() - file.size();
if (offset % 8) { if (vtfOffset % 8) {
msg(tr("reconstruct: %1: Wrong size of Volume Top File") msg(tr("reconstruct: %1: Wrong size of Volume Top File")
.arg(guidToQString(volumeHeader->FileSystemGuid))); .arg(guidToQString(volumeHeader->FileSystemGuid)));
return ERR_INVALID_FILE; return ERR_INVALID_FILE;
@ -1563,24 +1627,62 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
// Append constructed pad file to volume body // Append constructed pad file to volume body
reconstructed.append(pad); reconstructed.append(pad);
offset = vtfOffset; offset = vtfOffset;
// Ensure that no more files will be in this volume
childrenQueue.clear();
} }
// No more space left in volume // No more space left in volume
else if (vtfOffset < offset) { else if (vtfOffset < offset) {
//!TODO: attempt volume grow // Check if volume can be grown
msg(tr("reconstruct: %1: can't insert VTF, need additional %2 bytes") UINT8 parentType = item->parent()->type();
.arg(guidToQString(volumeHeader->FileSystemGuid)) if(parentType != TreeItem::File && parentType != TreeItem::Section) {
.arg(offset - vtfOffset, 8, 16, QChar('0'))); msg(tr("%1: can't grow root volume").arg(guidToQString(volumeHeader->FileSystemGuid)));
return ERR_INVALID_VOLUME; return ERR_INVALID_VOLUME;
} }
// Grow volume to fit VTF
UINT32 newSize = volumeSize + (offset - vtfOffset) + sizeof(EFI_FFS_FILE_HEADER);
result = growVolume(header, volumeSize, newSize);
if (result)
return result;
// Determine new VTF offset
vtfOffset = newSize - header.size() - file.size();
if (vtfOffset % 8) {
msg(tr("reconstruct: %1: Wrong size of Volume Top File")
.arg(guidToQString(volumeHeader->FileSystemGuid)));
return ERR_INVALID_FILE;
} }
// Fill all following bytes with empty char // Construct pad file
QByteArray pad;
result = constructPadFile(vtfOffset - offset, revision, empty, pad);
if (result)
return result;
// Append constructed pad file to volume body
reconstructed.append(pad);
reconstructed.append(file);
volumeSize = newSize;
break;
}
}
// Append last file and fill the rest with empty char
else { else {
INT32 size = volumeHeader->FvLength - header.size() - offset - file.size(); reconstructed.append(file);
// Append fill UINT32 volumeBodySize = volumeSize - header.size();
if (size > 0) if (volumeBodySize > (UINT32) reconstructed.size()) {
file.append(QByteArray(size, empty)); // Fill volume end with empty char
reconstructed.append(QByteArray(volumeBodySize - reconstructed.size(), empty));
}
else {
// Check if volume can be grown
UINT8 parentType = item->parent()->type();
if(parentType != TreeItem::File && parentType != TreeItem::Section) {
msg(tr("%1: can't grow root volume").arg(guidToQString(volumeHeader->FileSystemGuid)));
return ERR_INVALID_VOLUME;
}
// Grow volume to fit new body
UINT32 newSize = header.size() + reconstructed.size();
result = growVolume(header, volumeSize, newSize);
// Fill volume end with empty char
reconstructed.append(QByteArray(newSize - header.size() - reconstructed.size(), empty));
volumeSize = newSize;
}
break;
} }
} }
@ -1590,70 +1692,12 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
offset += file.size(); offset += file.size();
} }
// Check new body size // Check new volume size
if (header.size() + reconstructed.size() > volumeHeader->FvLength) if ((UINT32)(header.size() + reconstructed.size()) > volumeSize)
{ {
//!TODO: attemt volume grow msg(tr("reconstruct: Volume grow failed"));
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; 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 // Use current volume body
else else
@ -1835,7 +1879,37 @@ UINT8 FfsEngine::reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const U
return ERR_NOT_IMPLEMENTED; return ERR_NOT_IMPLEMENTED;
} }
UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize)
{
// Adjust new size to be representable by current FvBlockMap
EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) header.data();
EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*) (header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER));
UINT32 blockMapSize = header.size() - sizeof(EFI_FIRMWARE_VOLUME_HEADER);
UINT32 blockMapCount = blockMapSize / sizeof(EFI_FV_BLOCK_MAP_ENTRY);
// Check blockMap validity
if (blockMap[blockMapCount-1].NumBlocks != 0 || blockMap[blockMapCount-1].Length != 0)
return ERR_INVALID_VOLUME;
// Calculate new size
if (newSize <= size)
return ERR_INVALID_PARAMETER;
newSize += blockMap->Length - newSize % blockMap->Length;
// Recalculate number of blocks
blockMap->NumBlocks = newSize / blockMap->Length;
// Set new volume size
volumeHeader->FvLength = 0;
for(UINT8 i = 0; i < blockMapCount; i++) {
volumeHeader->FvLength += blockMap[i].NumBlocks * blockMap[i].Length;
}
// Recalculate volume header checksum
volumeHeader->Checksum = 0;
volumeHeader->Checksum = calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength);
return ERR_SUCCESS;
}
// Will be refactored later // Will be refactored later
QByteArray FfsEngine::decompressFile(const QModelIndex& index) const QByteArray FfsEngine::decompressFile(const QModelIndex& index) const

View File

@ -21,6 +21,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "basetypes.h" #include "basetypes.h"
#include "treeitem.h" #include "treeitem.h"
#include "treemodel.h" #include "treemodel.h"
#include "debuglistitem.h"
class TreeModel; class TreeModel;
@ -32,10 +33,12 @@ public:
// Default constructor and destructor // Default constructor and destructor
FfsEngine(QObject *parent = 0); FfsEngine(QObject *parent = 0);
~FfsEngine(void); ~FfsEngine(void);
// Returns model for Qt view classes // Returns model for Qt view classes
TreeModel* model() const; TreeModel* model() const;
// Returns current message
QString message() const; // Returns debug items queue
QQueue<DebugListItem*> debugMessage();
// Firmware image parsing // Firmware image parsing
UINT8 parseInputFile(const QByteArray & buffer); UINT8 parseInputFile(const QByteArray & buffer);
@ -59,6 +62,7 @@ public:
UINT8 reconstructImage(QByteArray & reconstructed); UINT8 reconstructImage(QByteArray & reconstructed);
UINT8 constructPadFile(const UINT32 size, const UINT8 revision, const char empty, QByteArray & pad); UINT8 constructPadFile(const UINT32 size, const UINT8 revision, const char empty, QByteArray & pad);
UINT8 reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const UINT8 revision = 2, char empty = '\xFF'); UINT8 reconstruct(TreeItem* item, QQueue<QByteArray> & queue, const UINT8 revision = 2, char empty = '\xFF');
UINT8 growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize);
// Operations on tree items // Operations on tree items
UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 type, const UINT8 mode); UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 type, const UINT8 mode);
@ -80,11 +84,11 @@ public:
QByteArray decompressFile(const QModelIndex & index) const; QByteArray decompressFile(const QModelIndex & index) const;
private: private:
QString text;
TreeItem *rootItem; TreeItem *rootItem;
TreeModel *treeModel; TreeModel *treeModel;
// Adds string to message // Debug window helper
void msg(const QString & message); QQueue<DebugListItem*> debugItems;
void msg(const QString & message, const QModelIndex index = QModelIndex());
// Internal operations used in insertInTree // Internal operations used in insertInTree
bool setTreeItemName(const QString & data, const QModelIndex & index); bool setTreeItemName(const QString & data, const QModelIndex & index);
bool setTreeItemText(const QString & data, const QModelIndex & index); bool setTreeItemText(const QString & data, const QModelIndex & index);

View File

@ -21,10 +21,12 @@ QString itemTypeToQString(const UINT8 type)
switch (type) { switch (type) {
case TreeItem::Root: case TreeItem::Root:
return QObject::tr("Root"); return QObject::tr("Root");
case TreeItem::Image:
return QObject::tr("Image");
case TreeItem::Capsule: case TreeItem::Capsule:
return QObject::tr("Capsule"); return QObject::tr("Capsule");
case TreeItem::Descriptor: case TreeItem::Descriptor:
return QObject::tr("Flash descriptor"); return QObject::tr("Descriptor");
case TreeItem::Region: case TreeItem::Region:
return QObject::tr("Region"); return QObject::tr("Region");
case TreeItem::Volume: case TreeItem::Volume:
@ -44,6 +46,7 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
{ {
switch (type) { switch (type) {
case TreeItem::Root: case TreeItem::Root:
case TreeItem::Image:
case TreeItem::Descriptor: case TreeItem::Descriptor:
case TreeItem::Padding: case TreeItem::Padding:
case TreeItem::Volume: case TreeItem::Volume:

View File

@ -38,6 +38,7 @@ public:
// Item types // Item types
enum ItemTypes { enum ItemTypes {
Root, Root,
Image,
Capsule, Capsule,
Descriptor, Descriptor,
Region, Region,

View File

@ -93,6 +93,9 @@ QModelIndex TreeModel::parent(const QModelIndex &index) const
return QModelIndex(); return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer()); TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
if (childItem == rootItem)
return QModelIndex();
TreeItem *parentItem = childItem->parent(); TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem) if (parentItem == rootItem)

View File

@ -48,8 +48,8 @@ UEFITool::~UEFITool()
void UEFITool::init() void UEFITool::init()
{ {
// Clear UI components // Clear components
ui->debugEdit->clear(); ui->debugListWidget->clear();
ui->infoEdit->clear(); ui->infoEdit->clear();
// Disable all actions except openImageFile // Disable all actions except openImageFile
@ -72,6 +72,7 @@ void UEFITool::init()
connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void)));
connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
this, SLOT(populateUi(const QModelIndex &))); this, SLOT(populateUi(const QModelIndex &)));
connect(ui->debugListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*)));
resizeTreeViewColums(); resizeTreeViewColums();
} }
@ -88,7 +89,10 @@ void UEFITool::populateUi(const QModelIndex &current)
|| ffsEngine->isOfType(TreeItem::Section, current)); || ffsEngine->isOfType(TreeItem::Section, current));
ui->actionInsertInto->setEnabled(ffsEngine->isOfType(TreeItem::Volume, current) ui->actionInsertInto->setEnabled(ffsEngine->isOfType(TreeItem::Volume, current)
|| ffsEngine->isOfType(TreeItem::File, current) || ffsEngine->isOfType(TreeItem::File, current)
|| ffsEngine->isOfType(TreeItem::Section, current)); || (ffsEngine->isOfType(TreeItem::Section, current)
&& (ffsEngine->isOfSubtype(EFI_SECTION_COMPRESSION, current)
|| ffsEngine->isOfSubtype(EFI_SECTION_GUID_DEFINED, current)
|| ffsEngine->isOfSubtype(EFI_SECTION_DISPOSABLE, current))));
ui->actionInsertBefore->setEnabled(ffsEngine->isOfType(TreeItem::File, current) ui->actionInsertBefore->setEnabled(ffsEngine->isOfType(TreeItem::File, current)
|| ffsEngine->isOfType(TreeItem::Section, current)); || ffsEngine->isOfType(TreeItem::Section, current));
ui->actionInsertAfter->setEnabled(ffsEngine->isOfType(TreeItem::File, current) ui->actionInsertAfter->setEnabled(ffsEngine->isOfType(TreeItem::File, current)
@ -100,7 +104,7 @@ void UEFITool::remove()
{ {
UINT8 result = ffsEngine->remove(currentIndex); UINT8 result = ffsEngine->remove(currentIndex);
if (result) { if (result) {
ui->debugEdit->setPlainText(ffsEngine->message());
} }
else else
ui->actionSaveImageFile->setEnabled(true); ui->actionSaveImageFile->setEnabled(true);
@ -115,7 +119,7 @@ void UEFITool::insert(const UINT8 mode)
UINT8 type; UINT8 type;
UINT8 objectType; UINT8 objectType;
if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_BEFORE) if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_AFTER)
type = item->parent()->type(); type = item->parent()->type();
else else
type = item->type(); type = item->type();
@ -199,7 +203,7 @@ void UEFITool::saveImageFile()
if (result) if (result)
{ {
ui->statusBar->showMessage(tr("Reconstruction failed (%1)").arg(result)); ui->statusBar->showMessage(tr("Reconstruction failed (%1)").arg(result));
ui->debugEdit->setPlainText(ffsEngine->message()); showDebugMessage();
return; return;
} }
@ -207,7 +211,7 @@ void UEFITool::saveImageFile()
outputFile.write(reconstructed); outputFile.write(reconstructed);
outputFile.close(); outputFile.close();
ui->statusBar->showMessage(tr("Reconstructed image written")); ui->statusBar->showMessage(tr("Reconstructed image written"));
ui->debugEdit->setPlainText(ffsEngine->message()); showDebugMessage();
} }
void UEFITool::resizeTreeViewColums() void UEFITool::resizeTreeViewColums()
@ -251,8 +255,7 @@ void UEFITool::openImageFile(QString path)
else else
ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName()));
ui->debugEdit->appendPlainText(ffsEngine->message()); showDebugMessage();
resizeTreeViewColums(); resizeTreeViewColums();
} }
@ -319,3 +322,22 @@ void UEFITool::dropEvent(QDropEvent* event)
openImageFile(path); openImageFile(path);
} }
void UEFITool::showDebugMessage()
{
ui->debugListWidget->clear();
QQueue<DebugListItem*> debugItems = ffsEngine->debugMessage();
for (int i = 0; i < debugItems.count(); i++) {
ui->debugListWidget->addItem((QListWidgetItem*) debugItems.at(i));
}
}
void UEFITool::scrollTreeView(QListWidgetItem* item)
{
DebugListItem* debugItem = (DebugListItem*) item;
QModelIndex index = debugItem->index();
if (index.isValid()) {
ui->structureTreeView->scrollTo(index);
ui->structureTreeView->selectionModel()->select(currentIndex, QItemSelectionModel::Clear);
ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select);
}
}

View File

@ -27,6 +27,7 @@
#include <QUrl> #include <QUrl>
#include "basetypes.h" #include "basetypes.h"
#include "ffs.h"
#include "ffsengine.h" #include "ffsengine.h"
namespace Ui { namespace Ui {
@ -58,6 +59,7 @@ private slots:
void insertAfter(); void insertAfter();
void replace(); void replace();
void remove(); void remove();
void scrollTreeView(QListWidgetItem* item);
private: private:
Ui::UEFITool * ui; Ui::UEFITool * ui;
@ -66,6 +68,7 @@ private:
void dragEnterEvent(QDragEnterEvent* event); void dragEnterEvent(QDragEnterEvent* event);
void dropEvent(QDropEvent* event); void dropEvent(QDropEvent* event);
void showDebugMessage();
}; };
#endif #endif

View File

@ -11,6 +11,7 @@ SOURCES += main.cpp \
ffsengine.cpp \ ffsengine.cpp \
treeitem.cpp \ treeitem.cpp \
treemodel.cpp \ treemodel.cpp \
debuglistitem.cpp \
LZMA/LzmaCompress.c \ LZMA/LzmaCompress.c \
LZMA/LzmaDecompress.c \ LZMA/LzmaDecompress.c \
LZMA/SDK/C/LzFind.c \ LZMA/SDK/C/LzFind.c \
@ -28,6 +29,7 @@ HEADERS += uefitool.h \
ffsengine.h \ ffsengine.h \
treeitem.h \ treeitem.h \
treemodel.h \ treemodel.h \
debuglistitem.h \
LZMA/LzmaCompress.h \ LZMA/LzmaCompress.h \
LZMA/LzmaDecompress.h \ LZMA/LzmaDecompress.h \
Tiano/EfiTianoDecompress.h \ Tiano/EfiTianoDecompress.h \

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>900</width> <width>1100</width>
<height>600</height> <height>700</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -20,7 +20,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>UEFITool 0.5.0</string> <string>UEFITool 0.6.0</string>
</property> </property>
<widget class="QWidget" name="centralWidget"> <widget class="QWidget" name="centralWidget">
<property name="sizePolicy"> <property name="sizePolicy">
@ -142,6 +142,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
</size>
</property>
<property name="title"> <property name="title">
<string>Debug</string> <string>Debug</string>
</property> </property>
@ -153,41 +159,7 @@
<number>5</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QPlainTextEdit" name="debugEdit"> <widget class="QListWidget" name="debugListWidget"/>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<family>Consolas</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>