mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-25 01:18:22 +08:00
934ce1f3f8
As the first step towards automated parsing, this change set replaces outdated BootGuard-related parsers with shiny new KaitaiStruct-based ones. It also does the following: - improves Intel FIT definitions by using the relevant specification - adds sha1, sha384, sha512 and sm3 digest implementations - updates LZMA SDK to v22.01 - moves GUIDs out of include files to prevent multiple instantiations - enforces C++11 - adds Kaitai-based parsers for Intel FIT, BootGuard v1 and BootGuard v2 structures - makes many small refactorings here, there and everywhere
675 lines
33 KiB
C++
Executable File
675 lines
33 KiB
C++
Executable File
/* meparser.cpp
|
|
|
|
Copyright (c) 2019, 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 <map>
|
|
|
|
#include "ffs.h"
|
|
#include "me.h"
|
|
#include "meparser.h"
|
|
#include "parsingdata.h"
|
|
#include "utility.h"
|
|
|
|
#ifdef U_ENABLE_ME_PARSING_SUPPORT
|
|
|
|
struct FPT_PARTITION_INFO {
|
|
FPT_HEADER_ENTRY ptEntry;
|
|
UINT8 type;
|
|
UModelIndex index;
|
|
friend bool operator< (const FPT_PARTITION_INFO & lhs, const FPT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; }
|
|
};
|
|
|
|
struct IFWI_PARTITION_INFO {
|
|
IFWI_HEADER_ENTRY ptEntry;
|
|
UINT8 type;
|
|
UINT8 subtype;
|
|
friend bool operator< (const IFWI_PARTITION_INFO & lhs, const IFWI_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; }
|
|
};
|
|
|
|
USTATUS MeParser::parseMeRegionBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain ME region
|
|
UByteArray meRegion = model->body(index);
|
|
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < ME_ROM_BYPASS_VECTOR_SIZE + sizeof(UINT32)) {
|
|
msg(usprintf("%s: ME region too small to fit ROM bypass vector", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Check ME signature to determine it's version
|
|
// ME v11 and older layout
|
|
if (*(UINT32*)meRegion.constData() == FPT_HEADER_SIGNATURE || *(UINT32*)(meRegion.constData() + ME_ROM_BYPASS_VECTOR_SIZE) == FPT_HEADER_SIGNATURE) {
|
|
UModelIndex ptIndex;
|
|
return parseFptRegion(meRegion, index, ptIndex);
|
|
}
|
|
|
|
// IFWI 1.6
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < sizeof(IFWI_16_LAYOUT_HEADER)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.6 layout header", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
const IFWI_16_LAYOUT_HEADER* ifwi16Header = (const IFWI_16_LAYOUT_HEADER*)meRegion.constData();
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < ifwi16Header->DataPartition.Offset + sizeof(UINT32)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.6 data partition", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
// Data partition always points to FPT header
|
|
if (*(UINT32*)(meRegion.constData() + ifwi16Header->DataPartition.Offset) == FPT_HEADER_SIGNATURE) {
|
|
UModelIndex ptIndex;
|
|
return parseIfwi16Region(meRegion, index, ptIndex);
|
|
}
|
|
|
|
// IFWI 1.7
|
|
if ((UINT32)meRegion.size() < sizeof(IFWI_17_LAYOUT_HEADER)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.7 layout header", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
const IFWI_17_LAYOUT_HEADER* ifwi17Header = (const IFWI_17_LAYOUT_HEADER*)meRegion.constData();
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < ifwi17Header->DataPartition.Offset + sizeof(UINT32)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.7 data partition", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
// Data partition always points to FPT header
|
|
if (*(UINT32*)(meRegion.constData() + ifwi17Header->DataPartition.Offset)== FPT_HEADER_SIGNATURE) {
|
|
UModelIndex ptIndex;
|
|
return parseIfwi17Region(meRegion, index, ptIndex);
|
|
}
|
|
|
|
// Something else entirely
|
|
msg(usprintf("%s: unknown ME region format", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check region size
|
|
if ((UINT32)region.size() < sizeof(FPT_HEADER)) {
|
|
msg(usprintf("%s: region too small to fit the FPT partition table header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Populate partition table header
|
|
const FPT_HEADER* ptHeader = (const FPT_HEADER*)region.constData();
|
|
UINT32 romBypassVectorSize = 0;
|
|
if (*(UINT32*)region.constData() != FPT_HEADER_SIGNATURE) {
|
|
// Adjust the header to skip ROM bypass vector
|
|
romBypassVectorSize = ME_ROM_BYPASS_VECTOR_SIZE;
|
|
ptHeader = (const FPT_HEADER*)(region.constData() + romBypassVectorSize);
|
|
}
|
|
|
|
// Check region size again
|
|
UINT32 ptBodySize = ptHeader->NumEntries * sizeof(FPT_HEADER_ENTRY);
|
|
UINT32 ptSize = romBypassVectorSize + sizeof(FPT_HEADER) + ptBodySize;
|
|
if ((UINT32)region.size() < ptSize) {
|
|
msg(usprintf("%s: ME region too small to fit the FPT partition table", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Get info
|
|
UByteArray header = region.left(romBypassVectorSize + sizeof(FPT_HEADER));
|
|
UByteArray body = region.mid(header.size(), ptBodySize);
|
|
|
|
UString name = UString("FPT partition table");
|
|
UString info;
|
|
|
|
// Special case of FPT header version 2.1
|
|
if (ptHeader->HeaderVersion == FPT_HEADER_VERSION_21) {
|
|
const FPT_HEADER_21* ptHeader21 = (const FPT_HEADER_21*)ptHeader;
|
|
|
|
info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n"
|
|
"Header length: %02Xh\nFlags: %Xh\nTicks to add: %04Xh\nTokens to add: %04Xh\nSPS Flags: %Xh\nFITC version: %u.%u.%u.%u\nCRC32 Checksum: %08Xh",
|
|
ptSize, ptSize,
|
|
(UINT32)header.size(), (UINT32)header.size(),
|
|
ptBodySize, ptBodySize,
|
|
(romBypassVectorSize ? "present" : "absent"),
|
|
ptHeader21->NumEntries,
|
|
ptHeader21->HeaderVersion,
|
|
ptHeader21->EntryVersion,
|
|
ptHeader21->HeaderLength,
|
|
ptHeader21->Flags,
|
|
ptHeader21->TicksToAdd,
|
|
ptHeader21->TokensToAdd,
|
|
ptHeader21->SPSFlags,
|
|
ptHeader21->FitcMajor, ptHeader21->FitcMinor, ptHeader21->FitcHotfix, ptHeader21->FitcBuild,
|
|
ptHeader21->HeaderCrc32);
|
|
// TODO: verify header crc32
|
|
}
|
|
// Default handling for all other versions, may be too generic in some corner cases
|
|
else {
|
|
info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n"
|
|
"Header length: %02Xh\nFlash cycle life: %04Xh\nFlash cycle limit: %04Xh\nUMA size: %Xh\nFlags: %Xh\nFITC version: %u.%u.%u.%u\nChecksum: %02Xh",
|
|
ptSize, ptSize,
|
|
(UINT32)header.size(), (UINT32)header.size(),
|
|
ptBodySize, ptBodySize,
|
|
(romBypassVectorSize ? "present" : "absent"),
|
|
ptHeader->NumEntries,
|
|
ptHeader->HeaderVersion,
|
|
ptHeader->EntryVersion,
|
|
ptHeader->HeaderLength,
|
|
ptHeader->FlashCycleLife,
|
|
ptHeader->FlashCycleLimit,
|
|
ptHeader->UmaSize,
|
|
ptHeader->Flags,
|
|
ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild,
|
|
ptHeader->HeaderChecksum);
|
|
// TODO: verify header checksum8
|
|
}
|
|
|
|
// Add tree item
|
|
index = model->addItem(0, Types::FptStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
// Add partition table entries
|
|
std::vector<FPT_PARTITION_INFO> partitions;
|
|
UINT32 offset = (UINT32)header.size();
|
|
UINT32 numEntries = ptHeader->NumEntries;
|
|
const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset);
|
|
for (UINT32 i = 0; i < numEntries; i++) {
|
|
// Populate entry header
|
|
const FPT_HEADER_ENTRY* ptEntry = firstPtEntry + i;
|
|
|
|
// Get info
|
|
name = visibleAsciiOrHex((UINT8*)ptEntry->Name, 4);
|
|
info = usprintf("Full size: %Xh (%u)\nPartition offset: %Xh\nPartition length: %Xh\nPartition type: %02Xh",
|
|
(UINT32)sizeof(FPT_HEADER_ENTRY), (UINT32)sizeof(FPT_HEADER_ENTRY),
|
|
ptEntry->Offset,
|
|
ptEntry->Size,
|
|
ptEntry->Type);
|
|
|
|
// Add tree item
|
|
const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry;
|
|
UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(FPT_HEADER_ENTRY)), UByteArray(), Fixed, index);
|
|
|
|
// Adjust offset
|
|
offset += sizeof(FPT_HEADER_ENTRY);
|
|
|
|
// Add valid partitions
|
|
if (type == Subtypes::ValidFptEntry) { // Skip absent and invalid partitions
|
|
// Add to partitions vector
|
|
FPT_PARTITION_INFO partition = {};
|
|
partition.type = Types::FptPartition;
|
|
partition.ptEntry = *ptEntry;
|
|
partition.index = entryIndex;
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
FPT_PARTITION_INFO padding = {};
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset < ptSize) {
|
|
msg(usprintf("%s: ME partition has intersection with ME partition table, skipped", __FUNCTION__),
|
|
partitions.front().index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset > ptSize) {
|
|
padding.ptEntry.Offset = ptSize;
|
|
padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size;
|
|
|
|
// Check that current region is fully present in the image
|
|
if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) {
|
|
if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) {
|
|
msg(usprintf("%s: FPT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: FPT partition can't fit into the region, truncated", __FUNCTION__), partitions[i].index);
|
|
partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) {
|
|
msg(usprintf("%s: FPT partition is located inside another FPT partition, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: FPT partition intersects with previous one, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<FPT_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
// Check for padding after the last region
|
|
if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) {
|
|
padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size;
|
|
padding.ptEntry.Size = (UINT32)(region.size() - padding.ptEntry.Offset);
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size);
|
|
if (partitions[i].type == Types::FptPartition) {
|
|
UModelIndex partitionIndex;
|
|
// Get info
|
|
name = visibleAsciiOrHex((UINT8*) partitions[i].ptEntry.Name, 4);
|
|
info = usprintf("Full size: %Xh (%u)\nPartition type: %02Xh\n",
|
|
(UINT32)partition.size(), (UINT32)partition.size(),
|
|
partitions[i].ptEntry.Type);
|
|
|
|
// Add tree item
|
|
UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.Type;
|
|
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
if (type == Subtypes::CodeFptPartition && partition.size() >= (int) sizeof(UINT32) && readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) {
|
|
// Parse code partition contents
|
|
UModelIndex cpdIndex;
|
|
ffsParser->parseCpdRegion(partition, partitions[i].ptEntry.Offset, partitionIndex, cpdIndex);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check region size again
|
|
if ((UINT32)region.size() < sizeof(IFWI_16_LAYOUT_HEADER)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.6 layout header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
const IFWI_16_LAYOUT_HEADER* ifwiHeader = (const IFWI_16_LAYOUT_HEADER*)region.constData();
|
|
|
|
// Add header
|
|
UINT32 ptSize = sizeof(IFWI_16_LAYOUT_HEADER);
|
|
UByteArray header = region.left(ptSize);
|
|
|
|
UString name = UString("IFWI 1.6 header");
|
|
UString info = usprintf("Full size: %Xh (%u)\n"
|
|
"Data partition offset: %Xh\nData partition size: %Xh\n"
|
|
"Boot1 partition offset: %Xh\nBoot1 partition size: %Xh\n"
|
|
"Boot2 partition offset: %Xh\nBoot2 partition size: %Xh\n"
|
|
"Boot3 partition offset: %Xh\nBoot3 partition size: %Xh\n"
|
|
"Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n"
|
|
"Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n"
|
|
"Checksum: %" PRIX64 "h",
|
|
(UINT32)header.size(), (UINT32)header.size(),
|
|
ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size,
|
|
ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size,
|
|
ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size,
|
|
ifwiHeader->BootPartition[2].Offset, ifwiHeader->BootPartition[2].Size,
|
|
ifwiHeader->BootPartition[3].Offset, ifwiHeader->BootPartition[3].Size,
|
|
ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size,
|
|
ifwiHeader->Checksum);
|
|
// Add tree item
|
|
index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent);
|
|
|
|
std::vector<IFWI_PARTITION_INFO> partitions;
|
|
// Add data partition
|
|
{
|
|
IFWI_PARTITION_INFO partition = {};
|
|
partition.type = Types::IfwiPartition;
|
|
partition.subtype = Subtypes::DataIfwiPartition;
|
|
partition.ptEntry = ifwiHeader->DataPartition;
|
|
partitions.push_back(partition);
|
|
}
|
|
// Add boot partitions
|
|
for (UINT8 i = 0 ; i < 4; i++) {
|
|
if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) {
|
|
IFWI_PARTITION_INFO partition = {};
|
|
partition.type = Types::IfwiPartition;
|
|
partition.subtype = Subtypes::BootIfwiPartition;
|
|
partition.ptEntry = ifwiHeader->BootPartition[i];
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
IFWI_PARTITION_INFO padding = {};
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset < ptSize) {
|
|
msg(usprintf("%s: IFWI partition has intersection with IFWI layout header, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset > ptSize) {
|
|
padding.ptEntry.Offset = ptSize;
|
|
padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size;
|
|
|
|
// Check that current region is fully present in the image
|
|
if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) {
|
|
if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) {
|
|
msg(usprintf("%s: IFWI partition is located outside of the opened image, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: IFWI partition can't fit into the region, truncated", __FUNCTION__), index);
|
|
partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) {
|
|
msg(usprintf("%s: IFWI partition is located inside another IFWI partition, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: IFWI partition intersects with previous one, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<IFWI_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
// Check for padding after the last region
|
|
if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) {
|
|
padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size;
|
|
padding.ptEntry.Size = (UINT32)(region.size() - padding.ptEntry.Offset);
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size);
|
|
if (partitions[i].type == Types::IfwiPartition) {
|
|
UModelIndex partitionIndex;
|
|
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
|
|
name = "Data partition";
|
|
}
|
|
else if (partitions[i].subtype == Subtypes::BootIfwiPartition) {
|
|
name = "Boot partition";
|
|
}
|
|
|
|
// Get info
|
|
info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size());
|
|
|
|
// Add tree item
|
|
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
// Parse partition further
|
|
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
|
|
UModelIndex dataPartitionFptRegionIndex;
|
|
parseFptRegion(partition, partitionIndex, dataPartitionFptRegionIndex);
|
|
}
|
|
else if (partitions[i].subtype == Subtypes::BootIfwiPartition) {
|
|
// Parse code partition contents
|
|
UModelIndex bootPartitionBpdtRegionIndex;
|
|
ffsParser->parseBpdtRegion(partition, 0, 0, partitionIndex, bootPartitionBpdtRegionIndex);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check region size again
|
|
if ((UINT32)region.size() < sizeof(IFWI_17_LAYOUT_HEADER)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI 1.7 layout header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
const IFWI_17_LAYOUT_HEADER* ifwiHeader = (const IFWI_17_LAYOUT_HEADER*)region.constData();
|
|
// TODO: add check for HeaderSize to be 0x40
|
|
|
|
// Add header
|
|
UINT32 ptSize = sizeof(IFWI_17_LAYOUT_HEADER);
|
|
UByteArray header = region.left(ptSize);
|
|
|
|
UString name = UString("IFWI 1.7 header");
|
|
UString info = usprintf("Full size: %Xh (%u)\n"
|
|
"Flags: %02Xh\n"
|
|
"Reserved: %02Xh\n"
|
|
"Checksum: %Xh\n"
|
|
"Data partition offset: %Xh\nData partition size: %Xh\n"
|
|
"Boot1 partition offset: %Xh\nBoot1 partition size: %Xh\n"
|
|
"Boot2 partition offset: %Xh\nBoot2 partition size: %Xh\n"
|
|
"Boot3 partition offset: %Xh\nBoot3 partition size: %Xh\n"
|
|
"Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n"
|
|
"Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n"
|
|
"Temp page offset: %Xh\nTemp page size: %Xh\n",
|
|
(UINT32)header.size(), (UINT32)header.size(),
|
|
ifwiHeader->Flags,
|
|
ifwiHeader->Reserved,
|
|
ifwiHeader->Checksum,
|
|
ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size,
|
|
ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size,
|
|
ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size,
|
|
ifwiHeader->BootPartition[2].Offset, ifwiHeader->BootPartition[2].Size,
|
|
ifwiHeader->BootPartition[3].Offset, ifwiHeader->BootPartition[3].Size,
|
|
ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size,
|
|
ifwiHeader->TempPage.Offset, ifwiHeader->TempPage.Size);
|
|
// Add tree item
|
|
index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent);
|
|
|
|
std::vector<IFWI_PARTITION_INFO> partitions;
|
|
// Add data partition
|
|
{
|
|
IFWI_PARTITION_INFO partition = {};
|
|
partition.type = Types::IfwiPartition;
|
|
partition.subtype = Subtypes::DataIfwiPartition;
|
|
partition.ptEntry = ifwiHeader->DataPartition;
|
|
partitions.push_back(partition);
|
|
}
|
|
// Add boot partitions
|
|
for (UINT8 i = 0 ; i < 4; i++) {
|
|
if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) {
|
|
IFWI_PARTITION_INFO partition = {};
|
|
partition.type = Types::IfwiPartition;
|
|
partition.subtype = Subtypes::BootIfwiPartition;
|
|
partition.ptEntry = ifwiHeader->BootPartition[i];
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
// Add temp page
|
|
if (ifwiHeader->TempPage.Offset != 0 && ifwiHeader->TempPage.Offset != 0xFFFFFFFF) {
|
|
IFWI_PARTITION_INFO partition = {};
|
|
partition.type = Types::IfwiPartition;
|
|
partition.subtype = Subtypes::DataPadding;
|
|
partition.ptEntry = ifwiHeader->TempPage;
|
|
partitions.push_back(partition);
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
IFWI_PARTITION_INFO padding = {};
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset < ptSize) {
|
|
msg(usprintf("%s: IFWI partition has intersection with IFWI layout header, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset > ptSize) {
|
|
padding.ptEntry.Offset = ptSize;
|
|
padding.ptEntry.Size = partitions.front().ptEntry.Offset - ptSize;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size;
|
|
|
|
// Check that current region is fully present in the image
|
|
if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) {
|
|
if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) {
|
|
msg(usprintf("%s: IFWI partition is located outside of the opened image, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: IFWI partition can't fit into the region, truncated", __FUNCTION__), index);
|
|
partitions[i].ptEntry.Size = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Size <= previousPartitionEnd) {
|
|
msg(usprintf("%s: IFWI partition is located inside another IFWI partition, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: IFWI partition intersects with previous one, skipped", __FUNCTION__), index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Size = partitions[i].ptEntry.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<IFWI_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
// Check for padding after the last region
|
|
if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Size < (UINT32)region.size()) {
|
|
padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Size;
|
|
padding.ptEntry.Size = (UINT32)(region.size() - padding.ptEntry.Offset);
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size);
|
|
if (partitions[i].type == Types::IfwiPartition) {
|
|
UModelIndex partitionIndex;
|
|
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
|
|
name = "Data partition";
|
|
|
|
}
|
|
else if (partitions[i].subtype == Subtypes::BootIfwiPartition){
|
|
name = "Boot partition";
|
|
}
|
|
else {
|
|
name = "Temp page";
|
|
}
|
|
|
|
// Get info
|
|
info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size());
|
|
|
|
// Add tree item
|
|
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
// Parse partition further
|
|
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
|
|
UModelIndex dataPartitionFptRegionIndex;
|
|
parseFptRegion(partition, partitionIndex, dataPartitionFptRegionIndex);
|
|
}
|
|
else if (partitions[i].subtype == Subtypes::BootIfwiPartition) {
|
|
// Parse code partition contents
|
|
UModelIndex bootPartitionBpdtRegionIndex;
|
|
ffsParser->parseBpdtRegion(partition, 0, 0, partitionIndex, bootPartitionBpdtRegionIndex);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
#endif // U_ENABLE_ME_PARSING_SUPPORT
|
|
|