diff options
Diffstat (limited to 'arch/x86/boot/compressed/efi.c')
-rw-r--r-- | arch/x86/boot/compressed/efi.c | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/arch/x86/boot/compressed/efi.c b/arch/x86/boot/compressed/efi.c index 4d363dfc2b33..09fa3b5d70b8 100644 --- a/arch/x86/boot/compressed/efi.c +++ b/arch/x86/boot/compressed/efi.c @@ -78,6 +78,46 @@ unsigned long efi_get_system_table(struct boot_params *bp) return sys_tbl_pa; } +/* + * EFI config table address changes to virtual address after boot, which may + * not be accessible for the kexec'd kernel. To address this, kexec provides + * the initial physical address via a struct setup_data entry, which is + * checked for here, along with some sanity checks. + */ +static struct efi_setup_data *get_kexec_setup_data(struct boot_params *bp, + enum efi_type et) +{ +#ifdef CONFIG_X86_64 + struct efi_setup_data *esd = NULL; + struct setup_data *data; + u64 pa_data; + + pa_data = bp->hdr.setup_data; + while (pa_data) { + data = (struct setup_data *)pa_data; + if (data->type == SETUP_EFI) { + esd = (struct efi_setup_data *)(pa_data + sizeof(struct setup_data)); + break; + } + + pa_data = data->next; + } + + /* + * Original ACPI code falls back to attempting normal EFI boot in these + * cases, so maintain existing behavior by indicating non-kexec + * environment to the caller, but print them for debugging. + */ + if (esd && !esd->tables) { + debug_putstr("kexec EFI environment missing valid configuration table.\n"); + return NULL; + } + + return esd; +#endif + return NULL; +} + /** * efi_get_conf_table - Given a pointer to boot_params, locate and return the physical * address of EFI configuration table. @@ -106,8 +146,12 @@ int efi_get_conf_table(struct boot_params *bp, unsigned long *cfg_tbl_pa, et = efi_get_type(bp); if (et == EFI_TYPE_64) { efi_system_table_64_t *stbl = (efi_system_table_64_t *)sys_tbl_pa; + struct efi_setup_data *esd; - *cfg_tbl_pa = stbl->tables; + /* kexec provides an alternative EFI conf table, check for it. */ + esd = get_kexec_setup_data(bp, et); + + *cfg_tbl_pa = esd ? esd->tables : stbl->tables; *cfg_tbl_len = stbl->nr_tables; } else if (et == EFI_TYPE_32) { efi_system_table_32_t *stbl = (efi_system_table_32_t *)sys_tbl_pa; |