Commit 6d3e32e6 authored by Matt Fleming's avatar Matt Fleming Committed by H. Peter Anvin

x86, efi: Make efi_call_phys_{prelog,epilog} CONFIG_RELOCATABLE-aware

efi_call_phys_prelog() sets up a 1:1 mapping of the physical address
range in swapper_pg_dir. Instead of replacing then restoring entries
in swapper_pg_dir we should be using initial_page_table which already
contains the 1:1 mapping.

It's safe to blindly switch back to swapper_pg_dir in the epilog
because the physical EFI routines are only called before
efi_enter_virtual_mode(), e.g. before any user processes have been
forked. Therefore, we don't need to track which pgd was in %cr3 when
we entered the prelog.

The previous code actually contained a bug because it assumed that the
kernel was loaded at a physical address within the first 8MB of ram,
usually at 0x100000. However, this isn't the case with a
CONFIG_RELOCATABLE=y kernel which could have been loaded anywhere in
the physical address space.

Also delete the ancient (and bogus) comments about the page table
being restored after the lock is released. There is no locking.

Cc: Matthew Garrett <mjg@redhat.com>
Cc: Darrent Hart <dvhart@linux.intel.com>
Signed-off-by: default avatarMatt Fleming <matt.fleming@intel.com>
Link: http://lkml.kernel.org/r/1323346250.3894.74.camel@mfleming-mobl1.ger.corp.intel.comSigned-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent dc47ce90
...@@ -39,43 +39,14 @@ ...@@ -39,43 +39,14 @@
*/ */
static unsigned long efi_rt_eflags; static unsigned long efi_rt_eflags;
static pgd_t efi_bak_pg_dir_pointer[2];
void efi_call_phys_prelog(void) void efi_call_phys_prelog(void)
{ {
unsigned long cr4;
unsigned long temp;
struct desc_ptr gdt_descr; struct desc_ptr gdt_descr;
local_irq_save(efi_rt_eflags); local_irq_save(efi_rt_eflags);
/* load_cr3(initial_page_table);
* If I don't have PAE, I should just duplicate two entries in page
* directory. If I have PAE, I just need to duplicate one entry in
* page directory.
*/
cr4 = read_cr4_safe();
if (cr4 & X86_CR4_PAE) {
efi_bak_pg_dir_pointer[0].pgd =
swapper_pg_dir[pgd_index(0)].pgd;
swapper_pg_dir[0].pgd =
swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
} else {
efi_bak_pg_dir_pointer[0].pgd =
swapper_pg_dir[pgd_index(0)].pgd;
efi_bak_pg_dir_pointer[1].pgd =
swapper_pg_dir[pgd_index(0x400000)].pgd;
swapper_pg_dir[pgd_index(0)].pgd =
swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
temp = PAGE_OFFSET + 0x400000;
swapper_pg_dir[pgd_index(0x400000)].pgd =
swapper_pg_dir[pgd_index(temp)].pgd;
}
/*
* After the lock is released, the original page table is restored.
*/
__flush_tlb_all(); __flush_tlb_all();
gdt_descr.address = __pa(get_cpu_gdt_table(0)); gdt_descr.address = __pa(get_cpu_gdt_table(0));
...@@ -85,28 +56,13 @@ void efi_call_phys_prelog(void) ...@@ -85,28 +56,13 @@ void efi_call_phys_prelog(void)
void efi_call_phys_epilog(void) void efi_call_phys_epilog(void)
{ {
unsigned long cr4;
struct desc_ptr gdt_descr; struct desc_ptr gdt_descr;
gdt_descr.address = (unsigned long)get_cpu_gdt_table(0); gdt_descr.address = (unsigned long)get_cpu_gdt_table(0);
gdt_descr.size = GDT_SIZE - 1; gdt_descr.size = GDT_SIZE - 1;
load_gdt(&gdt_descr); load_gdt(&gdt_descr);
cr4 = read_cr4_safe(); load_cr3(swapper_pg_dir);
if (cr4 & X86_CR4_PAE) {
swapper_pg_dir[pgd_index(0)].pgd =
efi_bak_pg_dir_pointer[0].pgd;
} else {
swapper_pg_dir[pgd_index(0)].pgd =
efi_bak_pg_dir_pointer[0].pgd;
swapper_pg_dir[pgd_index(0x400000)].pgd =
efi_bak_pg_dir_pointer[1].pgd;
}
/*
* After the lock is released, the original page table is restored.
*/
__flush_tlb_all(); __flush_tlb_all();
local_irq_restore(efi_rt_eflags); local_irq_restore(efi_rt_eflags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment