Skip to contentSkip to conversation

EFI urgent fixes

GIT PULL New Matt Fleming 2 participants 11 messages May 26, 2017 – May 28, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

Hi folks,

Please pull the following fixes. There are patches that resolve a few boot crashes and some minor build and boot log cleanups.

The following changes since commit 08332893e37af6ae779367e78e444f8f9571511d:

Linux 4.12-rc2 (2017-05-21 19:30:23 -0700)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git tags/efi-urgent

for you to fetch changes up to 5d36982a80248b2dcce395e7fa4ba342e814ced1:

efi/bgrt: Skip efi_bgrt_init in case of non-efi boot (2017-05-26 11:27:38 +0100)

----------------------------------------------------------------
- Make the boot console quiet when using Xen on EFI by deleting a pointless error message - Juergen Gross

- Silence harmless warnings emitted with GCC 7 - Arnd Bergmann

- Prevent a crash when booting kexec with the efi=old_map kernel command line parameter by disabling EFI runtime services - Sai Praneeth

- Fix boot crash when using kaslr and efi=old_map. The crash is caused because of assumptions about PAGE_OFFSET alignment which are not true with kaslr enabled - Baoquan He

- Fix boot regression when a machine has an ACPI BGRT table and is booted using BIOS, not EFI. - Dave Young

----------------------------------------------------------------
Arnd Bergmann (1):
efi: Remove duplicate 'const' specifiers

Baoquan He (1):
x86/efi: Correct ident mapping of efi old_map when kalsr enabled

Dave Young (1):
efi/bgrt: Skip efi_bgrt_init in case of non-efi boot

Juergen Gross (1):
efi: Don't issue error message when booted under xen

Sai Praneeth (1):
x86/efi: Disable runtime services on kexec kernel if booted with efi=old_map

 arch/x86/platform/efi/efi.c               |  6 ++-
 arch/x86/platform/efi/efi_64.c            | 79 +++++++++++++++++++++++++++----
 arch/x86/platform/efi/quirks.c            |  3 ++
 drivers/firmware/efi/efi-bgrt.c           |  3 ++
 drivers/firmware/efi/libstub/secureboot.c |  4 +-
 5 files changed, 83 insertions(+), 12 deletions(-)
#1 lore raw

PATCH 1/5 efi: Don't issue error message when booted under xen

From: Juergen Gross <[email protected]>

When booted as Xen dom0 there won't be an EFI memmap allocated. Avoid issuing an error message in this case:

[    0.144079] efi: Failed to allocate new EFI memmap
Cc "H. Peter Anvin" <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Ard Biesheuvel <[email protected]>
Cc <[email protected]> # v4.9+
---
 arch/x86/platform/efi/quirks.c | 3 +++
 1 file changed, 3 insertions(+)
arch/x86/platform/efi/quirks.c +3 -0
index 26615991d69c..e0cf95a83f3f 100644
@@ -360,6 +360,9 @@ void __init efi_free_boot_services(void)
 		free_bootmem_late(start, size);
 	}
 
+	if (!num_entries)
+		return;
+
 	new_size = efi.memmap.desc_size * num_entries;
 	new_phys = efi_memmap_alloc(num_entries);
 	if (!new_phys) {
Signature · 1 line
2.12.2
#2 lore raw

[tip:efi/urgent] efi: Don't issue error message when booted under Xen

efi: Don't issue error message when booted under Xen
Commit-ID:  1ea34adb87c969b89dfd83f1905a79161e9ada26
Gitweb:     http://git.kernel.org/tip/1ea34adb87c969b89dfd83f1905a79161e9ada26
Author:     Juergen Gross <[email protected]>
AuthorDate: Fri, 26 May 2017 12:36:47 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 28 May 2017 11:06:16 +0200

efi: Don't issue error message when booted under Xen

When booted as Xen dom0 there won't be an EFI memmap allocated. Avoid issuing an error message in this case:

  [    0.144079] efi: Failed to allocate new EFI memmap
---
 arch/x86/platform/efi/quirks.c | 3 +++
 1 file changed, 3 insertions(+)
arch/x86/platform/efi/quirks.c +3 -0
index 2661599..e0cf95a 100644
@@ -360,6 +360,9 @@ void __init efi_free_boot_services(void)
 		free_bootmem_late(start, size);
 	}
 
+	if (!num_entries)
+		return;
+
 	new_size = efi.memmap.desc_size * num_entries;
 	new_phys = efi_memmap_alloc(num_entries);
 	if (!new_phys) {
#3 lore raw

PATCH 2/5 efi: Remove duplicate 'const' specifiers

In reply to Matt Fleming

From: Arnd Bergmann <[email protected]>

gcc-7 shows a harmless warning:

drivers/firmware/efi/libstub/secureboot.c:19:27: error: duplicate 'const' declaration specifier [-Werror=duplicate-decl-specifier]
static const efi_char16_t const efi_SecureBoot_name[] = {
drivers/firmware/efi/libstub/secureboot.c:22:27: error: duplicate 'const' declaration specifier [-Werror=duplicate-decl-specifier]

Removing one of the specifiers gives us the expected behavior.

Fixes de8cb458625c ("efi: Get and store the secure boot status")
Cc Josh Boyer <jwboyer-rxtnV0ftBwyoClj4AeEUq9i2O/[email protected]>
Cc Ingo Molnar <[email protected]>
---
 drivers/firmware/efi/libstub/secureboot.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/firmware/efi/libstub/secureboot.c +2 -2
index 8c34d50a4d80..959777ec8a77 100644
@@ -16,10 +16,10 @@
 
 /* BIOS variables */
 static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
-static const efi_char16_t const efi_SecureBoot_name[] = {
+static const efi_char16_t efi_SecureBoot_name[] = {
 	'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0
 };
-static const efi_char16_t const efi_SetupMode_name[] = {
+static const efi_char16_t efi_SetupMode_name[] = {
 	'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0
 };
 
Signature · 1 line
2.12.2
#4 lore raw

[tip:efi/urgent] efi: Remove duplicate 'const' specifiers

efi: Remove duplicate 'const' specifiers
Commit-ID:  684e3f965d0be8c26fedefe94f637374242aba08
Gitweb:     http://git.kernel.org/tip/684e3f965d0be8c26fedefe94f637374242aba08
Author:     Arnd Bergmann <[email protected]>
AuthorDate: Fri, 26 May 2017 12:36:48 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 28 May 2017 11:06:16 +0200

efi: Remove duplicate 'const' specifiers

gcc-7 shows these harmless warnings:

drivers/firmware/efi/libstub/secureboot.c:19:27: error: duplicate 'const' declaration specifier [-Werror=duplicate-decl-specifier]
static const efi_char16_t const efi_SecureBoot_name[] = {
drivers/firmware/efi/libstub/secureboot.c:22:27: error: duplicate 'const' declaration specifier [-Werror=duplicate-decl-specifier]

Removing one of the specifiers gives us the expected behavior.

Cc Josh Boyer <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>
Fixes de8cb458625c ("efi: Get and store the secure boot status")
---
 drivers/firmware/efi/libstub/secureboot.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/firmware/efi/libstub/secureboot.c +2 -2
index 8c34d50..959777e 100644
@@ -16,10 +16,10 @@
 
 /* BIOS variables */
 static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
-static const efi_char16_t const efi_SecureBoot_name[] = {
+static const efi_char16_t efi_SecureBoot_name[] = {
 	'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0
 };
-static const efi_char16_t const efi_SetupMode_name[] = {
+static const efi_char16_t efi_SetupMode_name[] = {
 	'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0
 };
 
#5 lore raw

PATCH 3/5 x86/efi: Disable runtime services on kexec kernel if booted with efi=old_map

In reply to Matt Fleming

From: Sai Praneeth <[email protected]>

Booting kexec kernel with "efi=old_map" in kernel command line hits kernel panic as shown below.

 BUG: unable to handle kernel paging request at ffff88007fe78070
 IP: virt_efi_set_variable.part.7+0x63/0x1b0
 PGD 7ea28067
 PUD 7ea2b067
 PMD 7ea2d067
 PTE 0
 [...]
 Call Trace:
  virt_efi_set_variable+0x5d/0x70
  efi_delete_dummy_variable+0x7a/0x80
  efi_enter_virtual_mode+0x3f6/0x4a7
  start_kernel+0x375/0x400
  x86_64_start_reservations+0x2a/0x2c
  x86_64_start_kernel+0x168/0x176
  start_cpu+0x14/0x14

[ efi=old_map was never intended to work with kexec. The problem with
using efi=old_map is that the virtual addresses are assigned from the memory region used by other kernel mappings; vmalloc() space. Potentially there could be collisions when booting kexec if something else is mapped at the virtual address we allocated for runtime service regions in the initial boot - Matt Fleming ]

Since kexec was never intended to work with efi=old_map, disable runtime services in kexec if booted with efi=old_map, so that we don't panic.

---
 arch/x86/platform/efi/efi.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
arch/x86/platform/efi/efi.c +4 -2
index 7e76a4d8304b..43b96f5f78ba 100644
@@ -828,9 +828,11 @@ static void __init kexec_enter_virtual_mode(void)
 
 	/*
 	 * We don't do virtual mode, since we don't do runtime services, on
-	 * non-native EFI
+	 * non-native EFI. With efi=old_map, we don't do runtime services in
+	 * kexec kernel because in the initial boot something else might
+	 * have been mapped at these virtual addresses.
 	 */
-	if (!efi_is_native()) {
+	if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) {
 		efi_memmap_unmap();
 		clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 		return;
Signature · 1 line
2.12.2
#6 lore raw

[tip:efi/urgent] x86/efi: Disable runtime services on kexec kernel if booted with efi=old_map

x86/efi: Disable runtime services on kexec kernel if booted with efi=old_map
Commit-ID:  4e52797d2efefac3271abdc54439a3435abd77b9
Gitweb:     http://git.kernel.org/tip/4e52797d2efefac3271abdc54439a3435abd77b9
Author:     Sai Praneeth <[email protected]>
AuthorDate: Fri, 26 May 2017 12:36:49 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 28 May 2017 11:06:16 +0200

x86/efi: Disable runtime services on kexec kernel if booted with efi=old_map

Booting kexec kernel with "efi=old_map" in kernel command line hits kernel panic as shown below.

 BUG: unable to handle kernel paging request at ffff88007fe78070
 IP: virt_efi_set_variable.part.7+0x63/0x1b0
 PGD 7ea28067
 PUD 7ea2b067
 PMD 7ea2d067
 PTE 0
 [...]
 Call Trace:
  virt_efi_set_variable()
  efi_delete_dummy_variable()
  efi_enter_virtual_mode()
  start_kernel()
  x86_64_start_reservations()
  x86_64_start_kernel()
  start_cpu()

[ efi=old_map was never intended to work with kexec. The problem with
using efi=old_map is that the virtual addresses are assigned from the memory region used by other kernel mappings; vmalloc() space. Potentially there could be collisions when booting kexec if something else is mapped at the virtual address we allocated for runtime service regions in the initial boot - Matt Fleming ]

Since kexec was never intended to work with efi=old_map, disable runtime services in kexec if booted with efi=old_map, so that we don't panic.

---
 arch/x86/platform/efi/efi.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
arch/x86/platform/efi/efi.c +4 -2
index 7e76a4d..43b96f5 100644
@@ -828,9 +828,11 @@ static void __init kexec_enter_virtual_mode(void)
 
 	/*
 	 * We don't do virtual mode, since we don't do runtime services, on
-	 * non-native EFI
+	 * non-native EFI. With efi=old_map, we don't do runtime services in
+	 * kexec kernel because in the initial boot something else might
+	 * have been mapped at these virtual addresses.
 	 */
-	if (!efi_is_native()) {
+	if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) {
 		efi_memmap_unmap();
 		clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 		return;
#7 lore raw

PATCH 4/5 x86/efi: Correct ident mapping of efi old_map when kalsr enabled

In reply to Matt Fleming

From: Baoquan He <[email protected]>

For EFI with 'efi=old_map' kernel option specified, Kernel will panic when kaslr is enabled.

The back trace is:

BUG: unable to handle kernel paging request at 000000007febd57e

IP: 0x7febd57e
PGD 1025a067
PUD 0

Oops: 0010 [#1] SMP
[ ... ]
Call Trace:
 ? efi_call+0x58/0x90
 ? printk+0x58/0x6f
 efi_enter_virtual_mode+0x3c5/0x50d
 start_kernel+0x40f/0x4b8
 ? set_init_arg+0x55/0x55
 ? early_idt_handler_array+0x120/0x120
 x86_64_start_reservations+0x24/0x26
 x86_64_start_kernel+0x14c/0x16f
 start_cpu+0x14/0x14

The root cause is the ident mapping is not built correctly in old_map case.

For nokaslr kernel, PAGE_OFFSET is 0xffff880000000000 which is PGDIR_SIZE aligned. We can borrow the pud table from direct mapping safely. Given a physical address X, we have pud_index(X) == pud_index(__va(X)). However, for kaslr kernel, PAGE_OFFSET is PUD_SIZE aligned. For a given physical address X, pud_index(X) != pud_index(__va(X)). We can't only copy pgd entry from direct mapping to build ident mapping, instead need copy pud entry one by one from direct mapping.

Fix it.

Cc Dave Young <[email protected]>
Cc Ard Biesheuvel <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc "H. Peter Anvin" <[email protected]>
Cc Thomas Garnier <[email protected]>
Cc Kees Cook <[email protected]>
Cc Russ Anderson <[email protected]>
Cc Frank Ramsay <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Bhupesh Sharma <[email protected]>
---
 arch/x86/platform/efi/efi_64.c | 79 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 71 insertions(+), 8 deletions(-)
arch/x86/platform/efi/efi_64.c +71 -8
index c488625c9712..548728949324 100644
@@ -71,11 +71,13 @@ static void __init early_code_mapping_set_exec(int executable)
 
 pgd_t * __init efi_call_phys_prolog(void)
 {
-	unsigned long vaddress;
-	pgd_t *save_pgd;
+	unsigned long vaddr, addr_pgd, addr_p4d, addr_pud;
+	pgd_t *save_pgd, *pgd_k, *pgd_efi;
+	p4d_t *p4d, *p4d_k, *p4d_efi;
+	pud_t *pud;
 
 	int pgd;
-	int n_pgds;
+	int n_pgds, i, j;
 
 	if (!efi_enabled(EFI_OLD_MEMMAP)) {
 		save_pgd = (pgd_t *)read_cr3();
@@ -88,10 +90,49 @@ pgd_t * __init efi_call_phys_prolog(void)
 	n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
 	save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL);
 
+	/*
+	 * Build 1:1 ident mapping for old_map usage. It needs to be noticed
+	 * that PAGE_OFFSET is PGDIR_SIZE aligned with KASLR disabled, while
+	 * PUD_SIZE ALIGNED with KASLR enabled. So for a given physical
+	 * address X, the pud_index(X) != pud_index(__va(X)), we can only copy
+	 * pud entry of __va(X) to fill in pud entry of X to build 1:1 mapping
+	 * . Means here we can only reuse pmd table of direct mapping.
+	 */
 	for (pgd = 0; pgd < n_pgds; pgd++) {
-		save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
-		vaddress = (unsigned long)__va(pgd * PGDIR_SIZE);
-		set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), *pgd_offset_k(vaddress));
+		addr_pgd = (unsigned long)(pgd * PGDIR_SIZE);
+		vaddr = (unsigned long)__va(pgd * PGDIR_SIZE);
+		pgd_efi = pgd_offset_k(addr_pgd);
+		save_pgd[pgd] = *pgd_efi;
+
+		p4d = p4d_alloc(&init_mm, pgd_efi, addr_pgd);
+		if (!p4d) {
+			pr_err("Failed to allocate p4d table!\n");
+			goto out;
+		}
+
+		for (i = 0; i < PTRS_PER_P4D; i++) {
+			addr_p4d = addr_pgd + i * P4D_SIZE;
+			p4d_efi = p4d + p4d_index(addr_p4d);
+
+			pud = pud_alloc(&init_mm, p4d_efi, addr_p4d);
+			if (!pud) {
+				pr_err("Failed to allocate pud table!\n");
+				goto out;
+			}
+
+			for (j = 0; j < PTRS_PER_PUD; j++) {
+				addr_pud = addr_p4d + j * PUD_SIZE;
+
+				if (addr_pud > (max_pfn << PAGE_SHIFT))
+					break;
+
+				vaddr = (unsigned long)__va(addr_pud);
+
+				pgd_k = pgd_offset_k(vaddr);
+				p4d_k = p4d_offset(pgd_k, vaddr);
+				pud[j] = *pud_offset(p4d_k, vaddr);
+			}
+		}
 	}
 out:
 	__flush_tlb_all();
@@ -104,8 +145,11 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
 	/*
 	 * After the lock is released, the original page table is restored.
 	 */
-	int pgd_idx;
+	int pgd_idx, i;
 	int nr_pgds;
+	pgd_t *pgd;
+	p4d_t *p4d;
+	pud_t *pud;
 
 	if (!efi_enabled(EFI_OLD_MEMMAP)) {
 		write_cr3((unsigned long)save_pgd);
@@ -115,9 +159,28 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
 
 	nr_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
 
-	for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++)
+	for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++) {
+		pgd = pgd_offset_k(pgd_idx * PGDIR_SIZE);
 		set_pgd(pgd_offset_k(pgd_idx * PGDIR_SIZE), save_pgd[pgd_idx]);
 
+		if (!(pgd_val(*pgd) & _PAGE_PRESENT))
+			continue;
+
+		for (i = 0; i < PTRS_PER_P4D; i++) {
+			p4d = p4d_offset(pgd,
+					 pgd_idx * PGDIR_SIZE + i * P4D_SIZE);
+
+			if (!(p4d_val(*p4d) & _PAGE_PRESENT))
+				continue;
+
+			pud = (pud_t *)p4d_page_vaddr(*p4d);
+			pud_free(&init_mm, pud);
+		}
+
+		p4d = (p4d_t *)pgd_page_vaddr(*pgd);
+		p4d_free(&init_mm, p4d);
+	}
+
 	kfree(save_pgd);
 
 	__flush_tlb_all();
Signature · 1 line
2.12.2
#8 lore raw

[tip:efi/urgent] x86/efi: Correct EFI identity mapping under 'efi=old_map' when KASLR is enabled

x86/efi: Correct EFI identity mapping under 'efi=old_map' when KASLR is enabled
Commit-ID:  94133e46a0f5ca3f138479806104ab4a8cb0455e
Gitweb:     http://git.kernel.org/tip/94133e46a0f5ca3f138479806104ab4a8cb0455e
Author:     Baoquan He <[email protected]>
AuthorDate: Fri, 26 May 2017 12:36:50 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 28 May 2017 11:06:16 +0200

x86/efi: Correct EFI identity mapping under 'efi=old_map' when KASLR is enabled

For EFI with the 'efi=old_map' kernel option specified, the kernel will panic when KASLR is enabled:

  BUG: unable to handle kernel paging request at 000000007febd57e
  IP: 0x7febd57e
  PGD 1025a067
  PUD 0

  Oops: 0010 [#1] SMP
  Call Trace:
   efi_enter_virtual_mode()
   start_kernel()
   x86_64_start_reservations()
   x86_64_start_kernel()
   start_cpu()

The root cause is that the identity mapping is not built correctly in the 'efi=old_map' case.

On 'nokaslr' kernels, PAGE_OFFSET is 0xffff880000000000 which is PGDIR_SIZE aligned. We can borrow the PUD table from the direct mappings safely. Given a physical address X, we have pud_index(X) == pud_index(__va(X)).

However, on KASLR kernels, PAGE_OFFSET is PUD_SIZE aligned. For a given physical address X, pud_index(X) != pud_index(__va(X)). We can't just copy the PGD entry from direct mapping to build identity mapping, instead we need to copy the PUD entries one by one from the direct mapping.

Fix it.

Cc Ard Biesheuvel <[email protected]>
Cc Bhupesh Sharma <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Dave Young <[email protected]>
Cc Frank Ramsay <[email protected]>
Cc Kees Cook <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Russ Anderson <[email protected]>
Cc Thomas Garnier <[email protected]>
Cc Thomas Gleixner <[email protected]>
[ Fixed and reworded the changelog and code comments to be more readable. ]
---
 arch/x86/platform/efi/efi_64.c | 79 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 71 insertions(+), 8 deletions(-)
arch/x86/platform/efi/efi_64.c +71 -8
index c488625..eb8dff1 100644
@@ -71,11 +71,13 @@ static void __init early_code_mapping_set_exec(int executable)
 
 pgd_t * __init efi_call_phys_prolog(void)
 {
-	unsigned long vaddress;
-	pgd_t *save_pgd;
+	unsigned long vaddr, addr_pgd, addr_p4d, addr_pud;
+	pgd_t *save_pgd, *pgd_k, *pgd_efi;
+	p4d_t *p4d, *p4d_k, *p4d_efi;
+	pud_t *pud;
 
 	int pgd;
-	int n_pgds;
+	int n_pgds, i, j;
 
 	if (!efi_enabled(EFI_OLD_MEMMAP)) {
 		save_pgd = (pgd_t *)read_cr3();
@@ -88,10 +90,49 @@ pgd_t * __init efi_call_phys_prolog(void)
 	n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
 	save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL);
 
+	/*
+	 * Build 1:1 identity mapping for efi=old_map usage. Note that
+	 * PAGE_OFFSET is PGDIR_SIZE aligned when KASLR is disabled, while
+	 * it is PUD_SIZE ALIGNED with KASLR enabled. So for a given physical
+	 * address X, the pud_index(X) != pud_index(__va(X)), we can only copy
+	 * PUD entry of __va(X) to fill in pud entry of X to build 1:1 mapping.
+	 * This means here we can only reuse the PMD tables of the direct mapping.
+	 */
 	for (pgd = 0; pgd < n_pgds; pgd++) {
-		save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
-		vaddress = (unsigned long)__va(pgd * PGDIR_SIZE);
-		set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), *pgd_offset_k(vaddress));
+		addr_pgd = (unsigned long)(pgd * PGDIR_SIZE);
+		vaddr = (unsigned long)__va(pgd * PGDIR_SIZE);
+		pgd_efi = pgd_offset_k(addr_pgd);
+		save_pgd[pgd] = *pgd_efi;
+
+		p4d = p4d_alloc(&init_mm, pgd_efi, addr_pgd);
+		if (!p4d) {
+			pr_err("Failed to allocate p4d table!\n");
+			goto out;
+		}
+
+		for (i = 0; i < PTRS_PER_P4D; i++) {
+			addr_p4d = addr_pgd + i * P4D_SIZE;
+			p4d_efi = p4d + p4d_index(addr_p4d);
+
+			pud = pud_alloc(&init_mm, p4d_efi, addr_p4d);
+			if (!pud) {
+				pr_err("Failed to allocate pud table!\n");
+				goto out;
+			}
+
+			for (j = 0; j < PTRS_PER_PUD; j++) {
+				addr_pud = addr_p4d + j * PUD_SIZE;
+
+				if (addr_pud > (max_pfn << PAGE_SHIFT))
+					break;
+
+				vaddr = (unsigned long)__va(addr_pud);
+
+				pgd_k = pgd_offset_k(vaddr);
+				p4d_k = p4d_offset(pgd_k, vaddr);
+				pud[j] = *pud_offset(p4d_k, vaddr);
+			}
+		}
 	}
 out:
 	__flush_tlb_all();
@@ -104,8 +145,11 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
 	/*
 	 * After the lock is released, the original page table is restored.
 	 */
-	int pgd_idx;
+	int pgd_idx, i;
 	int nr_pgds;
+	pgd_t *pgd;
+	p4d_t *p4d;
+	pud_t *pud;
 
 	if (!efi_enabled(EFI_OLD_MEMMAP)) {
 		write_cr3((unsigned long)save_pgd);
@@ -115,9 +159,28 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
 
 	nr_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE);
 
-	for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++)
+	for (pgd_idx = 0; pgd_idx < nr_pgds; pgd_idx++) {
+		pgd = pgd_offset_k(pgd_idx * PGDIR_SIZE);
 		set_pgd(pgd_offset_k(pgd_idx * PGDIR_SIZE), save_pgd[pgd_idx]);
 
+		if (!(pgd_val(*pgd) & _PAGE_PRESENT))
+			continue;
+
+		for (i = 0; i < PTRS_PER_P4D; i++) {
+			p4d = p4d_offset(pgd,
+					 pgd_idx * PGDIR_SIZE + i * P4D_SIZE);
+
+			if (!(p4d_val(*p4d) & _PAGE_PRESENT))
+				continue;
+
+			pud = (pud_t *)p4d_page_vaddr(*p4d);
+			pud_free(&init_mm, pud);
+		}
+
+		p4d = (p4d_t *)pgd_page_vaddr(*pgd);
+		p4d_free(&init_mm, p4d);
+	}
+
 	kfree(save_pgd);
 
 	__flush_tlb_all();
#9 lore raw

PATCH 5/5 efi/bgrt: Skip efi_bgrt_init in case of non-efi boot

In reply to Matt Fleming

From: Dave Young <[email protected]>

Sabrina Dubroca reported an early panic below, it was introduced by commit 7b0a911478c7 ("efi/x86: Move the EFI BGRT init code to early init code"). The cause is on this machine even for legacy boot firmware still provide the ACPI BGRT table which should be EFI only. Thus the garbage bgrt data caused the efi_bgrt_init panic.

Add a checking to skip efi_bgrt_init in case non EFI booting solves this problem.

BUG: unable to handle kernel paging request at ffffffffff240001
IP: efi_bgrt_init+0xdc/0x134

PGD 1ac0c067
PUD 1ac0e067
PMD 1aee9067
PTE 9380701800000163

Oops: 0009 [#1] SMP
Modules linked in:
CPU: 0 PID: 0 Comm: swapper Not tainted 4.10.0-rc5-00116-g7b0a911 #19
Hardware name: Hewlett-Packard HP Z220 CMT Workstation/1790, BIOS K51 v01.02 05/03/2012
task: ffffffff9fc10500 task.stack: ffffffff9fc00000
RIP: 0010:efi_bgrt_init+0xdc/0x134
RSP: 0000:ffffffff9fc03d58 EFLAGS: 00010082
RAX: ffffffffff240001 RBX: 0000000000000000 RCX: 1380701800000006
RDX: 8000000000000163 RSI: 9380701800000163 RDI: 00000000000005be
RBP: ffffffff9fc03d70 R08: 1380701800001000 R09: 0000000000000002
R10: 000000000002d000 R11: ffff98a3dedd2fc6 R12: ffffffff9f9f22b6
R13: ffffffff9ff49480 R14: 0000000000000010 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffffffff9fd20000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: ffffffffff240001 CR3: 000000001ac09000 CR4: 00000000000406b0
Call Trace:
 ? acpi_parse_ioapic+0x98/0x98
 acpi_parse_bgrt+0x9/0xd
 acpi_table_parse+0x7a/0xa9
 acpi_boot_init+0x3c7/0x4f9
 ? acpi_parse_x2apic+0x74/0x74
 ? acpi_parse_x2apic_nmi+0x46/0x46
 setup_arch+0xb4b/0xc6f
 ? printk+0x52/0x6e
 start_kernel+0xb2/0x47b
 ? early_idt_handler_array+0x120/0x120
 x86_64_start_reservations+0x24/0x26
 x86_64_start_kernel+0xf7/0x11a
 start_cpu+0x14/0x14
Code: 48 c7 c7 10 16 a0 9f e8 4e 94 40 ff eb 62 be 06 00 00 00 e8 f9 ff 00 00 48 85 c0 75 0e 48
c7 c7 40 16 a0 9f e8 31 94 40 ff eb 45 <66> 44 8b 20 be 06 00 00 00 48 89 c7 8b 58 02 e8 87 00
01 00 66
RIP: efi_bgrt_init+0xdc/0x134 RSP: ffffffff9fc03d58
CR2: ffffffffff240001
---[ end trace f68728a0d3053b52 ]---
Kernel panic - not syncing: Attempted to kill the idle task!
---[ end Kernel panic - not syncing: Attempted to kill the idle task!
Fixes 7b0a911478c7 ("efi/x86: Move the EFI BGRT init code to early init code")
Cc <[email protected]> # v4.11+
---
 drivers/firmware/efi/efi-bgrt.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/firmware/efi/efi-bgrt.c +3 -0
index 04ca8764f0c0..8bf27323f7a3 100644
@@ -36,6 +36,9 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
 	if (acpi_disabled)
 		return;
 
+	if (!efi_enabled(EFI_BOOT))
+		return;
+
 	if (table->length < sizeof(bgrt_tab)) {
 		pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
 		       table->length, sizeof(bgrt_tab));
Signature · 1 line
2.12.2
#10 lore raw

[tip:efi/urgent] efi/bgrt: Skip efi_bgrt_init() in case of non-EFI boot

efi/bgrt: Skip efi_bgrt_init() in case of non-EFI boot
Commit-ID:  7425826f4f7ac60f2538b06a7f0a5d1006405159
Gitweb:     http://git.kernel.org/tip/7425826f4f7ac60f2538b06a7f0a5d1006405159
Author:     Dave Young <[email protected]>
AuthorDate: Fri, 26 May 2017 12:36:51 +0100
Committer:  Ingo Molnar <[email protected]>
CommitDate: Sun, 28 May 2017 11:06:17 +0200

efi/bgrt: Skip efi_bgrt_init() in case of non-EFI boot

Sabrina Dubroca reported an early panic:

  BUG: unable to handle kernel paging request at ffffffffff240001
  IP: efi_bgrt_init+0xdc/0x134

[...]

  ---[ end Kernel panic - not syncing: Attempted to kill the idle task!

... which was introduced by:

7b0a911478c7 ("efi/x86: Move the EFI BGRT init code to early init code")

The cause is that on this machine the firmware provides the EFI ACPI BGRT table even on legacy non-EFI bootups - which table should be EFI only.

The garbage BGRT data causes the efi_bgrt_init() panic.

Add a check to skip efi_bgrt_init() in case non-EFI bootup to work around this firmware bug.

Cc <[email protected]> # v4.11+
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>
Fixes 7b0a911478c7 ("efi/x86: Move the EFI BGRT init code to early init code")
[ Rewrote the changelog to be more readable. ]
---
 drivers/firmware/efi/efi-bgrt.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/firmware/efi/efi-bgrt.c +3 -0
index 04ca876..8bf2732 100644
@@ -36,6 +36,9 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
 	if (acpi_disabled)
 		return;
 
+	if (!efi_enabled(EFI_BOOT))
+		return;
+
 	if (table->length < sizeof(bgrt_tab)) {
 		pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
 		       table->length, sizeof(bgrt_tab));