| Default EFI to physical rather than virtual. Upstream seem to be going |
| in this direction. |
| |
| diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h |
| index 8e4a165..3c62f15 100644 |
| |
| |
| @@ -93,6 +93,9 @@ extern int add_efi_memmap; |
| extern void efi_memblock_x86_reserve_range(void); |
| extern void efi_call_phys_prelog(void); |
| extern void efi_call_phys_epilog(void); |
| +extern void efi_call_phys_prelog_in_physmode(void); |
| +extern void efi_call_phys_epilog_in_physmode(void); |
| +extern void efi_pagetable_init(void); |
| |
| #ifndef CONFIG_EFI |
| /* |
| diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c |
| index 0fe27d7..e1158b0 100644 |
| |
| |
| @@ -58,6 +58,7 @@ struct efi_memory_map memmap; |
| |
| static struct efi efi_phys __initdata; |
| static efi_system_table_t efi_systab __initdata; |
| +static efi_runtime_services_t phys_runtime; |
| |
| static int __init setup_noefi(char *arg) |
| { |
| @@ -172,7 +173,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map( |
| return status; |
| } |
| |
| -static efi_status_t __init phys_efi_get_time(efi_time_t *tm, |
| +static efi_status_t __init phys_efi_get_time_early(efi_time_t *tm, |
| efi_time_cap_t *tc) |
| { |
| efi_status_t status; |
| @@ -183,6 +184,112 @@ static efi_status_t __init phys_efi_get_time(efi_time_t *tm, |
| return status; |
| } |
| |
| +static efi_status_t phys_efi_get_time(efi_time_t *tm, |
| + efi_time_cap_t *tc) |
| +{ |
| + efi_status_t status; |
| + |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys2((void*)phys_runtime.get_time, tm, tc); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t __init phys_efi_set_time(efi_time_t *tm) |
| +{ |
| + efi_status_t status; |
| + |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys1((void*)phys_runtime.set_time, tm); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_get_wakeup_time(efi_bool_t *enabled, |
| + efi_bool_t *pending, |
| + efi_time_t *tm) |
| +{ |
| + efi_status_t status; |
| + |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys3((void*)phys_runtime.get_wakeup_time, enabled, |
| + pending, tm); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) |
| +{ |
| + efi_status_t status; |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys2((void*)phys_runtime.set_wakeup_time, enabled, |
| + tm); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_get_variable(efi_char16_t *name, |
| + efi_guid_t *vendor, |
| + u32 *attr, |
| + unsigned long *data_size, |
| + void *data) |
| +{ |
| + efi_status_t status; |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys5((void*)phys_runtime.get_variable, name, vendor, |
| + attr, data_size, data); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_get_next_variable(unsigned long *name_size, |
| + efi_char16_t *name, |
| + efi_guid_t *vendor) |
| +{ |
| + efi_status_t status; |
| + |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys3((void*)phys_runtime.get_next_variable, |
| + name_size, name, vendor); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_set_variable(efi_char16_t *name, |
| + efi_guid_t *vendor, |
| + unsigned long attr, |
| + unsigned long data_size, |
| + void *data) |
| +{ |
| + efi_status_t status; |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys5((void*)phys_runtime.set_variable, name, |
| + vendor, attr, data_size, data); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static efi_status_t phys_efi_get_next_high_mono_count(u32 *count) |
| +{ |
| + efi_status_t status; |
| + efi_call_phys_prelog_in_physmode(); |
| + status = efi_call_phys1((void*)phys_runtime.get_next_high_mono_count, |
| + count); |
| + efi_call_phys_epilog_in_physmode(); |
| + return status; |
| +} |
| + |
| +static void phys_efi_reset_system(int reset_type, |
| + efi_status_t status, |
| + unsigned long data_size, |
| + efi_char16_t *data) |
| +{ |
| + efi_call_phys_prelog_in_physmode(); |
| + efi_call_phys4((void*)phys_runtime.reset_system, reset_type, status, |
| + data_size, data); |
| + efi_call_phys_epilog_in_physmode(); |
| +} |
| + |
| int efi_set_rtc_mmss(unsigned long nowtime) |
| { |
| int real_seconds, real_minutes; |
| @@ -435,7 +542,9 @@ void __init efi_init(void) |
| * Make efi_get_time can be called before entering |
| * virtual mode. |
| */ |
| - efi.get_time = phys_efi_get_time; |
| + efi.get_time = phys_efi_get_time_early; |
| + |
| + memcpy(&phys_runtime, runtime, sizeof(efi_runtime_services_t)); |
| } else |
| printk(KERN_ERR "Could not map the EFI runtime service " |
| "table!\n"); |
| @@ -466,6 +575,14 @@ void __init efi_init(void) |
| #if EFI_DEBUG |
| print_efi_memmap(); |
| #endif |
| + |
| +#ifndef CONFIG_X86_64 |
| + /* |
| + * Only x86_64 supports physical mode as of now. Use virtual mode |
| + * forcibly. |
| + */ |
| + usevirtefi = 1; |
| +#endif |
| } |
| |
| static void __init runtime_code_page_mkexec(void) |
| @@ -579,6 +696,27 @@ void __init efi_enter_virtual_mode(void) |
| memmap.map = NULL; |
| } |
| |
| +void __init efi_setup_physical_mode(void) |
| +{ |
| +#ifdef CONFIG_X86_64 |
| + efi_pagetable_init(); |
| +#endif |
| + efi.get_time = phys_efi_get_time; |
| + efi.set_time = phys_efi_set_time; |
| + efi.get_wakeup_time = phys_efi_get_wakeup_time; |
| + efi.set_wakeup_time = phys_efi_set_wakeup_time; |
| + efi.get_variable = phys_efi_get_variable; |
| + efi.get_next_variable = phys_efi_get_next_variable; |
| + efi.set_variable = phys_efi_set_variable; |
| + efi.get_next_high_mono_count = |
| + phys_efi_get_next_high_mono_count; |
| + efi.reset_system = phys_efi_reset_system; |
| + efi.set_virtual_address_map = NULL; /* Not needed */ |
| + |
| + early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); |
| + memmap.map = NULL; |
| +} |
| + |
| /* |
| * Convenience functions to obtain memory types and attributes |
| */ |
| diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c |
| index 5cab48e..90767b1 100644 |
| |
| |
| @@ -110,3 +110,7 @@ void efi_call_phys_epilog(void) |
| |
| local_irq_restore(efi_rt_eflags); |
| } |
| + |
| +void efi_call_phys_prelog_in_physmode(void) { /* Not supported */ } |
| +void efi_call_phys_epilog_in_physmode(void) { /* Not supported */ } |
| + |
| diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c |
| index ac0621a..ad19fe9 100644 |
| |
| |
| @@ -39,7 +39,9 @@ |
| #include <asm/fixmap.h> |
| |
| static pgd_t save_pgd __initdata; |
| -static unsigned long efi_flags __initdata; |
| +static DEFINE_PER_CPU(unsigned long, efi_flags); |
| +static DEFINE_PER_CPU(unsigned long, save_cr3); |
| +static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss; |
| |
| static void __init early_mapping_set_exec(unsigned long start, |
| unsigned long end, |
| @@ -80,7 +82,7 @@ void __init efi_call_phys_prelog(void) |
| unsigned long vaddress; |
| |
| early_runtime_code_mapping_set_exec(1); |
| - local_irq_save(efi_flags); |
| + local_irq_save(get_cpu_var(efi_flags)); |
| vaddress = (unsigned long)__va(0x0UL); |
| save_pgd = *pgd_offset_k(0x0UL); |
| set_pgd(pgd_offset_k(0x0UL), *pgd_offset_k(vaddress)); |
| @@ -94,10 +96,23 @@ void __init efi_call_phys_epilog(void) |
| */ |
| set_pgd(pgd_offset_k(0x0UL), save_pgd); |
| __flush_tlb_all(); |
| - local_irq_restore(efi_flags); |
| + local_irq_restore(get_cpu_var(efi_flags)); |
| early_runtime_code_mapping_set_exec(0); |
| } |
| |
| +void efi_call_phys_prelog_in_physmode(void) |
| +{ |
| + local_irq_save(get_cpu_var(efi_flags)); |
| + get_cpu_var(save_cr3)= read_cr3(); |
| + write_cr3(virt_to_phys(efi_pgd)); |
| +} |
| + |
| +void efi_call_phys_epilog_in_physmode(void) |
| +{ |
| + write_cr3(get_cpu_var(save_cr3)); |
| + local_irq_restore(get_cpu_var(efi_flags)); |
| +} |
| + |
| void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size, |
| u32 type) |
| { |
| @@ -112,3 +127,78 @@ void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size, |
| |
| return (void __iomem *)__va(phys_addr); |
| } |
| + |
| +static pud_t *fill_pud(pgd_t *pgd, unsigned long vaddr) |
| +{ |
| + if (pgd_none(*pgd)) { |
| + pud_t *pud = (pud_t *)get_zeroed_page(GFP_ATOMIC); |
| + set_pgd(pgd, __pgd(_PAGE_TABLE | __pa(pud))); |
| + if (pud != pud_offset(pgd, 0)) |
| + printk(KERN_ERR "EFI PAGETABLE BUG #00! %p <-> %p\n", |
| + pud, pud_offset(pgd, 0)); |
| + } |
| + return pud_offset(pgd, vaddr); |
| +} |
| + |
| +static pmd_t *fill_pmd(pud_t *pud, unsigned long vaddr) |
| +{ |
| + if (pud_none(*pud)) { |
| + pmd_t *pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); |
| + set_pud(pud, __pud(_PAGE_TABLE | __pa(pmd))); |
| + if (pmd != pmd_offset(pud, 0)) |
| + printk(KERN_ERR "EFI PAGETABLE BUG #01! %p <-> %p\n", |
| + pmd, pmd_offset(pud, 0)); |
| + } |
| + return pmd_offset(pud, vaddr); |
| +} |
| + |
| +static pte_t *fill_pte(pmd_t *pmd, unsigned long vaddr) |
| +{ |
| + if (pmd_none(*pmd)) { |
| + pte_t *pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); |
| + set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte))); |
| + if (pte != pte_offset_kernel(pmd, 0)) |
| + printk(KERN_ERR "EFI PAGETABLE BUG #02!\n"); |
| + } |
| + return pte_offset_kernel(pmd, vaddr); |
| +} |
| + |
| +void __init efi_pagetable_init(void) |
| +{ |
| + efi_memory_desc_t *md; |
| + unsigned long size; |
| + u64 start_pfn, end_pfn, pfn, vaddr; |
| + void *p; |
| + pgd_t *pgd; |
| + pud_t *pud; |
| + pmd_t *pmd; |
| + pte_t *pte; |
| + |
| + memset(efi_pgd, 0, sizeof(efi_pgd)); |
| + for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { |
| + md = p; |
| + if (!(md->type & EFI_RUNTIME_SERVICES_CODE) && |
| + !(md->type & EFI_RUNTIME_SERVICES_DATA)) |
| + continue; |
| + |
| + start_pfn = md->phys_addr >> PAGE_SHIFT; |
| + size = md->num_pages << EFI_PAGE_SHIFT; |
| + end_pfn = PFN_UP(md->phys_addr + size); |
| + |
| + for (pfn = start_pfn; pfn <= end_pfn; pfn++) { |
| + vaddr = pfn << PAGE_SHIFT; |
| + pgd = efi_pgd + pgd_index(vaddr); |
| + pud = fill_pud(pgd, vaddr); |
| + pmd = fill_pmd(pud, vaddr); |
| + pte = fill_pte(pmd, vaddr); |
| + if (md->type & EFI_RUNTIME_SERVICES_CODE) |
| + set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); |
| + else |
| + set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)); |
| + } |
| + } |
| + pgd = efi_pgd + pgd_index(PAGE_OFFSET); |
| + set_pgd(pgd, *pgd_offset_k(PAGE_OFFSET)); |
| + pgd = efi_pgd + pgd_index(__START_KERNEL_map); |
| + set_pgd(pgd, *pgd_offset_k(__START_KERNEL_map)); |
| +} |
| diff --git a/include/linux/efi.h b/include/linux/efi.h |
| index fb737bc..c4e310e 100644 |
| |
| |
| @@ -290,6 +290,7 @@ extern void efi_map_pal_code (void); |
| extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg); |
| extern void efi_gettimeofday (struct timespec *ts); |
| extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */ |
| +extern void efi_setup_physical_mode(void); |
| extern u64 efi_get_iobase (void); |
| extern u32 efi_mem_type (unsigned long phys_addr); |
| extern u64 efi_mem_attributes (unsigned long phys_addr); |
| diff --git a/include/linux/init.h b/include/linux/init.h |
| index 577671c..2f1b28f 100644 |
| |
| |
| @@ -149,6 +149,7 @@ extern int do_one_initcall(initcall_t fn); |
| extern char __initdata boot_command_line[]; |
| extern char *saved_command_line; |
| extern unsigned int reset_devices; |
| +extern unsigned int usevirtefi; |
| |
| /* used by init/main.c */ |
| void setup_arch(char **); |
| diff --git a/init/main.c b/init/main.c |
| index 8646401..726025e 100644 |
| |
| |
| @@ -196,6 +196,14 @@ static int __init set_reset_devices(char *str) |
| |
| __setup("reset_devices", set_reset_devices); |
| |
| +unsigned int usevirtefi; |
| +static int __init set_virt_efi(char *str) |
| +{ |
| + usevirtefi = 1; |
| + return 1; |
| +} |
| +__setup("virtefi", set_virt_efi); |
| + |
| static const char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; |
| const char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; |
| static const char *panic_later, *panic_param; |
| @@ -668,8 +676,12 @@ asmlinkage void __init start_kernel(void) |
| pidmap_init(); |
| anon_vma_init(); |
| #ifdef CONFIG_X86 |
| - if (efi_enabled) |
| - efi_enter_virtual_mode(); |
| + if (efi_enabled) { |
| + if (usevirtefi) |
| + efi_enter_virtual_mode(); |
| + else |
| + efi_setup_physical_mode(); |
| + } |
| #endif |
| thread_info_cache_init(); |
| cred_init(); |