mirror of
https://github.com/LongSoft/UEFITool.git
synced 2025-05-19 01:35:22 -04:00

2) Fixed QHexView misalignment in Windows and macOS. 3) Added some more analysis to raw files and sections: raw files can (or can not) contain sections, raw sections can contain NVAR storage or PE/TE. 4) Improved CPU base address detection and propagation. 5) Improved FIT recognition. 7) ME region not displayed if it is in unknown format, fixed this because we still want to operate with it. 8) Small changes to Flash Descriptor parsing to get more cases for valid "Intel image". To get rid of cases when "Intel image" is already in tree but with parse error because of which "UEFI image" appears. 9) Added parsing of individual UEFI-files (these can be trimmed from UEFI-volume), displaying it as "UEFI volume part". 10) Added possibility to view/save contents of elements "Free volume space", "Free space" and such, because these can be non-empty. 11) Added info about blocks number and block size (with preliminary and stupid validity check) to volume info. 12) Added storage in settings of the following paths: open image file, save image file, open GUIDs file. 13) Added last opened files list. 14) Added permanent opened file name string to the end of the status bar. 15) Added opened file changes tracking: if the file was modified in other program while it is opened in UEFITool, there are 3 ways to act: a) ignore changes (but mark file path displayed in the status bar with italic font); b) ask user to reopen or ignore (if ignore, mark as in a); c) auto reopen changed file in UEFITool. If changes were in some way ignored, file path displayed in the right of the status bar will be marked with italic font and then become clickable: on click request to reopen appears again. 16) Switched to offset/size instead of byte array storing in each tree item. 17) For clarity - added icons to key tree items (compressed and with contents, contents now must be in root item only). 18) For usability - added expanding tree on open image (to depth level 1) and by menu command (expand all).
416 lines
15 KiB
C++
416 lines
15 KiB
C++
/* fssbuilder.cpp
|
|
|
|
Copyright (c) 2015, 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 "ffsbuilder.h"
|
|
|
|
#include "descriptor.h"
|
|
#include "ffs.h"
|
|
#include "peimage.h"
|
|
#include "utility.h"
|
|
#include "nvram.h"
|
|
|
|
#include <cstring>
|
|
|
|
USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Try to get emptyByte value from item's parsing data
|
|
UINT8 emptyByte = 0xFF;
|
|
if (!model->hasEmptyParsingData(index)) {
|
|
if (model->type(index) == Types::Volume) {
|
|
VOLUME_PARSING_DATA pdata = *(VOLUME_PARSING_DATA*)model->parsingData(index).constData();
|
|
emptyByte = pdata.emptyByte;
|
|
}
|
|
else if (model->type(index) == Types::File) {
|
|
FILE_PARSING_DATA pdata = *(FILE_PARSING_DATA*)model->parsingData(index).constData();
|
|
emptyByte = pdata.emptyByte;
|
|
}
|
|
}
|
|
|
|
erased = UByteArray(model->headerSize(index) + model->bodySize(index) + model->tailSize(index), emptyByte);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsBuilder::build(const UModelIndex & root, UByteArray & image)
|
|
{
|
|
// Sanity check
|
|
if (!root.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
if (model->type(root) == Types::Capsule) {
|
|
return buildCapsule(root, image);
|
|
}
|
|
else if (model->type(root) == Types::Image) {
|
|
if (model->subtype(root) == Subtypes::IntelImage) {
|
|
return buildIntelImage(root, image);
|
|
}
|
|
else if (model->subtype(root) == Subtypes::UefiImage) {
|
|
return buildRawArea(root, image);
|
|
}
|
|
}
|
|
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildCapsule(const UModelIndex & index, UByteArray & capsule)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// No action
|
|
if (model->action(index) == Actions::NoAction) {
|
|
// Use original item data
|
|
capsule = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Rebuild or Replace
|
|
else if (model->action(index) == Actions::Rebuild
|
|
|| model->action(index) == Actions::Replace) {
|
|
if (model->rowCount(index)) {
|
|
// Clear the supplied UByteArray
|
|
capsule.clear();
|
|
|
|
// Right now there is only one capsule image element supported
|
|
if (model->rowCount(index) != 1) {
|
|
msg(usprintf("buildCapsule: building of capsules with %d items is not yet supported", model->rowCount(index)), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// Build image
|
|
UModelIndex imageIndex = index.model()->index(0, 0, index);
|
|
UByteArray imageData;
|
|
|
|
// Check image type
|
|
if (model->type(imageIndex) == Types::Image) {
|
|
USTATUS result = U_SUCCESS;
|
|
if (model->subtype(imageIndex) == Subtypes::IntelImage) {
|
|
result = buildIntelImage(imageIndex, imageData);
|
|
}
|
|
else if (model->subtype(imageIndex) == Subtypes::UefiImage) {
|
|
result = buildRawArea(imageIndex, imageData);
|
|
}
|
|
else {
|
|
msg(UString("buildCapsule: unexpected item subtype ") + itemSubtypeToUString(model->type(imageIndex), model->subtype(imageIndex)), imageIndex);
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Check build result
|
|
if (result) {
|
|
msg(UString("buildCapsule: building of ") + model->name(imageIndex) + UString(" failed with error ") + errorCodeToUString(result), imageIndex);
|
|
return result;
|
|
}
|
|
else
|
|
capsule += imageData;
|
|
}
|
|
else {
|
|
msg(UString("buildCapsule: unexpected item type ") + itemTypeToUString(model->type(imageIndex)), imageIndex);
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Check size of reconstructed capsule body, it must remain the same
|
|
UINT32 newSize = (UINT32)capsule.size();
|
|
UINT32 oldSize = (UINT32)model->bodySize(index);
|
|
if (newSize > oldSize) {
|
|
msg(usprintf("buildCapsule: new capsule size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_CAPSULE;
|
|
}
|
|
else if (newSize < oldSize) {
|
|
msg(usprintf("buildCapsule: new capsule size %Xh (%u) is smaller than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_CAPSULE;
|
|
}
|
|
}
|
|
else
|
|
capsule = model->body(index);
|
|
|
|
// Build successful, append header and tail
|
|
capsule = model->header(index) + capsule + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
msg(UString("buildCapsule: unexpected action " + actionTypeToUString(model->action(index))), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildIntelImage(const UModelIndex & index, UByteArray & intelImage)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_SUCCESS;
|
|
|
|
// No action
|
|
if (model->action(index) == Actions::NoAction) {
|
|
intelImage = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
// Remove
|
|
else if (model->action(index) == Actions::Remove) {
|
|
intelImage.clear();
|
|
return U_SUCCESS;
|
|
}
|
|
// Rebuild
|
|
else if (model->action(index) == Actions::Rebuild) {
|
|
// First child will always be descriptor for this type of image, and it's read only for now
|
|
intelImage = model->header(index.model()->index(0, 0, index)) + model->body(index.model()->index(0, 0, index)) + model->tail(index.model()->index(0, 0, index));
|
|
|
|
// Process other regions
|
|
for (int i = 1; i < model->rowCount(index); i++) {
|
|
UModelIndex currentRegion = index.model()->index(i, 0, index);
|
|
|
|
// Skip regions with Remove action
|
|
if (model->action(currentRegion) == Actions::Remove)
|
|
continue;
|
|
|
|
// Check item type to be either region or padding
|
|
UINT8 type = model->type(currentRegion);
|
|
if (type == Types::Padding) {
|
|
// Add padding as is
|
|
intelImage += model->header(currentRegion) + model->body(currentRegion) + model->tail(currentRegion);
|
|
continue;
|
|
}
|
|
|
|
// Check region subtype
|
|
USTATUS result;
|
|
UByteArray region;
|
|
UINT8 regionType = model->subtype(currentRegion);
|
|
switch (regionType) {
|
|
case Subtypes::BiosRegion:
|
|
case Subtypes::PdrRegion:
|
|
result = buildRawArea(currentRegion, region);
|
|
if (result) {
|
|
msg(UString("buildIntelImage: building of region ") + regionTypeToUString(regionType) + UString(" failed with error ") + errorCodeToUString(result), currentRegion);
|
|
return result;
|
|
}
|
|
break;
|
|
case Subtypes::MeRegion:
|
|
case Subtypes::GbeRegion:
|
|
case Subtypes::DevExp1Region:
|
|
case Subtypes::Bios2Region:
|
|
case Subtypes::MicrocodeRegion:
|
|
case Subtypes::EcRegion:
|
|
case Subtypes::DevExp2Region:
|
|
case Subtypes::IeRegion:
|
|
case Subtypes::Tgbe1Region:
|
|
case Subtypes::Tgbe2Region:
|
|
case Subtypes::Reserved1Region:
|
|
case Subtypes::Reserved2Region:
|
|
case Subtypes::PttRegion:
|
|
// Add region as is
|
|
region = model->header(currentRegion) + model->body(currentRegion);
|
|
break;
|
|
default:
|
|
msg(UString("buildIntelImage: unknown region type"), currentRegion);
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Append the resulting region
|
|
intelImage += region;
|
|
}
|
|
|
|
// Check size of new image, it must be same as old one
|
|
UINT32 newSize = (UINT32)intelImage.size();
|
|
UINT32 oldSize = (UINT32)model->bodySize(index);
|
|
if (newSize > oldSize) {
|
|
msg(usprintf("buildIntelImage: new image size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
else if (newSize < oldSize) {
|
|
msg(usprintf("buildIntelImage: new image size %Xh (%u) is smaller than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
// Build successful, append header and tail
|
|
intelImage = model->header(index) + intelImage + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
msg(UString("buildIntelImage: unexpected action " + actionTypeToUString(model->action(index))), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// No action required
|
|
if (model->action(index) == Actions::NoAction) {
|
|
rawArea = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
// Remove
|
|
else if (model->action(index) == Actions::Remove) {
|
|
rawArea.clear();
|
|
return U_SUCCESS;
|
|
}
|
|
// Rebuild or Replace
|
|
else if (model->action(index) == Actions::Rebuild
|
|
|| model->action(index) == Actions::Replace) {
|
|
// Rebuild if there is at least 1 child
|
|
if (model->rowCount(index)) {
|
|
// Clear the supplied UByteArray
|
|
rawArea.clear();
|
|
|
|
// Build children
|
|
for (int i = 0; i < model->rowCount(index); i++) {
|
|
USTATUS result = U_SUCCESS;
|
|
UModelIndex currentChild = index.model()->index(i, 0, index);
|
|
UByteArray currentData;
|
|
|
|
// Check child type
|
|
if (model->type(currentChild) == Types::Volume) {
|
|
result = buildVolume(currentChild, currentData);
|
|
}
|
|
else if (model->type(currentChild) == Types::Padding) {
|
|
result = buildPadding(currentChild, currentData);
|
|
}
|
|
else {
|
|
msg(UString("buildRawArea: unexpected item type ") + itemTypeToUString(model->type(currentChild)), currentChild);
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
// Check build result
|
|
if (result) {
|
|
msg(UString("buildRawArea: building of ") + model->name(currentChild) + UString(" failed with error ") + errorCodeToUString(result), currentChild);
|
|
return result;
|
|
}
|
|
// Append current data
|
|
rawArea += currentData;
|
|
}
|
|
|
|
// Check size of new raw area, it must be same as original one
|
|
UINT32 newSize = (UINT32)rawArea.size();
|
|
UINT32 oldSize = (UINT32)model->bodySize(index);
|
|
if (newSize > oldSize) {
|
|
msg(usprintf("buildRawArea: new area size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_RAW_AREA;
|
|
}
|
|
else if (newSize < oldSize) {
|
|
msg(usprintf("buildRawArea: new area size %Xh (%u) is smaller than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
|
|
return U_INVALID_RAW_AREA;
|
|
}
|
|
}
|
|
// No need to rebuild a raw area with no children
|
|
else {
|
|
rawArea = model->body(index);
|
|
}
|
|
|
|
// Build successful, add header if needed
|
|
rawArea = model->header(index) + rawArea + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
msg(UString("buildRawArea: unexpected action " + actionTypeToUString(model->action(index))), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildPadding(const UModelIndex & index, UByteArray & padding)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// No action required
|
|
if (model->action(index) == Actions::NoAction) {
|
|
padding = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
// Remove
|
|
else if (model->action(index) == Actions::Remove) {
|
|
padding.clear();
|
|
return U_SUCCESS;
|
|
}
|
|
// Erase
|
|
else if (model->action(index) == Actions::Erase) {
|
|
return erase(index, padding);
|
|
}
|
|
|
|
msg(UString("buildPadding: unexpected action " + actionTypeToUString(model->action(index))), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildNonUefiData(const UModelIndex & index, UByteArray & data)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// No action required
|
|
if (model->action(index) == Actions::NoAction) {
|
|
data = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
// Remove
|
|
else if (model->action(index) == Actions::Remove) {
|
|
data.clear();
|
|
return U_SUCCESS;
|
|
}
|
|
// Erase
|
|
else if (model->action(index) == Actions::Erase) {
|
|
return erase(index, data);
|
|
}
|
|
|
|
// TODO: rebuild properly
|
|
|
|
msg(UString("buildNoUefiData: unexpected action " + actionTypeToUString(model->action(index))), index);
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildFreeSpace(const UModelIndex & index, UByteArray & freeSpace)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// No actions possible for free space
|
|
freeSpace = model->header(index) + model->body(index) + model->tail(index);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildVolume(const UModelIndex & index, UByteArray & volume)
|
|
{
|
|
U_UNUSED_PARAMETER(index);
|
|
U_UNUSED_PARAMETER(volume);
|
|
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildPadFile(const UModelIndex & index, UByteArray & padFile)
|
|
{
|
|
U_UNUSED_PARAMETER(index);
|
|
U_UNUSED_PARAMETER(padFile);
|
|
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildFile(const UModelIndex & index, UByteArray & file)
|
|
{
|
|
U_UNUSED_PARAMETER(index);
|
|
U_UNUSED_PARAMETER(file);
|
|
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
USTATUS FfsBuilder::buildSection(const UModelIndex & index, UByteArray & section)
|
|
{
|
|
U_UNUSED_PARAMETER(index);
|
|
U_UNUSED_PARAMETER(section);
|
|
|
|
return U_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|