commonlib/fsp_relocate: add PE32 section support
Recently published Intel CedarIslandFSP binary contains PE images in FSP-M and FSP-S. This causes coreboot boot hang on DeltaLake servers. PI spec PI_Spec_1_7_final_Jan_2019 on uefi.org talks about FV files requiring to support SECTION_PE32 sections and FSP specification states that FSP images are created in alignment with PI specification. FSP images are relocated at build time and run time using the func fsp_component_relocate. That code only supported TE image relocation so far. The change required to add support for pe_relocate in fsp-relocate.c I had to move a few functions to top of file as they need to be used by pe_relocate but were placed below the te_relocate function. I chose to place pe_relocate function next to te_relocate. The code supports PE32 format, not PE32+, at this time. Links for PE and FSP specs are provided below for reference. Link= https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/fsp-architecture-spec-v2.pdf Link= https://uefi.org/sites/default/files/resources/PI_Spec_1_7_final_Jan_2019.pdf TESTED= This code is tested with FSP version 33A for DeltaLake boot which has FSP-M and FSP-S as PE32 sections. This FSP version does not boot on DeltaLake without this change. Change-Id: I01e2c123d74f735a647b994aa66419c9796f193e Signed-off-by: Eddie Sharma <aeddiesharma@fb.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/66819 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Reviewed-by: Nathaniel L Desimone <nathaniel.l.desimone@intel.com>
This commit is contained in:
parent
1f5c5da812
commit
1df1cf994a
|
@ -23,6 +23,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#define FSP_DBG_LVL BIOS_NEVER
|
||||
#define MASK_24BITS 0x00FFFFFF
|
||||
|
||||
/*
|
||||
* UEFI defines everything as little endian. However, this piece of code
|
||||
|
@ -68,12 +69,41 @@ static void *relative_offset(void *base, ssize_t offset)
|
|||
return (void *)loc;
|
||||
}
|
||||
|
||||
static size_t csh_size(const EFI_COMMON_SECTION_HEADER *csh)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
/* Unpack the array into a type that can be used. */
|
||||
size = 0;
|
||||
size |= read_le8(&csh->Size[0]) << 0;
|
||||
size |= read_le8(&csh->Size[1]) << 8;
|
||||
size |= read_le8(&csh->Size[2]) << 16;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t file_section_offset(const EFI_FFS_FILE_HEADER *ffsfh)
|
||||
{
|
||||
if (IS_FFS_FILE2(ffsfh))
|
||||
return sizeof(EFI_FFS_FILE_HEADER2);
|
||||
else
|
||||
return sizeof(EFI_FFS_FILE_HEADER);
|
||||
}
|
||||
|
||||
static size_t section_data_offset(const EFI_COMMON_SECTION_HEADER *csh)
|
||||
{
|
||||
if (csh_size(csh) == MASK_24BITS)
|
||||
return sizeof(EFI_COMMON_SECTION_HEADER2);
|
||||
else
|
||||
return sizeof(EFI_COMMON_SECTION_HEADER);
|
||||
}
|
||||
|
||||
static uint32_t *fspp_reloc(void *fsp, size_t fsp_size, uint32_t e)
|
||||
{
|
||||
size_t offset;
|
||||
|
||||
/* Offsets live in bits 23:0. */
|
||||
offset = e & 0xffffff;
|
||||
offset = e & MASK_24BITS;
|
||||
|
||||
/* If bit 31 is set then the offset is considered a negative value
|
||||
* relative to the end of the image using 16MiB as the offset's
|
||||
|
@ -100,6 +130,153 @@ static size_t reloc_offset(uint16_t reloc_entry)
|
|||
return reloc_entry & ((1 << 12) - 1);
|
||||
}
|
||||
|
||||
static FSP_INFO_HEADER *fsp_get_info_hdr(void *fsp, size_t fih_offset)
|
||||
{
|
||||
EFI_FFS_FILE_HEADER *ffsfh;
|
||||
EFI_COMMON_SECTION_HEADER *csh;
|
||||
FSP_INFO_HEADER *fih;
|
||||
|
||||
printk(FSP_DBG_LVL, "FSP_INFO_HEADER offset is %zx\n", fih_offset);
|
||||
|
||||
if (fih_offset == 0) {
|
||||
printk(BIOS_ERR, "FSP_INFO_HEADER offset is 0.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FSP_INFO_HEADER is located at first file in FV within first RAW section. */
|
||||
ffsfh = relative_offset(fsp, fih_offset);
|
||||
fih_offset += file_section_offset(ffsfh);
|
||||
csh = relative_offset(fsp, fih_offset);
|
||||
fih_offset += section_data_offset(csh);
|
||||
fih = relative_offset(fsp, fih_offset);
|
||||
|
||||
if (guid_compare(&ffsfh->Name, &fih_guid)) {
|
||||
printk(BIOS_ERR, "Bad FIH GUID.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (read_le8(&csh->Type) != EFI_SECTION_RAW) {
|
||||
printk(BIOS_ERR, "FIH file should have raw section: %x\n",
|
||||
read_le8(&csh->Type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (read_le32(&fih->Signature) != FSP_SIG) {
|
||||
printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
|
||||
read_le32(&fih->Signature));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fih;
|
||||
}
|
||||
|
||||
static int pe_relocate(uintptr_t new_addr, void *pe, void *fsp, size_t fih_off)
|
||||
{
|
||||
EFI_IMAGE_NT_HEADERS32 *peih;
|
||||
EFI_IMAGE_DOS_HEADER *doshdr;
|
||||
EFI_IMAGE_OPTIONAL_HEADER32 *ophdr;
|
||||
FSP_INFO_HEADER *fih;
|
||||
uint32_t roffset, rsize;
|
||||
uint32_t offset;
|
||||
uint8_t *pe_base = pe;
|
||||
uint32_t image_base;
|
||||
uint32_t img_base_off;
|
||||
uint32_t delta;
|
||||
|
||||
doshdr = pe;
|
||||
if (read_le16(&doshdr->e_magic) != EFI_IMAGE_DOS_SIGNATURE) {
|
||||
printk(BIOS_ERR, "Invalid DOS Header/magic\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
peih = relative_offset(pe, doshdr->e_lfanew);
|
||||
|
||||
if (read_le32(&peih->Signature) != EFI_IMAGE_NT_SIGNATURE) {
|
||||
printk(BIOS_ERR, "Invalid PE32 header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ophdr = &peih->OptionalHeader;
|
||||
|
||||
if (read_le16(&ophdr->Magic) != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
printk(BIOS_ERR, "No support for non-PE32 images\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fih = fsp_get_info_hdr(fsp, fih_off);
|
||||
if (fih == NULL) {
|
||||
printk(BIOS_ERR, "No Image base found for FSP PE32\n");
|
||||
return -1;
|
||||
}
|
||||
image_base = read_le32(&fih->ImageBase);
|
||||
printk(FSP_DBG_LVL, "FSP InfoHdr Image Base is %x\n", image_base);
|
||||
|
||||
delta = new_addr - image_base;
|
||||
|
||||
img_base_off = read_le32(&ophdr->ImageBase);
|
||||
printk(FSP_DBG_LVL, "lfanew 0x%x, delta-0x%x, FSP Base 0x%x, NT32ImageBase 0x%x, offset 0x%x\n",
|
||||
read_le32(&doshdr->e_lfanew),
|
||||
delta, image_base, img_base_off,
|
||||
(uint32_t)((uint8_t *)&ophdr->ImageBase - pe_base));
|
||||
|
||||
printk(FSP_DBG_LVL, "relocating PE32 image at addr - 0x%lx\n", new_addr);
|
||||
rsize = read_le32(&ophdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
|
||||
roffset = read_le32(&ophdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
|
||||
printk(FSP_DBG_LVL, "relocation table at offset-%x,size=%x\n", roffset, rsize);
|
||||
// TODO - add support for PE32+ also
|
||||
|
||||
offset = roffset;
|
||||
while (offset < (roffset + rsize)) {
|
||||
uint32_t vaddr;
|
||||
uint32_t rlen, rnum;
|
||||
uint16_t *rdata;
|
||||
uint32_t i;
|
||||
EFI_IMAGE_DATA_DIRECTORY *relocd;
|
||||
|
||||
relocd = (void *) &pe_base[offset];
|
||||
offset += sizeof(*relocd);
|
||||
// Read relocation type, offset pairs
|
||||
rlen = read_le32(&relocd->Size) - sizeof(*relocd);
|
||||
rnum = rlen / sizeof(uint16_t);
|
||||
vaddr = read_le32(&relocd->VirtualAddress);
|
||||
rdata = (uint16_t *) &pe_base[offset];
|
||||
printk(FSP_DBG_LVL, "\t%d Relocs for RVA %x\n", rnum, vaddr);
|
||||
|
||||
for (i = 0; i < rnum; i++) {
|
||||
uint16_t roff = reloc_offset(rdata[i]);
|
||||
uint16_t rtype = reloc_type(rdata[i]);
|
||||
uint32_t aoff = vaddr + roff;
|
||||
uint32_t val;
|
||||
printk(FSP_DBG_LVL, "\t\treloc type %x offset %x aoff %x, base-0x%x\n",
|
||||
rtype, roff, aoff, img_base_off);
|
||||
switch (rtype) {
|
||||
case EFI_IMAGE_REL_BASED_ABSOLUTE:
|
||||
continue;
|
||||
case EFI_IMAGE_REL_BASED_HIGHLOW:
|
||||
val = read_le32(&pe_base[aoff]);
|
||||
printk(FSP_DBG_LVL, "Adjusting %p %x -> %x\n",
|
||||
&pe_base[aoff], val, val + delta);
|
||||
write_le32(&pe_base[aoff], val + delta);
|
||||
break;
|
||||
case EFI_IMAGE_REL_BASED_DIR64:
|
||||
printk(BIOS_ERR, "Error: Unsupported DIR64\n");
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_ERR, "Error: Unsupported relocation type %d\n",
|
||||
rtype);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
offset += sizeof(*rdata) * rnum;
|
||||
}
|
||||
printk(FSP_DBG_LVL, "Adjust Image Base %x->%x\n",
|
||||
img_base_off, img_base_off + delta);
|
||||
img_base_off += delta;
|
||||
write_le32(&ophdr->ImageBase, img_base_off);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int te_relocate(uintptr_t new_addr, void *te)
|
||||
{
|
||||
EFI_TE_IMAGE_HEADER *teih;
|
||||
|
@ -201,32 +378,11 @@ static int te_relocate(uintptr_t new_addr, void *te)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static size_t csh_size(const EFI_COMMON_SECTION_HEADER *csh)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
/* Unpack the array into a type that can be used. */
|
||||
size = 0;
|
||||
size |= read_le8(&csh->Size[0]) << 0;
|
||||
size |= read_le8(&csh->Size[1]) << 8;
|
||||
size |= read_le8(&csh->Size[2]) << 16;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t section_data_offset(const EFI_COMMON_SECTION_HEADER *csh)
|
||||
{
|
||||
if (csh_size(csh) == 0x00ffffff)
|
||||
return sizeof(EFI_COMMON_SECTION_HEADER2);
|
||||
else
|
||||
return sizeof(EFI_COMMON_SECTION_HEADER);
|
||||
}
|
||||
|
||||
static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh)
|
||||
{
|
||||
size_t section_size;
|
||||
|
||||
if (csh_size(csh) == 0x00ffffff)
|
||||
if (csh_size(csh) == MASK_24BITS)
|
||||
section_size = read_le32(&SECTION2_SIZE(csh));
|
||||
else
|
||||
section_size = csh_size(csh);
|
||||
|
@ -234,14 +390,6 @@ static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh)
|
|||
return section_size - section_data_offset(csh);
|
||||
}
|
||||
|
||||
static size_t file_section_offset(const EFI_FFS_FILE_HEADER *ffsfh)
|
||||
{
|
||||
if (IS_FFS_FILE2(ffsfh))
|
||||
return sizeof(EFI_FFS_FILE_HEADER2);
|
||||
else
|
||||
return sizeof(EFI_FFS_FILE_HEADER);
|
||||
}
|
||||
|
||||
static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh)
|
||||
{
|
||||
size_t size;
|
||||
|
@ -341,13 +489,14 @@ static ssize_t relocate_remaining_items(void *fsp, size_t size,
|
|||
if (read_le32(&fih->Signature) != FSP_SIG) {
|
||||
printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
|
||||
read_le32(&fih->Signature));
|
||||
return -1;
|
||||
}
|
||||
|
||||
adjustment = (intptr_t)new_addr - read_le32(&fih->ImageBase);
|
||||
|
||||
/* Update ImageBase to reflect FSP's new home. */
|
||||
write_le32(&fih->ImageBase, adjustment + read_le32(&fih->ImageBase));
|
||||
printk(FSP_DBG_LVL, "Updated FSP InfoHdr Image Base to %x\n",
|
||||
read_le32(&fih->ImageBase));
|
||||
|
||||
/* Need to find patch table and adjust each entry. The tables
|
||||
* following FSP_INFO_HEADER have a 32-bit signature and header
|
||||
|
@ -460,6 +609,9 @@ static ssize_t relocate_fvh(uintptr_t new_addr, void *fsp, size_t fsp_size,
|
|||
while (offset + sizeof(*csh) < file_offset) {
|
||||
size_t data_size;
|
||||
size_t data_offset;
|
||||
void *section_data;
|
||||
size_t section_offset;
|
||||
uintptr_t section_addr;
|
||||
|
||||
csh = relative_offset(fsp, offset);
|
||||
|
||||
|
@ -484,15 +636,18 @@ static ssize_t relocate_fvh(uintptr_t new_addr, void *fsp, size_t fsp_size,
|
|||
* relocated address based on the TE offset within
|
||||
* FSP proper.
|
||||
*/
|
||||
if (read_le8(&csh->Type) == EFI_SECTION_TE) {
|
||||
void *te;
|
||||
size_t te_offset = offset + data_offset;
|
||||
uintptr_t te_addr = new_addr + te_offset;
|
||||
section_offset = offset + data_offset;
|
||||
section_addr = new_addr + section_offset;
|
||||
section_data = relative_offset(fsp, section_offset);
|
||||
|
||||
if (read_le8(&csh->Type) == EFI_SECTION_TE) {
|
||||
printk(FSP_DBG_LVL, "TE image at offset %zx\n",
|
||||
te_offset);
|
||||
te = relative_offset(fsp, te_offset);
|
||||
te_relocate(te_addr, te);
|
||||
section_offset);
|
||||
te_relocate(section_addr, section_data);
|
||||
} else if (read_le8(&csh->Type) == EFI_SECTION_PE32) {
|
||||
printk(FSP_DBG_LVL, "PE32 image at offset %zx\n",
|
||||
section_offset);
|
||||
pe_relocate(new_addr, section_data, fsp, *fih_offset);
|
||||
}
|
||||
|
||||
offset += data_size + data_offset;
|
||||
|
|
Loading…
Reference in New Issue