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>
|
#include <string.h>
|
||||||
|
|
||||||
#define FSP_DBG_LVL BIOS_NEVER
|
#define FSP_DBG_LVL BIOS_NEVER
|
||||||
|
#define MASK_24BITS 0x00FFFFFF
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UEFI defines everything as little endian. However, this piece of code
|
* 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;
|
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)
|
static uint32_t *fspp_reloc(void *fsp, size_t fsp_size, uint32_t e)
|
||||||
{
|
{
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
|
||||||
/* Offsets live in bits 23:0. */
|
/* 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
|
/* 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
|
* 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);
|
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)
|
static int te_relocate(uintptr_t new_addr, void *te)
|
||||||
{
|
{
|
||||||
EFI_TE_IMAGE_HEADER *teih;
|
EFI_TE_IMAGE_HEADER *teih;
|
||||||
|
@ -201,32 +378,11 @@ static int te_relocate(uintptr_t new_addr, void *te)
|
||||||
return 0;
|
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)
|
static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh)
|
||||||
{
|
{
|
||||||
size_t section_size;
|
size_t section_size;
|
||||||
|
|
||||||
if (csh_size(csh) == 0x00ffffff)
|
if (csh_size(csh) == MASK_24BITS)
|
||||||
section_size = read_le32(&SECTION2_SIZE(csh));
|
section_size = read_le32(&SECTION2_SIZE(csh));
|
||||||
else
|
else
|
||||||
section_size = csh_size(csh);
|
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);
|
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)
|
static size_t ffs_file_size(const EFI_FFS_FILE_HEADER *ffsfh)
|
||||||
{
|
{
|
||||||
size_t size;
|
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) {
|
if (read_le32(&fih->Signature) != FSP_SIG) {
|
||||||
printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
|
printk(BIOS_ERR, "Unexpected FIH signature: %08x\n",
|
||||||
read_le32(&fih->Signature));
|
read_le32(&fih->Signature));
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustment = (intptr_t)new_addr - read_le32(&fih->ImageBase);
|
adjustment = (intptr_t)new_addr - read_le32(&fih->ImageBase);
|
||||||
|
|
||||||
/* Update ImageBase to reflect FSP's new home. */
|
/* Update ImageBase to reflect FSP's new home. */
|
||||||
write_le32(&fih->ImageBase, adjustment + read_le32(&fih->ImageBase));
|
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
|
/* Need to find patch table and adjust each entry. The tables
|
||||||
* following FSP_INFO_HEADER have a 32-bit signature and header
|
* 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) {
|
while (offset + sizeof(*csh) < file_offset) {
|
||||||
size_t data_size;
|
size_t data_size;
|
||||||
size_t data_offset;
|
size_t data_offset;
|
||||||
|
void *section_data;
|
||||||
|
size_t section_offset;
|
||||||
|
uintptr_t section_addr;
|
||||||
|
|
||||||
csh = relative_offset(fsp, offset);
|
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
|
* relocated address based on the TE offset within
|
||||||
* FSP proper.
|
* FSP proper.
|
||||||
*/
|
*/
|
||||||
if (read_le8(&csh->Type) == EFI_SECTION_TE) {
|
section_offset = offset + data_offset;
|
||||||
void *te;
|
section_addr = new_addr + section_offset;
|
||||||
size_t te_offset = offset + data_offset;
|
section_data = relative_offset(fsp, section_offset);
|
||||||
uintptr_t te_addr = new_addr + te_offset;
|
|
||||||
|
|
||||||
|
if (read_le8(&csh->Type) == EFI_SECTION_TE) {
|
||||||
printk(FSP_DBG_LVL, "TE image at offset %zx\n",
|
printk(FSP_DBG_LVL, "TE image at offset %zx\n",
|
||||||
te_offset);
|
section_offset);
|
||||||
te = relative_offset(fsp, te_offset);
|
te_relocate(section_addr, section_data);
|
||||||
te_relocate(te_addr, te);
|
} 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;
|
offset += data_size + data_offset;
|
||||||
|
|
Loading…
Reference in New Issue