2010-12-17 16:51:06 +00:00
|
|
|
Default EFI to physical rather than virtual. Upstream seem to be going
|
|
|
|
in this direction.
|
2010-12-16 20:37:54 +00:00
|
|
|
|
|
|
|
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
|
|
|
|
index 8e4a165..3c62f15 100644
|
|
|
|
--- a/arch/x86/include/asm/efi.h
|
|
|
|
+++ b/arch/x86/include/asm/efi.h
|
|
|
|
@@ -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
|
|
|
|
--- a/arch/x86/platform/efi/efi.c
|
|
|
|
+++ b/arch/x86/platform/efi/efi.c
|
|
|
|
@@ -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
|
|
|
|
--- a/arch/x86/platform/efi/efi_32.c
|
|
|
|
+++ b/arch/x86/platform/efi/efi_32.c
|
|
|
|
@@ -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
|
2010-12-17 16:51:06 +00:00
|
|
|
index ac0621a..ad19fe9 100644
|
2010-12-16 20:37:54 +00:00
|
|
|
--- 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_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;
|
2010-12-17 16:51:06 +00:00
|
|
|
+ if (!(md->type & EFI_RUNTIME_SERVICES_CODE) &&
|
|
|
|
+ !(md->type & EFI_RUNTIME_SERVICES_DATA))
|
2010-12-16 20:37:54 +00:00
|
|
|
+ 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);
|
2010-12-17 16:51:06 +00:00
|
|
|
+ if (md->type & EFI_RUNTIME_SERVICES_CODE)
|
2010-12-16 20:37:54 +00:00
|
|
|
+ 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
|
|
|
|
--- a/include/linux/efi.h
|
|
|
|
+++ b/include/linux/efi.h
|
|
|
|
@@ -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
|
|
|
|
--- 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 8646401..726025e 100644
|
|
|
|
--- a/init/main.c
|
|
|
|
+++ b/init/main.c
|
|
|
|
@@ -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();
|