From fabb37c736f9f688fe3eec98550a5c032a07cfaa Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sat, 18 Jan 2014 12:48:18 +0100 Subject: x86/efi: Split efi_enter_virtual_mode ... into a kexec flavor for better code readability and simplicity. The original one was getting ugly with ifdeffery. Signed-off-by: Borislav Petkov Tested-by: Toshi Kani Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 148 +++++++++++++++++++++++++++++--------------- 1 file changed, 98 insertions(+), 50 deletions(-) (limited to 'arch/x86/platform/efi') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 576bb126593a..6f0a46730826 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -879,8 +879,9 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md) } } -static int __init save_runtime_map(void) +static void __init save_runtime_map(void) { +#ifdef CONFIG_KEXEC efi_memory_desc_t *md; void *tmp, *p, *q = NULL; int count = 0; @@ -902,28 +903,12 @@ static int __init save_runtime_map(void) } efi_runtime_map_setup(q, count, memmap.desc_size); + return; - return 0; out: kfree(q); - return -ENOMEM; -} - -/* - * Map efi regions which were passed via setup_data. The virt_addr is a fixed - * addr which was used in first kernel of a kexec boot. - */ -static void __init efi_map_regions_fixed(void) -{ - void *p; - efi_memory_desc_t *md; - - for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { - md = p; - efi_map_region_fixed(md); /* FIXME: add error handling */ - get_systab_virt_addr(md); - } - + pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); +#endif } static void *realloc_pages(void *old_memmap, int old_shift) @@ -989,6 +974,72 @@ static void * __init efi_map_regions(int *count, int *pg_shift) return new_memmap; } +static void __init kexec_enter_virtual_mode(void) +{ +#ifdef CONFIG_KEXEC + efi_memory_desc_t *md; + void *p; + + efi.systab = NULL; + + /* + * We don't do virtual mode, since we don't do runtime services, on + * non-native EFI + */ + if (!efi_is_native()) { + efi_unmap_memmap(); + return; + } + + /* + * Map efi regions which were passed via setup_data. The virt_addr is a + * fixed addr which was used in first kernel of a kexec boot. + */ + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { + md = p; + efi_map_region_fixed(md); /* FIXME: add error handling */ + get_systab_virt_addr(md); + } + + save_runtime_map(); + + BUG_ON(!efi.systab); + + efi_sync_low_kernel_mappings(); + + /* + * Now that EFI is in virtual mode, update the function + * pointers in the runtime service table to the new virtual addresses. + * + * Call EFI services through wrapper functions. + */ + efi.runtime_version = efi_systab.hdr.revision; + efi.get_time = virt_efi_get_time; + efi.set_time = virt_efi_set_time; + efi.get_wakeup_time = virt_efi_get_wakeup_time; + efi.set_wakeup_time = virt_efi_set_wakeup_time; + efi.get_variable = virt_efi_get_variable; + efi.get_next_variable = virt_efi_get_next_variable; + efi.set_variable = virt_efi_set_variable; + efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; + efi.reset_system = virt_efi_reset_system; + efi.set_virtual_address_map = NULL; + efi.query_variable_info = virt_efi_query_variable_info; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; + + if (efi_enabled(EFI_OLD_MEMMAP) && (__supported_pte_mask & _PAGE_NX)) + runtime_code_page_mkexec(); + + /* clean DUMMY object */ + efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 0, NULL); +#endif +} + /* * This function will switch the EFI runtime services to virtual mode. * Essentially, we look through the EFI memmap and map every region that @@ -1008,11 +1059,12 @@ static void * __init efi_map_regions(int *count, int *pg_shift) * * Specially for kexec boot, efi runtime maps in previous kernel should * be passed in via setup_data. In that case runtime ranges will be mapped - * to the same virtual addresses as the first kernel. + * to the same virtual addresses as the first kernel, see + * kexec_enter_virtual_mode(). */ -void __init efi_enter_virtual_mode(void) +static void __init __efi_enter_virtual_mode(void) { - int err, count = 0, pg_shift = 0; + int count = 0, pg_shift = 0; void *new_memmap = NULL; efi_status_t status; @@ -1027,43 +1079,33 @@ void __init efi_enter_virtual_mode(void) return; } - if (efi_setup) { - efi_map_regions_fixed(); - } else { - efi_merge_regions(); - new_memmap = efi_map_regions(&count, &pg_shift); - if (!new_memmap) { - pr_err("Error reallocating memory, EFI runtime non-functional!\n"); - return; - } - - err = save_runtime_map(); - if (err) - pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); + efi_merge_regions(); + new_memmap = efi_map_regions(&count, &pg_shift); + if (!new_memmap) { + pr_err("Error reallocating memory, EFI runtime non-functional!\n"); + return; } + save_runtime_map(); + BUG_ON(!efi.systab); - if (!efi_setup) { - if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) - return; - } + if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) + return; efi_sync_low_kernel_mappings(); efi_dump_pagetable(); - if (!efi_setup) { - status = phys_efi_set_virtual_address_map( + status = phys_efi_set_virtual_address_map( memmap.desc_size * count, memmap.desc_size, memmap.desc_version, (efi_memory_desc_t *)__pa(new_memmap)); - if (status != EFI_SUCCESS) { - pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", - status); - panic("EFI call to SetVirtualAddressMap() failed!"); - } + if (status != EFI_SUCCESS) { + pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", + status); + panic("EFI call to SetVirtualAddressMap() failed!"); } /* @@ -1089,7 +1131,6 @@ void __init efi_enter_virtual_mode(void) efi_runtime_mkexec(); - /* * We mapped the descriptor array into the EFI pagetable above but we're * not unmapping it here. Here's why: @@ -1116,8 +1157,7 @@ void __init efi_enter_virtual_mode(void) * * efi_cleanup_page_tables(__pa(new_memmap), 1 << pg_shift); */ - if (!efi_setup) - free_pages((unsigned long)new_memmap, pg_shift); + free_pages((unsigned long)new_memmap, pg_shift); /* clean DUMMY object */ efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID, @@ -1127,6 +1167,14 @@ void __init efi_enter_virtual_mode(void) 0, NULL); } +void __init efi_enter_virtual_mode(void) +{ + if (efi_setup) + kexec_enter_virtual_mode(); + else + __efi_enter_virtual_mode(); +} + /* * Convenience functions to obtain memory types and attributes */ -- cgit v1.2.3