efi: Disable interrupts around EFI calls, not in the epilog/prolog calls

From: Ingo Molnar
Date: Tue Mar 03 2015 - 01:34:33 EST


commit 23a0d4e8fa6d3a1d7fb819f79bcc0a3739c30ba9 upstream.

Tapasweni Pathak reported that we do a kmalloc() in efi_call_phys_prolog()
on x86-64 while having interrupts disabled, which is a big no-no, as
kmalloc() can sleep.

Solve this by removing the irq disabling from the prolog/epilog calls
around EFI calls: it's unnecessary, as in this stage we are single
threaded in the boot thread, and we don't ever execute this from
interrupt contexts.

Reported-by: Tapasweni Pathak <tapaswenipathak@xxxxxxxxx>
Signed-off-by: Ingo Molnar <mingo@xxxxxxxxxx>
Signed-off-by: Matt Fleming <matt.fleming@xxxxxxxxx>
[ luis: backported to 3.10: adjusted context ]
Signed-off-by: Luis Henriques <luis.henriques@xxxxxxxxxxxxx>
---
arch/x86/platform/efi/efi.c | 7 +++++++
arch/x86/platform/efi/efi_32.c | 11 +++--------
arch/x86/platform/efi/efi_64.c | 3 ---
3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 6033be9ff81a..3c8bffdc71c8 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -250,12 +250,19 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
efi_memory_desc_t *virtual_map)
{
efi_status_t status;
+ unsigned long flags;

efi_call_phys_prelog();
+
+ /* Disable interrupts around EFI calls: */
+ local_irq_save(flags);
status = efi_call_phys4(efi_phys.set_virtual_address_map,
memory_map_size, descriptor_size,
descriptor_version, virtual_map);
+ local_irq_restore(flags);
+
efi_call_phys_epilog();
+
return status;
}

diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c
index 40e446941dd7..bebbee05e331 100644
--- a/arch/x86/platform/efi/efi_32.c
+++ b/arch/x86/platform/efi/efi_32.c
@@ -33,19 +33,16 @@

/*
* To make EFI call EFI runtime service in physical addressing mode we need
- * prelog/epilog before/after the invocation to disable interrupt, to
- * claim EFI runtime service handler exclusively and to duplicate a memory in
- * low memory space say 0 - 3G.
+ * prolog/epilog before/after the invocation to claim the EFI runtime service
+ * handler exclusively and to duplicate a memory mapping in low memory space,
+ * say 0 - 3G.
*/

-static unsigned long efi_rt_eflags;

void efi_call_phys_prelog(void)
{
struct desc_ptr gdt_descr;

- local_irq_save(efi_rt_eflags);
-
load_cr3(initial_page_table);
__flush_tlb_all();

@@ -64,6 +61,4 @@ void efi_call_phys_epilog(void)

load_cr3(swapper_pg_dir);
__flush_tlb_all();
-
- local_irq_restore(efi_rt_eflags);
}
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 39a0e7f1f0a3..2f6c1a9734c8 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -40,7 +40,6 @@
#include <asm/fixmap.h>

static pgd_t *save_pgd __initdata;
-static unsigned long efi_flags __initdata;

static void __init early_code_mapping_set_exec(int executable)
{
@@ -66,7 +65,6 @@ void __init efi_call_phys_prelog(void)
int n_pgds;

early_code_mapping_set_exec(1);
- local_irq_save(efi_flags);

n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
save_pgd = kmalloc(n_pgds * sizeof(pgd_t), GFP_KERNEL);
@@ -90,7 +88,6 @@ void __init efi_call_phys_epilog(void)
set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), save_pgd[pgd]);
kfree(save_pgd);
__flush_tlb_all();
- local_irq_restore(efi_flags);
early_code_mapping_set_exec(0);
}


--UugvWAfsgieZRqgk
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="3.16-efi-Disable-interrupts-around-EFI-calls-not-in-the-e.patch"