LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH] arch: x86: platform: efi: Disabling interrupt around kmalloc
@ 2015-03-03  2:58 Tapasweni Pathak
  2015-03-03  6:34 ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Ingo Molnar
  0 siblings, 1 reply; 5+ messages in thread
From: Tapasweni Pathak @ 2015-03-03  2:58 UTC (permalink / raw)
  To: matt.fleming, tglx, mingo, hpa, x86, linux-efi, linux-kernel
  Cc: tapaswenipathak

Disabling interrupts around kmalloc() is less than ideal. Move it
after the kmalloc().

Found using Coccinelle.

Signed-off-by: Tapasweni Pathak <tapaswenipathak@gmail.com>
Suggested-by: Matt Fleming <matt.fleming@intel.com>
---
 arch/x86/platform/efi/efi_64.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 17e80d8..5d6af59 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -88,10 +88,10 @@ void __init efi_call_phys_prolog(void)
 		return;

 	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);
+	local_irq_save(efi_flags);

 	for (pgd = 0; pgd < n_pgds; pgd++) {
 		save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
--
1.7.9.5


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls
  2015-03-03  2:58 [PATCH] arch: x86: platform: efi: Disabling interrupt around kmalloc Tapasweni Pathak
@ 2015-03-03  6:34 ` Ingo Molnar
  2015-03-03  6:48   ` [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction Ingo Molnar
  2015-03-10 13:36   ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Matt Fleming
  0 siblings, 2 replies; 5+ messages in thread
From: Ingo Molnar @ 2015-03-03  6:34 UTC (permalink / raw)
  To: Tapasweni Pathak
  Cc: matt.fleming, tglx, mingo, hpa, x86, linux-efi, linux-kernel


* Tapasweni Pathak <tapaswenipathak@gmail.com> wrote:

> Disabling interrupts around kmalloc() is less than ideal. Move it
> after the kmalloc().
> 
> Found using Coccinelle.
> 
> Signed-off-by: Tapasweni Pathak <tapaswenipathak@gmail.com>
> Suggested-by: Matt Fleming <matt.fleming@intel.com>
> ---
>  arch/x86/platform/efi/efi_64.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
> index 17e80d8..5d6af59 100644
> --- a/arch/x86/platform/efi/efi_64.c
> +++ b/arch/x86/platform/efi/efi_64.c
> @@ -88,10 +88,10 @@ void __init efi_call_phys_prolog(void)
>  		return;
> 
>  	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);
> +	local_irq_save(efi_flags);
> 
>  	for (pgd = 0; pgd < n_pgds; pgd++) {
>  		save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);

So why are interrupts disabled around page table operations to begin 
with? It's not like any of this can execute on two CPUs at once, nor 
can this be executed from interrupt context.

So, shouldn't we only protect the EFI calls themselves? If we did that 
we could save the efi_flags global variable ugliness as well.

I.e. something like the attached patch. (not tested and all that)

Thanks,

	Ingo

===================>
>From 160eef628f9e803ba62707ec7f610ea9f0f13984 Mon Sep 17 00:00:00 2001
From: Ingo Molnar <mingo@kernel.org>
Date: Tue, 3 Mar 2015 07:31:56 +0100
Subject: [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls

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@gmail.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 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 dbc8627a5cdf..2490a15d18f1 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -85,12 +85,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_prolog();
+
+	/* Disable interrupts around EFI calls: */
+	local_irq_save(flags);
 	status = efi_call_phys(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 40e7cda52936..abecc6e1dc90 100644
--- a/arch/x86/platform/efi/efi_32.c
+++ b/arch/x86/platform/efi/efi_32.c
@@ -33,11 +33,10 @@
 
 /*
  * To make EFI call EFI runtime service in physical addressing mode we need
- * prolog/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_sync_low_kernel_mappings(void) {}
 void __init efi_dump_pagetable(void) {}
@@ -61,8 +60,6 @@ void __init efi_call_phys_prolog(void)
 {
 	struct desc_ptr gdt_descr;
 
-	local_irq_save(efi_rt_eflags);
-
 	load_cr3(initial_page_table);
 	__flush_tlb_all();
 
@@ -81,8 +78,6 @@ void __init efi_call_phys_epilog(void)
 
 	load_cr3(swapper_pg_dir);
 	__flush_tlb_all();
-
-	local_irq_restore(efi_rt_eflags);
 }
 
 void __init efi_runtime_mkexec(void)
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 17e80d829df0..427eb3540e5f 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -42,7 +42,6 @@
 #include <asm/time.h>
 
 static pgd_t *save_pgd __initdata;
-static unsigned long efi_flags __initdata;
 
 /*
  * We allocate runtime services regions bottom-up, starting from -4G, i.e.
@@ -88,7 +87,6 @@ void __init efi_call_phys_prolog(void)
 		return;
 
 	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);
@@ -116,7 +114,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);
 }
 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction
  2015-03-03  6:34 ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Ingo Molnar
@ 2015-03-03  6:48   ` Ingo Molnar
  2015-03-10 13:36     ` Matt Fleming
  2015-03-10 13:36   ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Matt Fleming
  1 sibling, 1 reply; 5+ messages in thread
From: Ingo Molnar @ 2015-03-03  6:48 UTC (permalink / raw)
  To: Tapasweni Pathak
  Cc: matt.fleming, tglx, mingo, hpa, x86, linux-efi, linux-kernel,
	Borislav Petkov


Also clean up the save_pgd global variable while at it.

untested as well.

Thanks,

	Ingo

==============>
>From 166625ceaef68fcbeee63adc63c02d75abcaf0db Mon Sep 17 00:00:00 2001
From: Ingo Molnar <mingo@kernel.org>
Date: Tue, 3 Mar 2015 07:42:48 +0100
Subject: [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction

Currently x86-64 efi_call_phys_prolog() saves into a global variable (save_pgd),
and efi_call_phys_epilog() restores the kernel pagetables from that global
variable.

Change this to a cleaner save/restore pattern where the saving function returns
the saved object and the restore function restores that.

Apply the same concept to the 32-bit code as well.

Plus this approach, as an added bonus, allows us to express the
!efi_enabled(EFI_OLD_MEMMAP) situation in a clean fashion as well,
via a 'NULL' return value.

Cc: Tapasweni Pathak <tapaswenipathak@gmail.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 arch/x86/include/asm/efi.h     |  6 ++++--
 arch/x86/platform/efi/efi.c    |  5 +++--
 arch/x86/platform/efi/efi_32.c | 11 ++++++++---
 arch/x86/platform/efi/efi_64.c | 26 ++++++++++++++++----------
 4 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 25bce45c6fc4..3738b138b843 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -2,6 +2,8 @@
 #define _ASM_X86_EFI_H
 
 #include <asm/i387.h>
+#include <asm/pgtable.h>
+
 /*
  * We map the EFI regions needed for runtime services non-contiguously,
  * with preserved alignment on virtual addresses starting from -4G down
@@ -89,8 +91,8 @@ extern void __iomem *__init efi_ioremap(unsigned long addr, unsigned long size,
 extern struct efi_scratch efi_scratch;
 extern void __init efi_set_executable(efi_memory_desc_t *md, bool executable);
 extern int __init efi_memblock_x86_reserve_range(void);
-extern void __init efi_call_phys_prolog(void);
-extern void __init efi_call_phys_epilog(void);
+extern pgd_t * __init efi_call_phys_prolog(void);
+extern void __init efi_call_phys_epilog(pgd_t *save_pgd);
 extern void __init efi_unmap_memmap(void);
 extern void __init efi_memory_uc(u64 addr, unsigned long size);
 extern void __init efi_map_region(efi_memory_desc_t *md);
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 2490a15d18f1..aa5bd2b986c2 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -86,8 +86,9 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
 {
 	efi_status_t status;
 	unsigned long flags;
+	pgd_t *save_pgd;
 
-	efi_call_phys_prolog();
+	save_pgd = efi_call_phys_prolog();
 
 	/* Disable interrupts around EFI calls: */
 	local_irq_save(flags);
@@ -96,7 +97,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
 			       descriptor_version, virtual_map);
 	local_irq_restore(flags);
 
-	efi_call_phys_epilog();
+	efi_call_phys_epilog(save_pgd);
 
 	return status;
 }
diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c
index abecc6e1dc90..ed5b67338294 100644
--- a/arch/x86/platform/efi/efi_32.c
+++ b/arch/x86/platform/efi/efi_32.c
@@ -56,19 +56,24 @@ void __init efi_map_region(efi_memory_desc_t *md)
 void __init efi_map_region_fixed(efi_memory_desc_t *md) {}
 void __init parse_efi_setup(u64 phys_addr, u32 data_len) {}
 
-void __init efi_call_phys_prolog(void)
+pgd_t * __init efi_call_phys_prolog(void)
 {
 	struct desc_ptr gdt_descr;
+	pgd_t *save_pgd;
 
+	/* Current pgd is swapper_pg_dir, we'll restore it later: */
+	save_pgd = swapper_pg_dir;
 	load_cr3(initial_page_table);
 	__flush_tlb_all();
 
 	gdt_descr.address = __pa(get_cpu_gdt_table(0));
 	gdt_descr.size = GDT_SIZE - 1;
 	load_gdt(&gdt_descr);
+
+	return save_pgd;
 }
 
-void __init efi_call_phys_epilog(void)
+void __init efi_call_phys_epilog(pgd_t *save_pgd)
 {
 	struct desc_ptr gdt_descr;
 
@@ -76,7 +81,7 @@ void __init efi_call_phys_epilog(void)
 	gdt_descr.size = GDT_SIZE - 1;
 	load_gdt(&gdt_descr);
 
-	load_cr3(swapper_pg_dir);
+	load_cr3(save_pgd);
 	__flush_tlb_all();
 }
 
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 427eb3540e5f..a0ac0f9c307f 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -41,8 +41,6 @@
 #include <asm/realmode.h>
 #include <asm/time.h>
 
-static pgd_t *save_pgd __initdata;
-
 /*
  * We allocate runtime services regions bottom-up, starting from -4G, i.e.
  * 0xffff_ffff_0000_0000 and limit EFI VA mapping space to 64G.
@@ -77,14 +75,16 @@ static void __init early_code_mapping_set_exec(int executable)
 	}
 }
 
-void __init efi_call_phys_prolog(void)
+pgd_t * __init efi_call_phys_prolog(void)
 {
 	unsigned long vaddress;
+	pgd_t *save_pgd;
+
 	int pgd;
 	int n_pgds;
 
 	if (!efi_enabled(EFI_OLD_MEMMAP))
-		return;
+		return NULL;
 
 	early_code_mapping_set_exec(1);
 
@@ -97,22 +97,28 @@ void __init efi_call_phys_prolog(void)
 		set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), *pgd_offset_k(vaddress));
 	}
 	__flush_tlb_all();
+
+	return save_pgd;
 }
 
-void __init efi_call_phys_epilog(void)
+void __init efi_call_phys_epilog(pgd_t *save_pgd)
 {
 	/*
 	 * After the lock is released, the original page table is restored.
 	 */
-	int pgd;
-	int n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
+	int pgd_idx;
+	int nr_pgds;
 
-	if (!efi_enabled(EFI_OLD_MEMMAP))
+	if (!save_pgd)
 		return;
 
-	for (pgd = 0; pgd < n_pgds; pgd++)
-		set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), save_pgd[pgd]);
+	nr_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
+
+	for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++)
+		set_pgd(pgd_offset_k(pgd_idx * PGDIR_SIZE), save_pgd[pgd_idx]);
+
 	kfree(save_pgd);
+
 	__flush_tlb_all();
 	early_code_mapping_set_exec(0);
 }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls
  2015-03-03  6:34 ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Ingo Molnar
  2015-03-03  6:48   ` [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction Ingo Molnar
@ 2015-03-10 13:36   ` Matt Fleming
  1 sibling, 0 replies; 5+ messages in thread
From: Matt Fleming @ 2015-03-10 13:36 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Tapasweni Pathak, matt.fleming, tglx, mingo, hpa, x86, linux-efi,
	linux-kernel

On Tue, 03 Mar, at 07:34:33AM, Ingo Molnar wrote:
> 
> So why are interrupts disabled around page table operations to begin 
> with? It's not like any of this can execute on two CPUs at once, nor 
> can this be executed from interrupt context.
> 
> So, shouldn't we only protect the EFI calls themselves? If we did that 
> we could save the efi_flags global variable ugliness as well.
> 
> I.e. something like the attached patch. (not tested and all that)

Looks good to me Ingo. I'll run it through the test farm. Thanks!

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction
  2015-03-03  6:48   ` [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction Ingo Molnar
@ 2015-03-10 13:36     ` Matt Fleming
  0 siblings, 0 replies; 5+ messages in thread
From: Matt Fleming @ 2015-03-10 13:36 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Tapasweni Pathak, matt.fleming, tglx, mingo, hpa, x86, linux-efi,
	linux-kernel, Borislav Petkov

On Tue, 03 Mar, at 07:48:50AM, Ingo Molnar wrote:
> 
> Also clean up the save_pgd global variable while at it.
> 
> untested as well.
> 
> Thanks,
> 
> 	Ingo
> 
> ==============>
> From 166625ceaef68fcbeee63adc63c02d75abcaf0db Mon Sep 17 00:00:00 2001
> From: Ingo Molnar <mingo@kernel.org>
> Date: Tue, 3 Mar 2015 07:42:48 +0100
> Subject: [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction
> 
> Currently x86-64 efi_call_phys_prolog() saves into a global variable (save_pgd),
> and efi_call_phys_epilog() restores the kernel pagetables from that global
> variable.
> 
> Change this to a cleaner save/restore pattern where the saving function returns
> the saved object and the restore function restores that.
> 
> Apply the same concept to the 32-bit code as well.
> 
> Plus this approach, as an added bonus, allows us to express the
> !efi_enabled(EFI_OLD_MEMMAP) situation in a clean fashion as well,
> via a 'NULL' return value.
> 
> Cc: Tapasweni Pathak <tapaswenipathak@gmail.com>
> Signed-off-by: Ingo Molnar <mingo@kernel.org>
> ---
>  arch/x86/include/asm/efi.h     |  6 ++++--
>  arch/x86/platform/efi/efi.c    |  5 +++--
>  arch/x86/platform/efi/efi_32.c | 11 ++++++++---
>  arch/x86/platform/efi/efi_64.c | 26 ++++++++++++++++----------
>  4 files changed, 31 insertions(+), 17 deletions(-)

Applied, thanks!

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2015-03-10 13:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-03  2:58 [PATCH] arch: x86: platform: efi: Disabling interrupt around kmalloc Tapasweni Pathak
2015-03-03  6:34 ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Ingo Molnar
2015-03-03  6:48   ` [PATCH] efi: Clean up the efi_call_phys_[prolog|epilog]() save/restore interaction Ingo Molnar
2015-03-10 13:36     ` Matt Fleming
2015-03-10 13:36   ` [PATCH] efi: Disable interrupts around EFI calls, not in the epilog/prolog calls Matt Fleming

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).