mirror of
https://github.com/LongSoft/UEFITool.git
synced 2025-06-05 17:24:22 -04:00

Based on the open source AMDFWTOOL available here: https://github.com/coreboot/coreboot/tree/main/util/amdfwtool TODO: - Merge duplicated regions There can be multiple L2 directory tables, for A/B recovery or to support different SoC SKUs. They point to the same regions, causing the same area to be shown multiple times in the parsed image. - Better quirks support PSP hardcodes certain sizes and address types for some files. The parser might thus fail, even though it works on real hardware. Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
1030 lines
42 KiB
C++
1030 lines
42 KiB
C++
/* ffsparser_amd.cpp
|
|
|
|
Copyright (c) 2025 Patrick Rudolph. 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,
|
|
WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*/
|
|
|
|
#include "ffsparser.h"
|
|
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
#include "amd_descriptor.h"
|
|
#include "utility.h"
|
|
|
|
// Converts addresses to be relative to the start of the file (ROM addressing)
|
|
USTATUS FfsParser::amdRelativeOffset(const UModelIndex& parent, const UINT64 addr, const UINT64 mode, UINT64 & outaddr)
|
|
{
|
|
switch (mode) {
|
|
/* Since this utility operates on the BIOS file, physical address is converted
|
|
relative to the start of the BIOS file. */
|
|
case AMD_ADDR_PHYSICAL:
|
|
if (addr < SPI_ROM_BASE || addr > (SPI_ROM_BASE + FILE_REL_MASK)) {
|
|
msg(usprintf("%s: Invalid address(%lx) or mode(%lx)\n", __FUNCTION__, addr, mode));
|
|
}
|
|
outaddr = addr & FILE_REL_MASK;
|
|
return U_SUCCESS;
|
|
case AMD_ADDR_REL_BIOS:
|
|
{
|
|
UModelIndex parentImageIndex = (model->type(parent) == Types::Image) ? parent : model->findParentOfType(parent, Types::Image);
|
|
UByteArray parentImage = model->header(parentImageIndex) + model->body(parentImageIndex) + model->tail(parentImageIndex);
|
|
if (addr >= parentImage.size()) {
|
|
msg(usprintf("%s: Invalid address(%lx) or mode(%lx)\n", __FUNCTION__, addr, mode));
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
outaddr = addr;
|
|
return U_SUCCESS;
|
|
}
|
|
case AMD_ADDR_REL_TAB:
|
|
outaddr = addr + model->base(parent);
|
|
return U_SUCCESS;
|
|
default:
|
|
msg(usprintf("Unsupported mode %lu\n", mode));
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// Convert the ID to known file names
|
|
UString FfsParser::pspFileName(const UINT8 type, const UINT8 subtype)
|
|
{
|
|
#define ENUM_TO_STRING(x) case x: return usprintf("PSP file %02x - %02x (%s)", type, subtype, #x);
|
|
switch (type) {
|
|
ENUM_TO_STRING(AMD_FW_PSP_PUBKEY)
|
|
ENUM_TO_STRING(AMD_FW_PSP_BOOTLOADER)
|
|
ENUM_TO_STRING(AMD_FW_PSP_SECURED_OS)
|
|
ENUM_TO_STRING(AMD_FW_PSP_RECOVERY)
|
|
ENUM_TO_STRING(AMD_FW_PSP_NVRAM)
|
|
ENUM_TO_STRING(AMD_FW_RTM_PUBKEY)
|
|
ENUM_TO_STRING(AMD_FW_PSP_SMU_FIRMWARE)
|
|
ENUM_TO_STRING(AMD_FW_PSP_SECURED_DEBUG)
|
|
ENUM_TO_STRING(AMD_FW_PSP_TRUSTLETS)
|
|
ENUM_TO_STRING(AMD_FW_PSP_TRUSTLETKEY)
|
|
ENUM_TO_STRING(AMD_FW_PSP_SMU_FIRMWARE2)
|
|
ENUM_TO_STRING(AMD_DEBUG_UNLOCK)
|
|
ENUM_TO_STRING(AMD_FW_PSP_TEEIPKEY)
|
|
ENUM_TO_STRING(AMD_BOOT_DRIVER)
|
|
ENUM_TO_STRING(AMD_SOC_DRIVER)
|
|
ENUM_TO_STRING(AMD_DEBUG_DRIVER)
|
|
ENUM_TO_STRING(AMD_INTERFACE_DRIVER)
|
|
ENUM_TO_STRING(AMD_HW_IPCFG)
|
|
ENUM_TO_STRING(AMD_WRAPPED_IKEK)
|
|
ENUM_TO_STRING(AMD_TOKEN_UNLOCK)
|
|
ENUM_TO_STRING(AMD_SEC_GASKET)
|
|
ENUM_TO_STRING(AMD_MP2_FW)
|
|
ENUM_TO_STRING(AMD_DRIVER_ENTRIES)
|
|
ENUM_TO_STRING(AMD_FW_KVM_IMAGE)
|
|
ENUM_TO_STRING(AMD_FW_MP5)
|
|
ENUM_TO_STRING(AMD_S0I3_DRIVER)
|
|
ENUM_TO_STRING(AMD_FW_ABL_PUBKEY)
|
|
ENUM_TO_STRING(AMD_PSP_FUSE_CHAIN)
|
|
ENUM_TO_STRING(AMD_FW_BIOS_TABLE)
|
|
ENUM_TO_STRING(AMD_FW_RECOVERYAB_A)
|
|
ENUM_TO_STRING(AMD_FW_RECOVERYAB_B)
|
|
|
|
// BIOS types
|
|
ENUM_TO_STRING(AMD_BIOS_SIG)
|
|
ENUM_TO_STRING(AMD_BIOS_APCB)
|
|
ENUM_TO_STRING(AMD_BIOS_APOB)
|
|
ENUM_TO_STRING(AMD_BIOS_BIN)
|
|
ENUM_TO_STRING(AMD_BIOS_APOB_NV)
|
|
ENUM_TO_STRING(AMD_BIOS_PMUI)
|
|
ENUM_TO_STRING(AMD_BIOS_PMUD)
|
|
ENUM_TO_STRING(AMD_BIOS_UCODE)
|
|
ENUM_TO_STRING(AMD_BIOS_APCB_BK)
|
|
ENUM_TO_STRING(AMD_BIOS_EARLY_VGA)
|
|
ENUM_TO_STRING(AMD_BIOS_MP2_CFG)
|
|
ENUM_TO_STRING(AMD_BIOS_PSP_SHARED_MEM)
|
|
ENUM_TO_STRING(AMD_BIOS_L2_PTR)
|
|
ENUM_TO_STRING(AMD_BIOS_INVALID)
|
|
default:
|
|
break;
|
|
}
|
|
#undef ENUM_TO_STRING
|
|
return usprintf("PSP file %02x - %02x", type, subtype);
|
|
}
|
|
|
|
// Returns U_SUCCESS when the table is known, fully within the specified UByteArray
|
|
// and the checksum is valid
|
|
USTATUS FfsParser::isValidTable(const UByteArray & amdImage, const UINT32 offset)
|
|
{
|
|
if (offset % 16) {
|
|
msg(usprintf("%s: Invalid offset specified: %x", __FUNCTION__, offset));
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((offset + sizeof(UINT32)) > amdImage.size()) {
|
|
msg(usprintf("%s: Offset outside of image: %x", __FUNCTION__, offset));
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
const UINT32 *cookie = (const UINT32 *)(amdImage.constData() + offset);
|
|
UINT32 headerSize = 0;
|
|
switch (*cookie) {
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
headerSize = sizeof(AMD_PSP_COMBO_DIRECTORY_HEADER);
|
|
break;
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
headerSize = sizeof(AMD_PSP_DIRECTORY_HEADER);
|
|
break;
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
headerSize = sizeof(AMD_BIOS_DIRECTORY_HEADER);
|
|
break;
|
|
default:
|
|
msg(usprintf("%s: Unknown cookie 0x%x", __FUNCTION__, *cookie));
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Full header is part of image?
|
|
if ((offset + headerSize) > amdImage.size()) {
|
|
msg(usprintf("%s: Table header at 0x%X not within image", __FUNCTION__, offset));
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Fill in table specific details
|
|
UINT32 checksumStart;
|
|
UINT32 checksumSize;
|
|
UINT32 size;
|
|
UINT32 checksum;
|
|
switch (*cookie) {
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_PSP_COMBO_DIRECTORY_HEADER* hdr = (const AMD_PSP_COMBO_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_PSP_COMBO_ENTRY) + sizeof(AMD_PSP_COMBO_DIRECTORY_HEADER);
|
|
checksum = hdr->Checksum;
|
|
|
|
checksumStart = offset + 8; // Start after checksum field
|
|
checksumSize = size - 8; // Subtract cookie and checksum field
|
|
break;
|
|
}
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_PSP_DIRECTORY_HEADER* hdr = (const AMD_PSP_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_PSP_DIRECTORY_ENTRY) + sizeof(AMD_PSP_DIRECTORY_HEADER);
|
|
checksum = hdr->Checksum;
|
|
|
|
checksumStart = offset + 8; // Start after checksum field
|
|
checksumSize = size - 8; // Subtract cookie and checksum field
|
|
break;
|
|
}
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_BIOS_DIRECTORY_HEADER* hdr = (const AMD_BIOS_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_BIOS_DIRECTORY_ENTRY) + sizeof(AMD_BIOS_DIRECTORY_HEADER);
|
|
checksum = hdr->Checksum;
|
|
|
|
checksumStart = offset + 8; // Start after checksum field
|
|
checksumSize = size - 8; // Subtract cookie and checksum field
|
|
break;
|
|
}
|
|
default:
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Full table is part of image?
|
|
if ((offset + size) > amdImage.size()) {
|
|
msg(usprintf("%s: Table at 0x%X not within image", __FUNCTION__, offset));
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Validate table checksum
|
|
const UINT32 calcChecksum = fletcher32(amdImage.mid(checksumStart, checksumSize));
|
|
if (calcChecksum != checksum) {
|
|
msg(usprintf("%s: Header at offset %x checksum mismatch, expected %x found %x", __FUNCTION__, offset, checksum, calcChecksum));
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Returns U_SUCCESS when the table could be extracted from the image
|
|
USTATUS FfsParser::extractTable(const UByteArray & amdImage, const UINT32 offset, UByteArray & table)
|
|
{
|
|
USTATUS result;
|
|
|
|
result = isValidTable(amdImage, offset);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
const UINT32 *cookie = (const UINT32 *)(amdImage.constData() + offset);
|
|
UINT32 size;
|
|
switch (*cookie) {
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_PSP_COMBO_DIRECTORY_HEADER* hdr = (const AMD_PSP_COMBO_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_PSP_COMBO_ENTRY) + sizeof(AMD_PSP_COMBO_DIRECTORY_HEADER);
|
|
break;
|
|
}
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_PSP_DIRECTORY_HEADER* hdr = (const AMD_PSP_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_PSP_DIRECTORY_ENTRY) + sizeof(AMD_PSP_DIRECTORY_HEADER);
|
|
break;
|
|
}
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
{
|
|
const AMD_BIOS_DIRECTORY_HEADER* hdr = (const AMD_BIOS_DIRECTORY_HEADER*)(cookie);
|
|
size = hdr->Num_Entries * sizeof(AMD_BIOS_DIRECTORY_ENTRY) + sizeof(AMD_BIOS_DIRECTORY_HEADER);
|
|
break;
|
|
}
|
|
default:
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
table = amdImage.mid(offset, size);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::parseISH(const UByteArray & fileImage, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
USTATUS result;
|
|
|
|
// Parse ISH table
|
|
const AMD_ISH_DIRECTORY_TABLE* ishTable = (const AMD_ISH_DIRECTORY_TABLE*)fileImage.constData();
|
|
UINTN length = sizeof(AMD_ISH_DIRECTORY_TABLE);
|
|
|
|
if (fileImage.size() < length) {
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Checksum starts right after checksum field
|
|
UByteArray data = fileImage.mid(4, length - 4);
|
|
const UINT32 Checksum = fletcher32(data);
|
|
if (ishTable->Checksum != Checksum) {
|
|
msg(usprintf("%s: ISH table checksum mismatch, expected %x found %x", __FUNCTION__, ishTable->Checksum, Checksum));
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
data = fileImage.mid(0, length);
|
|
|
|
// ISH directory
|
|
UString name("ISH directory");
|
|
UString info = usprintf("Full size : %Xh (%u)\nPl2_location : %Xh (%u)\nBoot_Priority: %Xh (%u)\nSlot_Max_Size: %Xh (%u)\nPsp_Id : %Xh (%u)\n",
|
|
(UINT32)length, (UINT32)length,
|
|
ishTable->Pl2_location, ishTable->Pl2_location,
|
|
ishTable->Boot_Priority, ishTable->Boot_Priority,
|
|
ishTable->Slot_Max_Size, ishTable->Slot_Max_Size,
|
|
ishTable->Psp_Id, ishTable->Psp_Id);
|
|
|
|
// Add ISH directory image tree item
|
|
index = model->addItem(0, Types::DirectoryTable, Subtypes::ISHDirectory, name, UString(), info, UByteArray(), data, UByteArray(), Fixed, parent);
|
|
const UModelIndex ishIndex = index;
|
|
|
|
// Add PSPL2 directory tree item
|
|
result = parsePSPDir(ishTable->Pl2_location, ishIndex, index);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to parse PSPL2 pointed to by ISH", __FUNCTION__));
|
|
return result;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::findByRange(const UINT32 base, const UINT32 size, const UModelIndex & index, UModelIndex & found)
|
|
{
|
|
// Sort by inserting
|
|
for (int i = 0; i < model->rowCount(index); i++) {
|
|
UModelIndex current = index.model()->index(i, 0, index);
|
|
UINT32 currentSize = (model->header(current).size() + model->body(current).size() + model->tail(current).size());
|
|
|
|
// Non overlapping case
|
|
if ((model->base(current) + currentSize) < base || model->base(current) > (base + size))
|
|
continue;
|
|
// Expect range to start at base or earlier
|
|
if (model->base(current) > base)
|
|
continue;
|
|
// Existing region is too small
|
|
if (currentSize < size)
|
|
continue;
|
|
found = current;
|
|
|
|
findByRange(base, size, current, found);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
return U_ITEM_NOT_FOUND;
|
|
}
|
|
|
|
USTATUS FfsParser::insertRegion(UINT32 imageOffset, const UINT32 size, const UString name, UINT8 type, UINT8 subType, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
UString parentName = (model->type(parent) != Types::Image) ? model->name(parent): UString();
|
|
USTATUS result;
|
|
|
|
UModelIndex parentImageIndex = (model->type(parent) != Types::Image) ? model->findParentOfType(parent, Types::Image) : parent;
|
|
UByteArray parentImage = model->header(parentImageIndex) + model->body(parentImageIndex) + model->tail(parentImageIndex);
|
|
UModelIndex containerIndex = parentImageIndex;
|
|
|
|
msg(usprintf("%s: inserting %x-%x: ", __FUNCTION__, imageOffset, imageOffset + size) + name);
|
|
|
|
UModelIndex findIndex;
|
|
result = findByRange(imageOffset, size, parentImageIndex, findIndex);
|
|
if (result == U_SUCCESS && findIndex.isValid()) {
|
|
msg(usprintf("\n%s: findByRange returned model type %x:%x at offset %x, size %x", __FUNCTION__, model->type(findIndex), model->subtype(findIndex), model->base(findIndex), model->body(findIndex).size()));
|
|
msg(usprintf("%s: was looking for model type %x:%x at offset %x, size %x\n", __FUNCTION__, type, subType, imageOffset, size));
|
|
}
|
|
if (result == U_SUCCESS && findIndex.isValid() && model->type(findIndex) == type && model->subtype(findIndex) == subType &&
|
|
model->base(findIndex) == imageOffset && (model->header(findIndex).size() + model->body(findIndex).size() + model->tail(findIndex).size()) == size) {
|
|
UString info;
|
|
info += UString("Parent : ") + parentName + UString("\n");
|
|
info += usprintf("ParentOffset : %Xh\n", model->base(parent));
|
|
|
|
model->addInfo(findIndex, info);
|
|
|
|
msg(usprintf("%s: Skipping already added model type %x:%x at offset %x", __FUNCTION__, type, subType, imageOffset));
|
|
return U_SUCCESS;
|
|
} else if (result == U_SUCCESS && findIndex.isValid() && model->type(findIndex) != type && model->base(findIndex) <= imageOffset &&
|
|
(model->header(findIndex).size() + model->body(findIndex).size() + model->tail(findIndex).size()) > size) {
|
|
containerIndex = findIndex;
|
|
}
|
|
|
|
UString info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\n",
|
|
size, size, imageOffset, imageOffset);
|
|
|
|
if (model->type(parent) != Types::Image) {
|
|
info += UString("Parent : ") + parentName + UString("\n");
|
|
info += usprintf("ParentOffset : %Xh\n", model->base(parent));
|
|
}
|
|
|
|
// Sort by inserting
|
|
for (int i = 0; i < model->rowCount(containerIndex); i++) {
|
|
UModelIndex current = index.model()->index(i, 0, containerIndex);
|
|
|
|
if (model->base(current) > imageOffset) {
|
|
// Add directory file tree item
|
|
index = model->addItem(imageOffset - model->base(containerIndex), type, subType, name, parentName, info, UByteArray(), parentImage.mid(imageOffset, size), UByteArray(), Fixed, current, CREATE_MODE_BEFORE);
|
|
return U_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Add directory file tree item
|
|
index = model->addItem(imageOffset - model->base(containerIndex), type, subType, name, parentName, info, UByteArray(), parentImage.mid(imageOffset, size), UByteArray(), Fixed, containerIndex);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::insertDirectoryFile(const UINT32 imageOffset, const UINT32 size, const UString name, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
/*
|
|
* Directories can reference that same BIOS region multiple
|
|
* times, due to A/B partition support or because multiple SoC
|
|
* versions are supported in one ROM.
|
|
* Check if the requested file already exists and do not create
|
|
* a new region.
|
|
*/
|
|
return insertRegion(imageOffset, size, name, Types::Region, Subtypes::PspDirectoryFile, parent, index);
|
|
}
|
|
|
|
USTATUS FfsParser::parseComboDir(const UINT32 offset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
UINTN level = 0;
|
|
USTATUS result;
|
|
|
|
UModelIndex parentImageIndex = (model->type(parent) == Types::Image) ? parent : model->findParentOfType(parent, Types::Image);
|
|
UByteArray parentImage = model->header(parentImageIndex) + model->body(parentImageIndex) + model->tail(parentImageIndex);
|
|
UString parentName = (model->type(parent) != Types::Image) ? model->name(parent) : "";
|
|
|
|
UByteArray tableImage;
|
|
// extractTable also validates the input
|
|
result = extractTable(parentImage, offset, tableImage);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
const AMD_PSP_COMBO_DIRECTORY_HEADER* hdr = (const AMD_PSP_COMBO_DIRECTORY_HEADER*)(tableImage.data());
|
|
|
|
// Check PSP signature
|
|
switch (hdr->Cookie) {
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
level = 1;
|
|
break;
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
level = 2;
|
|
break;
|
|
default:
|
|
msg(usprintf("%s: Combo header has invalid cookie 0x%x", __FUNCTION__, hdr->Cookie));
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
// PSP combo directory table
|
|
UString name = usprintf("PSP %s directory table", (level == 1) ? "COMBO" : "BHD2");
|
|
UString info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nEntry count : %u\n",
|
|
(UINT32)tableImage.size(), (UINT32)tableImage.size(),
|
|
(UINT32)offset, (UINT32)offset,
|
|
hdr->Num_Entries);
|
|
if (parentName != "") {
|
|
name += "(" + parentName + ")";
|
|
}
|
|
|
|
// Add PSP combo directory table
|
|
result = insertRegion(offset, tableImage.size(), name, Types::DirectoryTable, Subtypes::ComboDirectory, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
const UModelIndex pspHeaderIndex = index;
|
|
|
|
const AMD_PSP_COMBO_ENTRY* Entry = (const AMD_PSP_COMBO_ENTRY*)(hdr + 1);
|
|
for (UINTN i = 0; i < hdr->Num_Entries; i++) {
|
|
// Fill directory table
|
|
REGION_INFO psp_entry;
|
|
psp_entry.type = Entry[i].Id;
|
|
psp_entry.offset = sizeof(AMD_PSP_COMBO_DIRECTORY_HEADER) + i * sizeof(AMD_PSP_COMBO_ENTRY);
|
|
psp_entry.length = sizeof(AMD_PSP_COMBO_ENTRY);
|
|
psp_entry.data = tableImage.mid(psp_entry.offset, psp_entry.length);
|
|
|
|
UString entryName = name + " entry";
|
|
|
|
// PSP table entry
|
|
info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nID Select : %Xh\nID : %Xh\nLvl2_Address : %llXh\n",
|
|
(UINT32)psp_entry.length, (UINT32)psp_entry.length,
|
|
(UINT32)psp_entry.offset, (UINT32)psp_entry.offset,
|
|
Entry[i].Id_Sel, Entry[i].Id, Entry[i].Lvl2_Address);
|
|
|
|
// Add PSP table entry image tree item
|
|
index = model->addItem(psp_entry.offset, Types::DirectoryTableEntry, Subtypes::ComboDirectory, entryName, UString(), info, UByteArray(), psp_entry.data, UByteArray(), Fixed, pspHeaderIndex);
|
|
UModelIndex pspEntryIndex = index;
|
|
|
|
if (isSupportedCookie(parentImage, Entry[i].Lvl2_Address) == U_SUCCESS) {
|
|
result = parsePSPDir(Entry[i].Lvl2_Address, pspEntryIndex, index);
|
|
}
|
|
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::decompressBios(UModelIndex& parent)
|
|
{
|
|
USTATUS result;
|
|
|
|
UByteArray parentImage = model->header(parent) + model->body(parent) + model->tail(parent);
|
|
if (parentImage.size() < 256) {
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
parentImage = parentImage.mid(256, parentImage.size() - 256);
|
|
UByteArray output;
|
|
|
|
result = zlibDecompress(parentImage, output);
|
|
if (result) {
|
|
msg(usprintf("%s: decompression failed with error ", __FUNCTION__) + errorCodeToUString(result), parent);
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
model->setUncompressedData(parent, output);
|
|
model->setCompressed(parent, true);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::parseBiosDir(const UINT32 imageOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
UINTN level = 0;
|
|
USTATUS result;
|
|
|
|
UModelIndex parentImageIndex = (model->type(parent) == Types::Image) ? parent : model->findParentOfType(parent, Types::Image);
|
|
UByteArray parentImage = model->header(parentImageIndex) + model->body(parentImageIndex) + model->tail(parentImageIndex);
|
|
UString parentName = (model->type(parent) != Types::Image) ? model->name(parent) : "";
|
|
|
|
UByteArray tableImage;
|
|
// extractTable also validates the input
|
|
result = extractTable(parentImage, imageOffset, tableImage);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to extract table", __FUNCTION__));
|
|
return result;
|
|
}
|
|
|
|
const AMD_BIOS_DIRECTORY_HEADER* hdr = (const AMD_BIOS_DIRECTORY_HEADER*)(tableImage.data());
|
|
|
|
// Check Bios directory signature
|
|
switch (hdr->Cookie) {
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
level = 1;
|
|
break;
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
level = 2;
|
|
break;
|
|
default:
|
|
msg(usprintf("%s: Bios header has invalid cookie 0x%x", __FUNCTION__, hdr->Cookie));
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
// Bios directory table
|
|
UString name = usprintf("Bios %s directory table", (level == 1) ? "COMBO" : "BHD2");
|
|
UString info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nEntry count : %u\n",
|
|
(UINT32)tableImage.size(), (UINT32)tableImage.size(),
|
|
(UINT32)imageOffset, (UINT32)imageOffset,
|
|
hdr->Num_Entries);
|
|
if (parentName != "") {
|
|
name += "(" + parentName + ")";
|
|
}
|
|
|
|
// Add Bios directory table
|
|
result = insertRegion(imageOffset, tableImage.size(), name, Types::DirectoryTable, Subtypes::BiosDirectory, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
const UModelIndex biosHeaderIndex = index;
|
|
|
|
const AMD_BIOS_DIRECTORY_ENTRY* Entry = (const AMD_BIOS_DIRECTORY_ENTRY*)(hdr + 1);
|
|
for (UINTN i = 0; i < hdr->Num_Entries; i++) {
|
|
const UINT64 addr = Entry[i].Address_AddressMode & 0x3fffffffffffffffULL;
|
|
const UINT64 mode = Entry[i].Address_AddressMode >> 62;
|
|
const UINT32 size = Entry[i].Size;
|
|
|
|
// Fill directory table
|
|
REGION_INFO bios_entry;
|
|
bios_entry.type = Entry[i].Type;
|
|
bios_entry.offset = sizeof(AMD_BIOS_DIRECTORY_HEADER) + i * sizeof(AMD_BIOS_DIRECTORY_ENTRY);
|
|
bios_entry.length = sizeof(AMD_BIOS_DIRECTORY_ENTRY);
|
|
bios_entry.data = tableImage.mid(bios_entry.offset, bios_entry.length);
|
|
const UString fileName = pspFileName(Entry[i].Type, Entry[i].RegionType);
|
|
|
|
// Bios table entry
|
|
info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nType : %Xh\nRegionType : %Xh\nFlags : %Xh\nAddress : %llXh\nAddressMode : %Xh\nDestination : %llXh\n",
|
|
(UINT32)bios_entry.length, (UINT32)bios_entry.length,
|
|
(UINT32)bios_entry.offset, (UINT32)bios_entry.offset,
|
|
Entry[i].Type, Entry[i].RegionType, Entry[i].Flags, addr, mode, Entry[i].Destination);
|
|
|
|
// Add Bios table entry image tree item
|
|
index = model->addItem(bios_entry.offset, Types::DirectoryTableEntry, Subtypes::BiosDirectory, fileName, UString(), info, UByteArray(), bios_entry.data, UByteArray(), Fixed, biosHeaderIndex);
|
|
|
|
// Look for files based on directory table
|
|
UINT64 entry_offset = 0;
|
|
result = amdRelativeOffset(biosHeaderIndex, addr, mode, entry_offset);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: amdRelativeOffset failed for file ", __FUNCTION__) + name);
|
|
continue;
|
|
}
|
|
if (size == 0x00000000 || size == 0xffffffff) {
|
|
msg(usprintf("%s: Skipping BIOS file %d with no size", __FUNCTION__, i));
|
|
continue;
|
|
}
|
|
|
|
// BIOS file entry
|
|
info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nType : %Xh\nRegionType : %Xh\nFlags : %Xh\n Reset Image : %s\n Copy Image : %s\n ReadOnly : %s\n Compressed : %s",
|
|
(UINT32)size, (UINT32)size,
|
|
(UINT32)entry_offset, (UINT32)entry_offset,
|
|
Entry[i].Type, Entry[i].RegionType, Entry[i].Flags,
|
|
(Entry[i].Flags & 1) ? "true" : "false",
|
|
(Entry[i].Flags & 2) ? "true" : "false",
|
|
(Entry[i].Flags & 4) ? "true" : "false",
|
|
(Entry[i].Flags & 8) ? "true" : "false"
|
|
);
|
|
|
|
UModelIndex findIndex = model->findByBase(entry_offset - model->base(parentImageIndex));
|
|
if (findIndex.isValid() && model->type(findIndex) == Types::Region && model->subtype(findIndex) == Subtypes::BiosRegion) {
|
|
msg(usprintf("%s: Skipping already added BIOS file", __FUNCTION__) + fileName);
|
|
continue;
|
|
}
|
|
|
|
result = insertDirectoryFile((UINT32)entry_offset, size, fileName, biosHeaderIndex, index);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to create directory file", __FUNCTION__) + fileName);
|
|
}
|
|
const UModelIndex biosFileIndex = index;
|
|
|
|
switch (Entry[i].Type) {
|
|
case AMD_BIOS_L2_PTR:
|
|
if (level == 1) {
|
|
result = parseBiosDir(entry_offset, parentImageIndex, index);
|
|
}
|
|
break;
|
|
case AMD_BIOS_BIN:
|
|
|
|
if (Entry[i].Flags & 8) {
|
|
result = decompressBios(index);
|
|
parseGenericImage(model->uncompressedData(biosFileIndex), 0, biosFileIndex, index);
|
|
} else {
|
|
parseGenericImage(model->body(biosFileIndex), 0, biosFileIndex, index);
|
|
}
|
|
|
|
break;
|
|
}
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to parse file", __FUNCTION__) + fileName);
|
|
}
|
|
}
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS FfsParser::parsePSPDir(const UINT32 offset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
UINTN level;
|
|
USTATUS result;
|
|
|
|
UModelIndex parentImageIndex = (model->type(parent) == Types::Image) ? parent : model->findParentOfType(parent, Types::Image);
|
|
UByteArray parentImage = model->header(parentImageIndex) + model->body(parentImageIndex) + model->tail(parentImageIndex);
|
|
UString parentName = (model->type(parent) != Types::Image) ? model->name(parent) : "";
|
|
|
|
UByteArray tableImage;
|
|
// extractTable also validates the input
|
|
result = extractTable(parentImage, offset, tableImage);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
const AMD_PSP_DIRECTORY_HEADER* hdr = (const AMD_PSP_DIRECTORY_HEADER*)(tableImage.constData());
|
|
|
|
// Check PSP signature
|
|
switch (hdr->Cookie) {
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
level = 1;
|
|
break;
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
level = 2;
|
|
break;
|
|
default:
|
|
msg(usprintf("%s: PSP header has invalid cookie 0x%x", __FUNCTION__, hdr->Cookie));
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
|
|
// Update REGION_INFO
|
|
REGION_INFO psp_directory;
|
|
if (level == 1)
|
|
psp_directory.type = Subtypes::PspL1DirectoryRegion;
|
|
else
|
|
psp_directory.type = Subtypes::PspL2DirectoryRegion;
|
|
psp_directory.offset = offset;
|
|
psp_directory.length = (hdr->Additional_Info_Fields & 0x3ff) << 12;
|
|
|
|
// Validate image length
|
|
if ((psp_directory.offset + psp_directory.length) > parentImage.size()) {
|
|
return U_INVALID_IMAGE;
|
|
}
|
|
psp_directory.data = parentImage.mid(psp_directory.offset, psp_directory.length);
|
|
|
|
// PSP directory
|
|
UString name = usprintf("PSPL%d directory", level);
|
|
if (parentName != "") {
|
|
name += " (" + parentName + ")";
|
|
}
|
|
UString info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nPSP Table Size: %Xh (%u)",
|
|
(UINT32)psp_directory.length, (UINT32)psp_directory.length,
|
|
(UINT32)psp_directory.offset, (UINT32)psp_directory.offset,
|
|
(UINT32)tableImage.size(), (UINT32)tableImage.size());
|
|
|
|
// Add PSP directory region
|
|
result = insertRegion(offset, (hdr->Additional_Info_Fields & 0x3ff) << 12, name, Types::Region, psp_directory.type, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
const UModelIndex pspRegionIndex = index;
|
|
|
|
// PSP directory table
|
|
name = UString("PSP directory table");
|
|
info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nEntry count : %u\n",
|
|
(UINT32)tableImage.size(), (UINT32)tableImage.size(),
|
|
(UINT32)offset, (UINT32)offset,
|
|
hdr->Num_Entries);
|
|
|
|
// Add PSP directory table
|
|
index = model->addItem(0, Types::DirectoryTable, Subtypes::PSPDirectory, name, UString(), info, UByteArray(), tableImage, UByteArray(), Fixed, pspRegionIndex);
|
|
UModelIndex pspHeaderIndex = index;
|
|
|
|
const AMD_PSP_DIRECTORY_ENTRY* Entry = (const AMD_PSP_DIRECTORY_ENTRY*)(hdr + 1);
|
|
for (UINTN i = 0; i < hdr->Num_Entries; i++) {
|
|
const UINT8 type = Entry[i].Type;
|
|
const UINT8 subtype = Entry[i].SubProg;
|
|
const UINT16 flags = Entry[i].Flags;
|
|
const UINT64 addr = Entry[i].Address_AddressMode & 0x3fffffffffffffffULL;
|
|
const UINT64 mode = Entry[i].Address_AddressMode >> 62;
|
|
UINT32 size = Entry[i].Size;
|
|
const UString fileName = pspFileName(type, subtype);
|
|
|
|
msg(usprintf("%s: Entry%d: ", __FUNCTION__, i) + fileName);
|
|
|
|
// Fill directory table
|
|
|
|
REGION_INFO psp_entry;
|
|
psp_entry.type = type;
|
|
psp_entry.offset = sizeof(AMD_PSP_DIRECTORY_HEADER) + i * sizeof(AMD_PSP_DIRECTORY_ENTRY);
|
|
psp_entry.length = sizeof(AMD_PSP_DIRECTORY_ENTRY);
|
|
psp_entry.data = psp_directory.data.mid(psp_entry.offset, psp_entry.length);
|
|
|
|
// PSP table entry
|
|
UString info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nFileType : %Xh\nSubProg : %Xh\nFlags : %Xh\nFile Address : %llxh\nAddressMode : %xh\nFile Size : %xh (%u)\n",
|
|
(UINT32)psp_entry.length, (UINT32)psp_entry.length,
|
|
(UINT32)psp_entry.offset, (UINT32)psp_entry.offset,
|
|
type, subtype, flags, addr, mode, size, size);
|
|
|
|
// Add PSP table entry image tree item
|
|
index = model->addItem(psp_entry.offset, Types::DirectoryTableEntry, Subtypes::PSPDirectory, fileName, UString(), info, UByteArray(), psp_entry.data, UByteArray(), Fixed, pspHeaderIndex);
|
|
|
|
// Look for files based on directory table
|
|
UINT64 entry_offset = 0;
|
|
result = amdRelativeOffset(pspHeaderIndex, addr, mode, entry_offset);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: amdRelativeOffset failed for file ", __FUNCTION__) + fileName);
|
|
continue;
|
|
}
|
|
|
|
// Some firmwares are broken and set size 0 for ISH directory table
|
|
switch (type) {
|
|
case AMD_FW_RECOVERYAB_A:
|
|
case AMD_FW_RECOVERYAB_B:
|
|
size = 4096;
|
|
}
|
|
|
|
if (size == 0x00000000 || size == 0xffffffff) {
|
|
msg(usprintf("%s: Skipping PSP file with no size ", __FUNCTION__) + fileName);
|
|
continue;
|
|
}
|
|
|
|
// PSP file entry
|
|
info = usprintf("Full size : %Xh (%u)\nOffset : %Xh (%u)\nFileType : %Xh\nSubProg : %Xh\nFlags : %Xh",
|
|
(UINT32)size, (UINT32)size,
|
|
(UINT32)entry_offset, (UINT32)entry_offset,
|
|
type, subtype, flags);
|
|
|
|
// Add PSP file tree item
|
|
UModelIndex parentIndex = model->findByBase(entry_offset);
|
|
if (!parentIndex.isValid()) {
|
|
parentIndex = model->findParentOfType(pspRegionIndex, Types::Image);
|
|
}
|
|
|
|
result = insertDirectoryFile((UINT32)entry_offset, size, fileName, pspRegionIndex, index);
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to create directory file", __FUNCTION__) + fileName);
|
|
}
|
|
const UModelIndex pspFileIndex = index;
|
|
|
|
switch (type) {
|
|
case AMD_FW_RECOVERYAB_A:
|
|
case AMD_FW_RECOVERYAB_B:
|
|
if (level == 1) {
|
|
// Can be a PSPL2 table or ISH directory
|
|
result = isValidTable(parentImage, model->base(pspFileIndex));
|
|
if (result == U_SUCCESS) {
|
|
result = parsePSPDir(model->base(pspFileIndex), pspFileIndex, index);
|
|
} else {
|
|
result = parseISH(model->body(pspFileIndex), pspFileIndex, index);
|
|
}
|
|
}
|
|
break;
|
|
case AMD_FW_BIOS_TABLE:
|
|
//if (level == 2) {
|
|
result = parseBiosDir(entry_offset, pspFileIndex, index);
|
|
// }
|
|
break;
|
|
}
|
|
if (result != U_SUCCESS) {
|
|
msg(usprintf("%s: Failed to parse file ", __FUNCTION__) + fileName);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Returns U_SUCCESS when a supported cookie is found at the specified offset
|
|
USTATUS FfsParser::isSupportedCookie(const UByteArray & amdImage, const UINT32 offset)
|
|
{
|
|
if (offset % 16) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((offset + sizeof(UINT32)) > amdImage.size()) {
|
|
return U_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
const UINT32 *cookie = (const UINT32 *)(amdImage.constData() + offset);
|
|
|
|
switch (*cookie) {
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
return U_SUCCESS;
|
|
}
|
|
return U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
|
|
// Decodes any supported PSP table found at the specified offset
|
|
USTATUS FfsParser::decodePSPTableAny(const UByteArray & amdImage, const UINT32 offset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
USTATUS result;
|
|
|
|
if (offset % 16) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
result = isValidTable(amdImage, offset);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
const UINT32 *cookie = (const UINT32 *)(amdImage.constData() + offset);
|
|
switch (*cookie) {
|
|
case AMD_PSP_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSPL2_DIRECTORY_HEADER_SIGNATURE:
|
|
result = parsePSPDir(offset, parent, index);
|
|
break;
|
|
case AMD_PSP_COMBO_DIRECTORY_HEADER_SIGNATURE:
|
|
case AMD_PSP_BHD2_DIRECTORY_HEADER_SIGNATURE:
|
|
result = parseComboDir(offset, parent, index);
|
|
break;
|
|
case AMD_BIOS_HEADER_SIGNATURE:
|
|
case AMD_BHDL2_HEADER_SIGNATURE:
|
|
result = parseBiosDir(offset, parent, index);
|
|
break;
|
|
default:
|
|
result = U_UNKNOWN_ITEM_TYPE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
USTATUS FfsParser::parseEFTable(const UByteArray & amdImage, const UINT32 efOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
USTATUS result;
|
|
UString name("Embedded Firmware Table");
|
|
if (efOffset + sizeof(AMD_EMBEDDED_FIRMWARE) > amdImage.size()) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
AMD_EMBEDDED_FIRMWARE* ef_descriptor = (AMD_EMBEDDED_FIRMWARE*)(amdImage.constData() + efOffset);
|
|
|
|
msg(usprintf("%s: IMC_Entry 0x%x", __FUNCTION__, ef_descriptor->IMC_Entry));
|
|
msg(usprintf("%s: GEC_Entry 0x%x", __FUNCTION__, ef_descriptor->GEC_Entry));
|
|
msg(usprintf("%s: xHCI_Entry 0x%x", __FUNCTION__, ef_descriptor->xHCI_Entry));
|
|
msg(usprintf("%s: Efs_Generation 0x%x", __FUNCTION__, ef_descriptor->Efs_Generation));
|
|
msg(usprintf("%s: PSP_Directory 0x%x", __FUNCTION__, ef_descriptor->PSP_Directory));
|
|
msg(usprintf("%s: New_PSP_Directory 0x%x", __FUNCTION__, ef_descriptor->New_PSP_Directory));
|
|
msg(usprintf("%s: BIOS0_Entry 0x%x", __FUNCTION__, ef_descriptor->BIOS0_Entry));
|
|
msg(usprintf("%s: BIOS1_Entry 0x%x", __FUNCTION__, ef_descriptor->BIOS1_Entry));
|
|
msg(usprintf("%s: BIOS2_Entry 0x%x", __FUNCTION__, ef_descriptor->BIOS2_Entry));
|
|
msg(usprintf("%s: BIOS3_Entry 0x%x", __FUNCTION__, ef_descriptor->BIOS3_Entry));
|
|
|
|
// The specification between SoCs changed a lot, and at this point the
|
|
// SoC/PSP ID isn't known. Attempt to decode all tables without assuming
|
|
// to find a specific type
|
|
if (isSupportedCookie(amdImage, ef_descriptor->PSP_Directory) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->PSP_Directory, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
if (isSupportedCookie(amdImage, ef_descriptor->New_PSP_Directory) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->New_PSP_Directory, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
if (isSupportedCookie(amdImage, ef_descriptor->BIOS0_Entry) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->BIOS0_Entry, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
if (isSupportedCookie(amdImage, ef_descriptor->BIOS1_Entry) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->BIOS1_Entry, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
if (isSupportedCookie(amdImage, ef_descriptor->BIOS2_Entry) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->BIOS2_Entry, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
if (isSupportedCookie(amdImage, ef_descriptor->BIOS3_Entry) == U_SUCCESS) {
|
|
result = decodePSPTableAny(amdImage, ef_descriptor->BIOS3_Entry, parent, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Creates the OSI Fletcher checksum. See 8473-1, Appendix C, section C.3.
|
|
* The checksum field of the passed PDU does not need to be reset to zero.
|
|
*
|
|
* The "Fletcher Checksum" was proposed in a paper by John G. Fletcher of
|
|
* Lawrence Livermore Labs. The Fletcher Checksum was proposed as an
|
|
* alternative to cyclical redundancy checks because it provides error-
|
|
* detection properties similar to cyclical redundancy checks but at the
|
|
* cost of a simple summation technique. Its characteristics were first
|
|
* published in IEEE Transactions on Communications in January 1982. One
|
|
* version has been adopted by ISO for use in the class-4 transport layer
|
|
* of the network protocol.
|
|
*
|
|
* This program expects:
|
|
* stdin: The input file to compute a checksum for. The input file
|
|
* not be longer than 256 bytes.
|
|
* stdout: Copied from the input file with the Fletcher's Checksum
|
|
* inserted 8 bytes after the beginning of the file.
|
|
* stderr: Used to print out error messages.
|
|
*/
|
|
UINT32 FfsParser::fletcher32(const UByteArray &Image)
|
|
{
|
|
UINT32 c0;
|
|
UINT32 c1;
|
|
UINT32 checksum;
|
|
INTN index;
|
|
const UINT16 *pptr = (const UINT16 *)Image.constData();
|
|
|
|
INTN length = Image.size() / 2;
|
|
|
|
c0 = 0xFFFF;
|
|
c1 = 0xFFFF;
|
|
|
|
while (length) {
|
|
index = length >= 359 ? 359 : length;
|
|
length -= index;
|
|
do {
|
|
c0 += *(pptr++);
|
|
c1 += c0;
|
|
} while (--index);
|
|
c0 = (c0 & 0xFFFF) + (c0 >> 16);
|
|
c1 = (c1 & 0xFFFF) + (c1 >> 16);
|
|
}
|
|
|
|
/* Sums[0,1] mod 64K + overflow */
|
|
c0 = (c0 & 0xFFFF) + (c0 >> 16);
|
|
c1 = (c1 & 0xFFFF) + (c1 >> 16);
|
|
checksum = (c1 << 16) | c0;
|
|
|
|
return checksum;
|
|
}
|
|
|
|
USTATUS FfsParser::parseAMDImage(const UByteArray & amdImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
AMD_EMBEDDED_FIRMWARE* ef_descriptor = NULL;
|
|
USTATUS result;
|
|
UINT32 ProbeOffset;
|
|
|
|
// Probe all possible locations for the header
|
|
for (ProbeOffset = AMD_EMBEDDED_FIRMWARE_OFFSET; (ProbeOffset + sizeof(AMD_EMBEDDED_FIRMWARE)) < amdImage.size(); ProbeOffset += 0x80000) {
|
|
// Store the beginning of descriptor as descriptor base address
|
|
ef_descriptor = (AMD_EMBEDDED_FIRMWARE*)(amdImage.constData() + ProbeOffset);
|
|
|
|
// Check descriptor signature
|
|
if (ef_descriptor->Signature == AMD_EMBEDDED_FIRMWARE_SIGNATURE) {
|
|
msg(usprintf("%s: Embedded firmware table found at offset 0x%x", __FUNCTION__, ProbeOffset));
|
|
// The Embedded Firmware table has no checksum, thus test if there's a valid pointer
|
|
if (isSupportedCookie(amdImage, ef_descriptor->PSP_Directory) != U_SUCCESS &&
|
|
isSupportedCookie(amdImage, ef_descriptor->New_PSP_Directory) != U_SUCCESS &&
|
|
isSupportedCookie(amdImage, ef_descriptor->BIOS0_Entry) != U_SUCCESS &&
|
|
isSupportedCookie(amdImage, ef_descriptor->BIOS1_Entry) != U_SUCCESS &&
|
|
isSupportedCookie(amdImage, ef_descriptor->BIOS2_Entry) != U_SUCCESS &&
|
|
isSupportedCookie(amdImage, ef_descriptor->BIOS3_Entry) != U_SUCCESS) {
|
|
ef_descriptor = NULL;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ef_descriptor == NULL) {
|
|
msg(usprintf("%s: Embedded firmware table not found", __FUNCTION__));
|
|
return U_ITEM_NOT_FOUND;
|
|
}
|
|
|
|
// AMD image
|
|
UString name("AMD image");
|
|
UString info = usprintf("Full size: %Xh (%u)\n",
|
|
(UINT32)amdImage.size(), (UINT32)amdImage.size());
|
|
|
|
// Set image base
|
|
imageBase = model->base(parent) + localOffset;
|
|
|
|
// Add AMD image tree item
|
|
index = model->addItem(localOffset, Types::Image, Subtypes::AmdImage, name, UString(), info, UByteArray(), amdImage, UByteArray(), Fixed, parent);
|
|
UModelIndex parentIndex = index;
|
|
|
|
result = parseEFTable(amdImage, ProbeOffset, parentIndex, index);
|
|
if (result != U_SUCCESS) {
|
|
return result;
|
|
}
|
|
#if 0
|
|
// Sort regions in ascending order
|
|
std::sort(regions.begin(), regions.end());
|
|
|
|
// Add offsets of actual regions
|
|
for (size_t i = 0; i < regions.size(); i++) {
|
|
if (regions[i].type != Subtypes::ZeroPadding && regions[i].type != Subtypes::OnePadding && regions[i].type != Subtypes::DataPadding)
|
|
info += "\n" + itemSubtypeToUString(Types::Region, regions[i].type)
|
|
+ usprintf(" region offset: %Xh", regions[i].offset + localOffset);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|