UEFITool/common/meparser.cpp

700 lines
34 KiB
C++
Raw Permalink Normal View History

/* 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)) {
2022-08-25 12:49:10 +08:00
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) {
2022-08-25 12:49:10 +08:00
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;
2022-08-28 18:01:43 +08:00
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 {
2022-08-28 18:01:43 +08:00
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();
2022-08-25 12:49:10 +08:00
UINT32 numEntries = ptHeader->NumEntries;
const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset);
2022-08-25 12:49:10 +08:00
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
2022-08-25 12:49:10 +08:00
FPT_PARTITION_INFO partition = {};
partition.type = Types::FptPartition;
partition.ptEntry = *ptEntry;
partition.index = entryIndex;
partitions.push_back(partition);
}
}
// Check for empty set of partitions
if (partitions.empty()) {
// Add a single padding partition in this case
FPT_PARTITION_INFO padding = {};
padding.ptEntry.Offset = offset;
padding.ptEntry.Size = (UINT32)(region.size() - padding.ptEntry.Offset);
padding.type = Types::Padding;
partitions.push_back(padding);
}
make_partition_table_consistent:
if (partitions.empty()) {
return U_INVALID_ME_PARTITION_TABLE;
}
// Sort partitions by offset
std::sort(partitions.begin(), partitions.end());
// Check for intersections and paddings between partitions
2022-08-25 12:49:10 +08:00
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);
2022-08-28 18:01:43 +08:00
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");
2022-08-28 18:01:43 +08:00
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
{
2022-08-25 12:49:10 +08:00
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 < 5; i++) {
if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) {
2022-08-25 12:49:10 +08:00
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:
if (partitions.empty()) {
return U_INVALID_ME_PARTITION_TABLE;
}
// Sort partitions by offset
std::sort(partitions.begin(), partitions.end());
// Check for intersections and paddings between partitions
2022-08-25 12:49:10 +08:00
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");
2022-08-28 18:01:43 +08:00
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
{
2022-08-25 12:49:10 +08:00
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 < 5; i++) {
if (ifwiHeader->BootPartition[i].Offset != 0 && ifwiHeader->BootPartition[i].Offset != 0xFFFFFFFF) {
2022-08-25 12:49:10 +08:00
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) {
2022-08-25 12:49:10 +08:00
IFWI_PARTITION_INFO partition = {};
partition.type = Types::IfwiPartition;
partition.subtype = Subtypes::DataPadding;
partition.ptEntry = ifwiHeader->TempPage;
partitions.push_back(partition);
}
make_partition_table_consistent:
if (partitions.empty()) {
return U_INVALID_ME_PARTITION_TABLE;
}
// Sort partitions by offset
std::sort(partitions.begin(), partitions.end());
// Check for intersections and paddings between partitions
2022-08-25 12:49:10 +08:00
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 bootPartitionRegionIndex;
if (*(UINT32*)partition.constData() == FPT_HEADER_SIGNATURE) {
// Parse as FptRegion
parseFptRegion(partition, partitionIndex, bootPartitionRegionIndex);
}
else {
// Parse as BpdtRegion
ffsParser->parseBpdtRegion(partition, 0, 0, partitionIndex, bootPartitionRegionIndex);
}
}
}
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