Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From 2621de478a92bbfec35d53a336f9411b4944423b Mon Sep 17 00:00:00 2001
- From: Dan Yang <dsyang92 (at) gmail (dot) com>
- Date: Mon, 9 Apr 2012 22:20:47 -0400
- Subject: [PATCH] efi: run efi in physical mode instead of virtual mode
- ---
- arch/x86/include/asm/efi.h | 3 +
- arch/x86/platform/efi/efi.c | 142 +++++++++++++++++++++++++++++++++++++++-
- arch/x86/platform/efi/efi_32.c | 4 ++
- arch/x86/platform/efi/efi_64.c | 96 ++++++++++++++++++++++++++-
- include/linux/efi.h | 1 +
- include/linux/init.h | 1 +
- init/main.c | 16 ++++-
- 7 files changed, 256 insertions(+), 7 deletions(-)
- diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
- index 7093e4a..ed6f6fb 100644
- --- a/arch/x86/include/asm/efi.h
- +++ b/arch/x86/include/asm/efi.h
- @@ -94,6 +94,9 @@ extern void efi_set_executable(efi_memory_desc_t *md, bool executable);
- 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 37718f0..ced4504 100644
- --- a/arch/x86/platform/efi/efi.c
- +++ b/arch/x86/platform/efi/efi.c
- @@ -69,6 +69,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)
- {
- @@ -230,7 +231,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)
- {
- unsigned long flags;
- @@ -244,6 +245,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;
- @@ -551,7 +658,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");
- @@ -579,6 +688,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
- }
- void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
- @@ -751,6 +868,27 @@ void __init efi_enter_virtual_mode(void)
- kfree(new_memmap);
- }
- +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 40e4469..d494d70 100644
- --- a/arch/x86/platform/efi/efi_32.c
- +++ b/arch/x86/platform/efi/efi_32.c
- @@ -67,3 +67,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 ac3aa54..999af26 100644
- --- a/arch/x86/platform/efi/efi_64.c
- +++ b/arch/x86/platform/efi/efi_64.c
- @@ -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_code_mapping_set_exec(int executable)
- {
- @@ -63,7 +65,7 @@ void __init efi_call_phys_prelog(void)
- unsigned long vaddress;
- early_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));
- @@ -77,10 +79,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_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)
- {
- @@ -97,3 +112,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 2362a0b..86300d4 100644
- --- a/include/linux/efi.h
- +++ b/include/linux/efi.h
- @@ -326,6 +326,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 9146f39..0801c0f 100644
- --- a/include/linux/init.h
- +++ b/include/linux/init.h
- @@ -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 217ed23..d045381 100644
- --- a/init/main.c
- +++ b/init/main.c
- @@ -149,6 +149,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;
- @@ -609,8 +617,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();
- --
- 1.7.10
Advertisement
Add Comment
Please, Sign In to add comment