static size_t section_data_size(const EFI_COMMON_SECTION_HEADER *csh) { size_t section_size; if (csh_size(csh) == 0x00ffffff) section_size = read_le32(&SECTION2_SIZE(csh)); else section_size = csh_size(csh); return section_size - section_data_offset(csh); }
static ssize_t relocate_fvh(uintptr_t new_addr, void *fsp, size_t fsp_size, size_t fvh_offset, size_t *fih_offset) { EFI_FIRMWARE_VOLUME_HEADER *fvh; EFI_FFS_FILE_HEADER *ffsfh; EFI_COMMON_SECTION_HEADER *csh; size_t offset; size_t file_offset; size_t size; size_t fv_length; offset = fvh_offset; fvh = relative_offset(fsp, offset); if (read_le32(&fvh->Signature) != EFI_FVH_SIGNATURE) return -1; fv_length = read_le64(&fvh->FvLength); printk(FSP_DBG_LVL, "FVH length: %zx Offset: %zx Mapping length: %zx\n", fv_length, offset, fsp_size); if (fv_length + offset > fsp_size) return -1; /* Parse only this FV. However, the algorithm uses offsets into the * entire FSP region so make size include the starting offset. */ size = fv_length + offset; if (guid_compare(&fvh->FileSystemGuid, &ffs2_guid)) { printk(BIOS_ERR, "FVH not an FFS2 type.\n"); return -1; } if (read_le16(&fvh->ExtHeaderOffset) != 0) { EFI_FIRMWARE_VOLUME_EXT_HEADER *fveh; offset += read_le16(&fvh->ExtHeaderOffset); fveh = relative_offset(fsp, offset); printk(FSP_DBG_LVL, "Extended Header Offset: %zx Size: %zx\n", (size_t)read_le16(&fvh->ExtHeaderOffset), (size_t)read_le32(&fveh->ExtHeaderSize)); offset += read_le32(&fveh->ExtHeaderSize); /* FFS files are 8 byte aligned after extended header. */ offset = ALIGN_UP(offset, 8); } else { offset += read_le16(&fvh->HeaderLength); } file_offset = offset; while (file_offset + sizeof(*ffsfh) < size) { offset = file_offset; printk(FSP_DBG_LVL, "file offset: %zx\n", file_offset); /* First file and section should be FSP info header. */ if (fih_offset != NULL && *fih_offset == 0) *fih_offset = file_offset; ffsfh = relative_offset(fsp, file_offset); printk(FSP_DBG_LVL, "file type = %x\n", read_le8(&ffsfh->Type)); printk(FSP_DBG_LVL, "file attribs = %x\n", read_le8(&ffsfh->Attributes)); /* Exit FV relocation when empty space found */ if (read_le8(&ffsfh->Type) == EFI_FV_FILETYPE_FFS_MAX) break; /* Next file on 8 byte alignment. */ file_offset += ffs_file_size(ffsfh); file_offset = ALIGN_UP(file_offset, 8); /* Padding files have no section information. */ if (read_le8(&ffsfh->Type) == EFI_FV_FILETYPE_FFS_PAD) continue; offset += file_section_offset(ffsfh); while (offset + sizeof(*csh) < file_offset) { size_t data_size; size_t data_offset; csh = relative_offset(fsp, offset); printk(FSP_DBG_LVL, "section offset: %zx\n", offset); printk(FSP_DBG_LVL, "section type: %x\n", read_le8(&csh->Type)); data_size = section_data_size(csh); data_offset = section_data_offset(csh); if (data_size + data_offset + offset > file_offset) { printk(BIOS_ERR, "Section exceeds FV size.\n"); return -1; } /* * The entire FSP 1.1 image can be thought of as one * program with a single link address even though there * are multiple TEs linked separately. The reason is * that each TE is linked for XIP. So in order to * relocate the TE properly we need to form the * 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; printk(FSP_DBG_LVL, "TE image at offset %zx\n", te_offset); te = relative_offset(fsp, te_offset); te_relocate(te_addr, te); } offset += data_size + data_offset; /* Sections are aligned to 4 bytes. */ offset = ALIGN_UP(offset, 4); } } /* Return amount of buffer parsed: FV size. */ return fv_length; }
static ssize_t relocate_remaining_items(void *fsp, size_t size, uintptr_t new_addr, size_t fih_offset) { EFI_FFS_FILE_HEADER *ffsfh; EFI_COMMON_SECTION_HEADER *csh; FSP_INFO_HEADER *fih; ssize_t adjustment; size_t offset; 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 -1; } /* FSP_INFO_HEADER 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 -1; } if (read_le8(&csh->Type) != EFI_SECTION_RAW) { printk(BIOS_ERR, "FIH file should have raw section: %x\n", read_le8(&csh->Type)); return -1; } 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)); /* Need to find patch table and adjust each entry. The tables * following FSP_INFO_HEADER have a 32-bit signature and header * length. The patch table is denoted as having a 'FSPP' signature; * the table format doesn't follow the other tables. */ offset = fih_offset + read_le32(&fih->HeaderLength); while (offset + 2 * sizeof(uint32_t) <= size) { uint32_t *table_headers; table_headers = relative_offset(fsp, offset); printk(FSP_DBG_LVL, "Checking offset %zx for 'FSPP'\n", offset); if (read_le32(&table_headers[0]) != FSPP_SIG) { offset += read_le32(&table_headers[1]); continue; } if (relocate_patch_table(fsp, size, offset, adjustment)) { printk(BIOS_ERR, "FSPP relocation failed.\n"); return -1; } return fih_offset; } printk(BIOS_ERR, "Could not find the FSP patch table.\n"); return -1; }
static ssize_t relocate_fvh(void *fsp, size_t fsp_size, size_t fvh_offset, size_t *fih_offset) { EFI_FIRMWARE_VOLUME_HEADER *fvh; EFI_FFS_FILE_HEADER *ffsfh; EFI_COMMON_SECTION_HEADER *csh; size_t offset; size_t file_offset; size_t size; offset = fvh_offset; fvh = relative_offset(fsp, offset); if (fvh->Signature != EFI_FVH_SIGNATURE) return -1; printk(FSP_DBG_LVL, "FVH length: %zx Offset: %zx Mapping length: %zx\n", (size_t)fvh->FvLength, offset, fsp_size); if (fvh->FvLength + offset > fsp_size) return -1; /* Parse only this FV. However, the algorithm uses offsets into the * entire FSP region so make size include the starting offset. */ size = fvh->FvLength + offset; if (memcmp(&fvh->FileSystemGuid, &ffs2_guid, sizeof(ffs2_guid))) { printk(BIOS_ERR, "FVH not an FFS2 type.\n"); return -1; } if (fvh->ExtHeaderOffset != 0) { EFI_FIRMWARE_VOLUME_EXT_HEADER *fveh; offset += fvh->ExtHeaderOffset; fveh = relative_offset(fsp, offset); printk(FSP_DBG_LVL, "Extended Header Offset: %zx Size: %zx\n", (size_t)fvh->ExtHeaderOffset, (size_t)fveh->ExtHeaderSize); offset += fveh->ExtHeaderSize; /* FFS files are 8 byte aligned after extended header. */ offset = ALIGN_UP(offset, 8); } else { offset += fvh->HeaderLength; } file_offset = offset; while (file_offset + sizeof(*ffsfh) < size) { offset = file_offset; printk(FSP_DBG_LVL, "file offset: %zx\n", file_offset); /* First file and section should be FSP info header. */ if (fih_offset != NULL && *fih_offset == 0) *fih_offset = file_offset; ffsfh = relative_offset(fsp, file_offset); printk(FSP_DBG_LVL, "file type = %x\n", ffsfh->Type); printk(FSP_DBG_LVL, "file attribs = %x\n", ffsfh->Attributes); /* Exit FV relocation when empty space found */ if (ffsfh->Type == EFI_FV_FILETYPE_FFS_MAX) break; /* Next file on 8 byte alignment. */ file_offset += ffs_file_size(ffsfh); file_offset = ALIGN_UP(file_offset, 8); /* Padding files have no section information. */ if (ffsfh->Type == EFI_FV_FILETYPE_FFS_PAD) continue; offset += file_section_offset(ffsfh); while (offset + sizeof(*csh) < file_offset) { size_t data_size; size_t data_offset; csh = relative_offset(fsp, offset); printk(FSP_DBG_LVL, "section offset: %zx\n", offset); printk(FSP_DBG_LVL, "section type: %x\n", csh->Type); data_size = section_data_size(csh); data_offset = section_data_offset(csh); if (data_size + data_offset + offset > file_offset) { printk(BIOS_ERR, "Section exceeds FV size.\n"); return -1; } if (csh->Type == EFI_SECTION_TE) { void *te; size_t te_offset = offset + data_offset; printk(FSP_DBG_LVL, "TE image at offset %zx\n", te_offset); te = relative_offset(fsp, te_offset); te_relocate_in_place(te, data_size); } offset += data_size + data_offset; /* Sections are aligned to 4 bytes. */ offset = ALIGN_UP(offset, 4); } } /* Return amount of buffer parsed: FV size. */ return fvh->FvLength; }