efi/arm64: unmap the kernel during runtime service calls
Subsystems (3)
cover efi/arm64: unmap the kernel during runtime service calls
UEFI doesn't keep any secrets, so it is mostly unaffected by Meltdown and Spectre. At boot time, when UEFI is in charge of everything, there is simply no point in inferring things from the cache state if you can simply read the data. At runtime, when UEFI runs in the execution context of the OS, it is really up to the OS to ensure that lower privilege levels cannot access OS data structures unpermitted.
This applies equally to Spectre variant 2: UEFI runtime services are not entered directly from userland but always via the kernel, which carries out any required branch predictor maintenance when switching between the various tasks (and the UEFI runtime execution context may be considered a separate task in this sense)
Spectre variant 1 is a different matter though. It requires code changes and software rebuilds with updated compilers to be fully hardened against it, although nobody seems to know exactly what that means at the moment. Given the poor track record of vendors when it comes to keeping UEFI firmware up to date, it is highly likely that vulnerable versions will still be in circulation long after we fixed the OS.
Since UEFI interacts with data structures that the OS may consider opaque (capsule images, authenticated variable updates), it is not really up to the OS to reason about which invocation is safe and which one isn't. The only solution really is to simply unmap the entire kernel during UEFI runtime services invocations, so that there are no secrets to steal to begin with.
Patch #1 is included for completeness. I sent it out before, and it is a dependency for this series, but it is otherwise unrelated.
Patch #2 creates a separate stack for UEFI and puts it in the EFI page tables, along with the asm wrapper that invokes the UEFI runtime services and a vector table that is activated before and deactivated after the service is called.
Patch #3 implements marshalling of all byref arguments taken by UEFI runtime services. This is necessary because they will refer to memory that is going to be unmapped.
Patch #4 implements the actual unmap/remap sequences, by setting/clearing the EPD1 bit in TCR_EL1, and doing a local TLB flush.
Note that capsule update has been omitted. This is a bit involved, and I'd rather get some feedback before burning too many cycles on that. All other services should be functional, with the caveat that EFI variable names are now limited to 1024 [wide] characters, and UEFI variables themselves to 64 KB.
Ard Biesheuvel (4):
efi: arm64: Check whether x18 is preserved by runtime services calls efi/arm64: map the stack and entry wrapper into the UEFI page tables efi/arm64: marshall runtime services arguments via buffer in TTBR0 efi/arm64: unmap the kernel while executing UEFI services
arch/arm/include/asm/efi.h | 5 + arch/arm64/Kconfig | 1 - arch/arm64/include/asm/efi.h | 30 +- arch/arm64/include/asm/stacktrace.h | 4 + arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/efi-rt-wrapper.S | 112 +++++ arch/arm64/kernel/efi.c | 505 +++++++++++++++++++- arch/arm64/kernel/entry.S | 1 + drivers/firmware/efi/arm-runtime.c | 2 + 9 files changed, 658 insertions(+), 5 deletions(-) create mode 100644 arch/arm64/kernel/efi-rt-wrapper.S
Signature · 1 line
2.11.0
PATCH 1/4 efi: arm64: Check whether x18 is preserved by runtime services calls
Whether or not we will ever decide to start using x18 as a platform register in Linux is uncertain, but by that time, we will need to ensure that UEFI runtime services calls don't corrupt it. So let's start issuing warnings now for this, and increase the likelihood that these firmware images have all been replaced by that time.
This has been fixed on the EDK2 side in commit 6d73863b5464 ("BaseTools/tools_def AARCH64: mark register x18 as reserved")., dated July 13, 2017.
--- arch/arm64/include/asm/efi.h | 4 +- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/efi-rt-wrapper.S | 41 ++++++++++++++++++++ arch/arm64/kernel/efi.c | 6 +++ 4 files changed, 52 insertions(+), 2 deletions(-)
arch/arm64/include/asm/efi.h +3 -1
@@ -31,7 +31,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); ({ \ efi_##f##_t *__f; \ __f = p->f; \ - __f(args); \ + __efi_rt_asm_wrapper(__f, #f, args); \ }) #define arch_efi_call_virt_teardown() \
@@ -40,6 +40,8 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);efi_virtmap_unload (); \ }) +efi_status_t __efi_rt_asm_wrapper (void *,const char *, ...); +#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)/* arch specific definitions used by the stub code */
arch/arm64/kernel/Makefile +2 -1
@@ -38,7 +38,8 @@ arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o -arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o +arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o \ + efi-rt-wrapper.o arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.o
arch/arm64/kernel/efi-rt-wrapper.S new file +41 -0
41 lines not on this page · raw message
arch/arm64/kernel/efi.c +6 -0
@@ -124,3 +124,9 @@ bool efi_poweroff_required(void) {return efi_enabled (EFI_RUNTIME_SERVICES ); } + +asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) +{ +pr_err_ratelimited (FW_BUG "register x18 corrupted by EFI %s\n" , f); +return s; +}
Signature · 1 line
2.11.0
PATCH 1/4 efi: arm64: Check whether x18 is preserved by runtime services calls
Quoted 10 lines · Ard Biesheuvel
Whether or not we will ever decide to start using x18 as a platform register in Linux is uncertain, but by that time, we will need to ensure that UEFI runtime services calls don't corrupt it. So let's start issuing warnings now for this, and increase the likelihood that these firmware images have all been replaced by that time.
This has been fixed on the EDK2 side in commit
6d73863b5464("BaseTools/tools_def AARCH64: mark register x18 as reserved")., dated July 13, 2017.
Quoted 7 lines
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 4 +- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/efi-rt-wrapper.S | 41 ++++++++++++++++++++ arch/arm64/kernel/efi.c | 6 +++ 4 files changed, 52 insertions(+), 2 deletions(-)
Looks good to me:
Will
Quoted 100 lines
arch/arm64/include/asm/efi.h +3
-1@@ -31,7 +31,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); ({ \ efi_##f##_t *__f; \ __f = p->f; \ - __f(args); \ + __efi_rt_asm_wrapper(__f, #f, args); \ }) #define arch_efi_call_virt_teardown() \@@ -40,6 +40,8 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);efi_virtmap_unload (); \ }) +efi_status_t __efi_rt_asm_wrapper (void *,const char *, ...); +#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)/* arch specific definitions used by the stub code */ arch/arm64/kernel/Makefile +2
-1@@ -38,7 +38,8 @@ arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o -arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o +arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o \ + efi-rt-wrapper.o arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.oarch/arm64/kernel/efi-rt-wrapper.S new file +41
-0@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 Linaro Ltd <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/linkage.h> + +ENTRY(__efi_rt_asm_wrapper) + stp x29, x30, [sp, #-32]! + mov x29, sp + + /* + * Register x18 is designated as the 'platform' register by the AAPCS, + * which means firmware running at the same exception level as the OS + * (such as UEFI) should never touch it. + */ + stp x1, x18, [sp, #16] + + /* + * We are lucky enough that no EFI runtime services take more than + * 5 arguments, so all are passed in registers rather than via the + * stack. + */ + mov x8, x0 + mov x0, x2 + mov x1, x3 + mov x2, x4 + mov x3, x5 + mov x4, x6 + blr x8 + + ldp x1, x2, [sp, #16] + cmp x2, x18 + ldp x29, x30, [sp], #32 + b.ne 0f + ret +0: b efi_handle_corrupted_x18 // tail call +ENDPROC(__efi_rt_asm_wrapper)arch/arm64/kernel/efi.c +6
-0@@ -124,3 +124,9 @@ bool efi_poweroff_required(void) {return efi_enabled (EFI_RUNTIME_SERVICES ); } + +asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) +{ +pr_err_ratelimited (FW_BUG "register x18 corrupted by EFI %s\n" , f); +return s; +}Signature · 1 line
2.11.0
PATCH 2/4 efi/arm64: map the stack and entry wrapper into the UEFI page tables
In reply to Ard BiesheuvelAs a preparatory step towards unmapping the kernel entirely while executing UEFI runtime services, move the stack and the entry wrapper routine mappings into the EFI page tables. Also, create a vector table that overrides the main one while executing in the firmware so we will be able to remap/unmap the kernel while taking interrupts.
--- arch/arm/include/asm/efi.h | 5 ++ arch/arm64/include/asm/efi.h | 23 ++++++++- arch/arm64/include/asm/stacktrace.h | 4 ++ arch/arm64/kernel/efi-rt-wrapper.S | 51 +++++++++++++++++++- arch/arm64/kernel/efi.c | 24 +++++++++ arch/arm64/kernel/entry.S | 1 + drivers/firmware/efi/arm-runtime.c | 2 + 7 files changed, 107 insertions(+), 3 deletions(-)
arch/arm/include/asm/efi.h +5 -0
@@ -99,4 +99,9 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base, return dram_base + SZ_512M; } +static inline int efi_allocate_runtime_regions (struct mm_struct *mm) +{ +return 0 ; +} +#endif /* _ASM_ARM_EFI_H */
arch/arm64/include/asm/efi.h +21 -2
@@ -2,11 +2,13 @@#ifndef _ASM_EFI_H#define _ASM_EFI_H +#include <asm/memory.h> + +#ifndef __ASSEMBLY__#include <asm/boot.h> #include <asm/cpufeature.h> #include <asm/fpsimd.h> #include <asm/io.h> -#include <asm/memory.h> #include <asm/mmu_context.h> #include <asm/neon.h> #include <asm/ptrace.h>
@@ -30,8 +32,9 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); #define arch_efi_call_virt(p, f, args...) \ ({ \ efi_##f##_t *__f; \ + typeof(__efi_rt_asm_wrapper) *__wrap = (void *)EFI_CODE_BASE; \ __f = p->f; \ - __efi_rt_asm_wrapper(__f, #f, args); \ + __wrap(__f, #f, args); \ })#define arch_efi_call_virt_teardown () \
@@ -146,4 +149,20 @@ static inline void efi_set_pgd(struct mm_struct *mm)void efi_virtmap_load (void );void efi_virtmap_unload (void ); +int __init efi_allocate_runtime_regions (struct mm_struct *mm); + +#endif /* __ASSEMBLY__ */ + +/* +* When running with vmap'ed stacks, we need the base of the stack to be aligned +* appropriately, where the exact alignment depends on the page size. Let's just +* put the stack at address 0x0, which is guaranteed to be free and aligned. +*/ +#define EFI_STACK_BASE 0x0 +#define EFI_STACK_SIZE THREAD_SIZE + +/* where to map the pivot code in the UEFI page tables */ +#define EFI_CODE_BASE 0x200000 +#define EFI_CODE_SIZE PAGE_SIZE +#endif /* _ASM_EFI_H */
arch/arm64/include/asm/stacktrace.h +4 -0
@@ -72,6 +72,8 @@ static inline bool on_overflow_stack(unsigned long sp)static inline bool on_overflow_stack (unsigned long sp) {return false; }#endif +bool on_efi_stack (unsigned long sp); +/* * We can only safely access per-cpu stacks from current in a non-preemptible * context.
@@ -88,6 +90,8 @@ static inline bool on_accessible_stack(struct task_struct *tsk, unsigned long spreturn true;if (on_sdei_stack (sp))return true; +if (on_efi_stack (sp)) +return true;return false; }
arch/arm64/kernel/efi-rt-wrapper.S +50 -1
73 lines not on this page · raw message
arch/arm64/kernel/efi.c +24 -0
@@ -130,3 +130,27 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)pr_err_ratelimited (FW_BUG "register x18 corrupted by EFI %s\n" , f);return s; } + +bool on_efi_stack (unsigned long sp) +{ +return sp >=EFI_STACK_BASE && sp < (EFI_STACK_BASE +EFI_STACK_SIZE ); +} + +int __init efi_allocate_runtime_regions (struct mm_struct *mm) +{ + static u8 stack[EFI_STACK_SIZE] __page_aligned_bss; + +/* map the stack */ +create_pgd_mapping (mm,__pa_symbol (stack), +EFI_STACK_BASE ,EFI_STACK_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL ) |PTE_NG ), + false); + +/* map the runtime wrapper pivot function */ +create_pgd_mapping (mm,__pa_symbol (__efi_rt_asm_wrapper), +EFI_CODE_BASE ,EFI_CODE_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL_ROX ) |PTE_NG ), + false); + +return 0 ; +}
arch/arm64/kernel/entry.S +1 -0
@@ -204,6 +204,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 + tbz x21, #63, 1f // skip if TTBR0 covers the stack mrs x21, ttbr0_el1 tst x21, #TTBR_ASID_MASK // Check for the reserved ASID orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR
drivers/firmware/efi/arm-runtime.c +2 -0
@@ -107,6 +107,8 @@ static bool __init efi_virtmap_init(void)if (efi_memattr_apply_permissions (&efi_mm, efi_set_mapping_permissions))return false; +efi_allocate_runtime_regions (&efi_mm); +return true; }
Signature · 1 line
2.11.0
PATCH 2/4 efi/arm64: map the stack and entry wrapper into the UEFI page tables
Hi Ard,
Quoted 6 lines · Ard Biesheuvel
As a preparatory step towards unmapping the kernel entirely while executing UEFI runtime services, move the stack and the entry wrapper routine mappings into the EFI page tables. Also, create a vector table that overrides the main one while executing in the firmware so we will be able to remap/unmap the kernel while taking interrupts.
[...]
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 67
Quoted 6 lines
arch/arm64/kernel/efi-rt-wrapper.S +6
-0+ .macro ventry + .align 7 +.Lv\@ : stp x29, x30, [sp, #-16]! // preserve x29 and x30 + mrs x29, elr_el1 // preserve ELR + adr x30, .Lret // take return address + msr elr_el1, x30 // set ELR to return address
Oh man, are you really doing two ERETs for a single exception, or am I missing something?
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 75
Quoted 8 lines
arch/arm64/kernel/efi-rt-wrapper.S +8
-0+ ldr x30, 2b // take address of 'vectors' + msr vbar_el1, x30 // set VBAR to 'vectors' + isb + add x30, x30, #.Lv\@ - __efi_rt_vectors + br x30 + .endm + +.Lret: msr elr_el1, x29
If you take an IRQ here, aren't you toast?
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 86
Quoted 11 lines
arch/arm64/kernel/efi-rt-wrapper.S +11
-0+ adr x30, __efi_rt_vectors + msr vbar_el1, x30 + isb + ldp x29, x30, [sp], #16 + eret + + .align 11 +__efi_rt_vectors: + .rept 8 + ventry + .endr
Have you thought about SDEI at all? I can't see any code here to handle that.
Quoted 11 lines
arch/arm64/kernel/efi-rt-wrapper.S +4
-0+ /* + * EFI runtime services never drop to EL0, so the + * remaining vector table entries are not needed. + */arch/arm64/kernel/efi.c +0
-0@@ -130,3 +130,27 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)pr_err_ratelimited (FW_BUG "register x18 corrupted by EFI %s\n" , f);return s;
arch/arm64/kernel/efi.c asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) · line 141
Quoted 10 lines
arch/arm64/kernel/efi.c +9
-0} + +bool on_efi_stack (unsigned long sp) +{ +return sp >=EFI_STACK_BASE && sp < (EFI_STACK_BASE +EFI_STACK_SIZE ); +} + +int __init efi_allocate_runtime_regions (struct mm_struct *mm) +{ + static u8 stack[EFI_STACK_SIZE] __page_aligned_bss;
Probably just me, but the use of a function static variable in a function annotated with __init just makes me feel uneasy. Could we move it out into wider scope?
Quoted 20 lines
arch/arm64/kernel/efi.c +15
-0+ +/* map the stack */ +create_pgd_mapping (mm,__pa_symbol (stack), +EFI_STACK_BASE ,EFI_STACK_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL ) |PTE_NG ), + false); + +/* map the runtime wrapper pivot function */ +create_pgd_mapping (mm,__pa_symbol (__efi_rt_asm_wrapper), +EFI_CODE_BASE ,EFI_CODE_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL_ROX ) |PTE_NG ), + false); + +return 0 ; +}arch/arm64/kernel/entry.S +0
-0
arch/arm64/kernel/entry.S alternative_if ARM64_HAS_PAN · line 207
Quoted 4 lines
arch/arm64/kernel/entry.S +1
-0@@ -204,6 +204,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif .if \el != 0 + tbz x21, #63, 1f // skip if TTBR0 covers the stack
So this is really a "detect EFI" check, right? Maybe comment it as such. Also, probably want to check bit 55 just in case tagging ever takes off.
Will
PATCH 2/4 efi/arm64: map the stack and entry wrapper into the UEFI page tables
Quoted 19 lines · Will Deacon
Hi Ard,
Quoted 6 lines · Ard Biesheuvel
As a preparatory step towards unmapping the kernel entirely while executing UEFI runtime services, move the stack and the entry wrapper routine mappings into the EFI page tables. Also, create a vector table that overrides the main one while executing in the firmware so we will be able to remap/unmap the kernel while taking interrupts.
[...]
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 67
Quoted 6 lines
arch/arm64/kernel/efi-rt-wrapper.S +6
-0+ .macro ventry + .align 7 +.Lv\@ : stp x29, x30, [sp, #-16]! // preserve x29 and x30 + mrs x29, elr_el1 // preserve ELR + adr x30, .Lret // take return address + msr elr_el1, x30 // set ELR to return address
Quoted 3 lines
Oh man, are you really doing two ERETs for a single exception, or am I missing something?
Yes. I told you it was poetry.
Quoted 11 lines
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 75
Quoted 8 lines
arch/arm64/kernel/efi-rt-wrapper.S +8
-0+ ldr x30, 2b // take address of 'vectors' + msr vbar_el1, x30 // set VBAR to 'vectors' + isb + add x30, x30, #.Lv\@ - __efi_rt_vectors + br x30 + .endm + +.Lret: msr elr_el1, x29If you take an IRQ here, aren't you toast?
Yep. So we need to switch this with setting the VBAR below then.
Quoted 6 lines
Quoted 6 lines
arch/arm64/kernel/efi-rt-wrapper.S +6
-0+ adr x30, __efi_rt_vectors + msr vbar_el1, x30 + isb + ldp x29, x30, [sp], #16 + eret +
Quoted 9 lines
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 86
Quoted 5 lines
arch/arm64/kernel/efi-rt-wrapper.S +5
-0+ .align 11 +__efi_rt_vectors: + .rept 8 + ventry + .endrHave you thought about SDEI at all? I can't see any code here to handle that.
Nope
Quoted 17 lines
Quoted 17 lines
arch/arm64/kernel/efi-rt-wrapper.S +4
-0+ /* + * EFI runtime services never drop to EL0, so the + * remaining vector table entries are not needed. + */arch/arm64/kernel/efi.c +5
-0@@ -130,3 +130,27 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)pr_err_ratelimited (FW_BUG "register x18 corrupted by EFI %s\n" , f);return s; } + +bool on_efi_stack (unsigned long sp) +{ +return sp >=EFI_STACK_BASE && sp < (EFI_STACK_BASE +EFI_STACK_SIZE ); +}
Quoted 9 lines
arch/arm64/kernel/efi.c asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) · line 141
Quoted 4 lines
arch/arm64/kernel/efi.c +4
-0+ +int __init efi_allocate_runtime_regions (struct mm_struct *mm) +{ + static u8 stack[EFI_STACK_SIZE] __page_aligned_bss;Probably just me, but the use of a function static variable in a function annotated with
__initjust makes me feel uneasy. Could we move it out into wider scope?
Should be fine, but I don't mind moving it out.
Quoted 22 lines
Quoted 22 lines
arch/arm64/kernel/efi.c +15
-0+ +/* map the stack */ +create_pgd_mapping (mm,__pa_symbol (stack), +EFI_STACK_BASE ,EFI_STACK_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL ) |PTE_NG ), + false); + +/* map the runtime wrapper pivot function */ +create_pgd_mapping (mm,__pa_symbol (__efi_rt_asm_wrapper), +EFI_CODE_BASE ,EFI_CODE_SIZE , +__pgprot (pgprot_val (PAGE_KERNEL_ROX ) |PTE_NG ), + false); + +return 0 ; +}arch/arm64/kernel/entry.S +0
-0@@ -204,6 +204,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif
Quoted 6 lines
arch/arm64/kernel/entry.S alternative_if ARM64_HAS_PAN · line 207
Quoted 2 lines
arch/arm64/kernel/entry.S +1
-0.if \el != 0 + tbz x21, #63, 1f // skip if TTBR0 covers the stackSo this is really a "detect EFI" check, right? Maybe comment it as such. Also, probably want to check bit 55 just in case tagging ever takes off.
Right. So just bit #55 should be sufficient then, right?
PATCH 2/4 efi/arm64: map the stack and entry wrapper into the UEFI page tables
Quoted 18 lines · Ard Biesheuvel
Quoted 17 lines · Will Deacon
Quoted 6 lines · Ard Biesheuvel
As a preparatory step towards unmapping the kernel entirely while executing UEFI runtime services, move the stack and the entry wrapper routine mappings into the EFI page tables. Also, create a vector table that overrides the main one while executing in the firmware so we will be able to remap/unmap the kernel while taking interrupts.
[...]
Quoted 6 lines
arch/arm64/kernel/efi-rt-wrapper.S +6
-0+ .macro ventry + .align 7 +.Lv\@ : stp x29, x30, [sp, #-16]! // preserve x29 and x30 + mrs x29, elr_el1 // preserve ELR + adr x30, .Lret // take return address + msr elr_el1, x30 // set ELR to return address
Quoted 5 lines
Quoted 3 lines
Oh man, are you really doing two ERETs for a single exception, or am I missing something?
Yes. I told you it was poetry.
We should organise a recital.
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
arch/arm64/kernel/efi-rt-wrapper.S +8
-0+ ldr x30, 2b // take address of 'vectors' + msr vbar_el1, x30 // set VBAR to 'vectors' + isb + add x30, x30, #.Lv\@ - __efi_rt_vectors + br x30 + .endm + +.Lret: msr elr_el1, x29
Quoted 4 lines
Quoted 2 lines
If you take an IRQ here, aren't you toast?
Yep. So we need to switch this with setting the VBAR below then.
Hmm, but the ELR will still be clobbered by an IRQ, so I don't see how you can make this safe unless you hack SPSR before entering the kernel vectors on the entry side.
Quoted 10 lines
Quoted 8 lines
arch/arm64/kernel/efi-rt-wrapper.S ENTRY(__efi_rt_asm_wrapper) · line 86
Quoted 4 lines
arch/arm64/kernel/efi-rt-wrapper.S +4
-0+__efi_rt_vectors: + .rept 8 + ventry + .endrHave you thought about SDEI at all? I can't see any code here to handle that.
Nope
Add JamesM to cc ;)
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
arch/arm64/kernel/entry.S +0
-0@@ -204,6 +204,7 @@ alternative_if ARM64_HAS_PAN alternative_else_nop_endif
Quoted 8 lines
Quoted 6 lines
arch/arm64/kernel/entry.S alternative_if ARM64_HAS_PAN · line 207
Quoted 2 lines
arch/arm64/kernel/entry.S +1
-0.if \el != 0 + tbz x21, #63, 1f // skip if TTBR0 covers the stackSo this is really a "detect EFI" check, right? Maybe comment it as such. Also, probably want to check bit 55 just in case tagging ever takes off.
Right. So just bit #55 should be sufficient then, right?
Yes, I think so.
Will
PATCH 3/4 efi/arm64: marshall runtime services arguments via buffer in TTBR0
In reply to Ard BiesheuvelIf we want to unmap the kernel while executing UEFI runtime services, we need to ensure that all arguments passed to those services are valid during that time, and this includes pointer arguments to string buffers and other data. So we have to do the rather tedious job of replacing each generic runtime wrapper with one that takes the above into account.
Fortunately, UEFI runtime services are not reentrant, so we can create a static buffer with fields for each runtime service, and a separate buffer for get/set_variable. This does limit variable names to 1024 characters and the variables themselves to 64 KB, but this should not be a limitation in practice.
Note that capsule update is omitted for now - this will be addressed in a subsequent patch.
--- arch/arm64/Kconfig | 1 - arch/arm64/include/asm/efi.h | 5 + arch/arm64/kernel/efi.c | 475 +++++++++++++++++++- 3 files changed, 479 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +0 -1
@@ -1196,7 +1196,6 @@ config EFI select LIBFDT select UCS2_STRING select EFI_PARAMS_FROM_FDT - select EFI_RUNTIME_WRAPPERS select EFI_STUB select EFI_ARMSTUB default y
arch/arm64/include/asm/efi.h +5 -0
@@ -165,4 +165,9 @@ int __init efi_allocate_runtime_regions(struct mm_struct *mm);#define EFI_CODE_BASE 0x200000#define EFI_CODE_SIZE PAGE_SIZE +#define EFI_VARBUFFER_BASE 0x210000 +#define EFI_VARBUFFER_SIZE SZ_64K + +#define EFI_DATA_BASE 0x220000 +#endif /* _ASM_EFI_H */
arch/arm64/kernel/efi.c +474 -1
494 lines not on this page · raw message
Signature · 1 line
2.11.0
PATCH 4/4 efi/arm64: unmap the kernel while executing UEFI services
In reply to Ard BiesheuvelNow that all UEFI runtime service wrappers ensure that byref arguments are moved into the UEFI marshalling buffer (which is not part of the kernel mapping), we can proceed and unmap the kernel while UEFI runtime service calls are in progress.
This is done by setting the EPD1 bit and flushing the TLB of the local CPU. This makes it independent of KPTI or whether non-global mappings are being used.
--- arch/arm64/kernel/efi-rt-wrapper.S | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+)
arch/arm64/kernel/efi-rt-wrapper.S +22 -0
@@ -9,6 +9,24 @@ #include <linux/linkage.h> #include <asm/efi.h> + .macro sepd1, reg + mrs \reg, tcr_el1 // read Translation Control Reg + orr \reg, \reg, #1 << 23 // set EPD1 bit + msr tcr_el1, \reg // write back TCR + isb + tlbi vmalle1 + dsb nsh + .endm + + .macro cepd1, reg + mrs \reg, tcr_el1 // read Translation Control Reg + bic \reg, \reg, #1 << 23 // clear EPD1 bit + msr tcr_el1, \reg // write back TCR + isb + tlbi vmalle1 + dsb nsh + .endm + .section ".rodata", "a" .align PAGE_SHIFT ENTRY(__efi_rt_asm_wrapper)
@@ -27,6 +45,7 @@ ENTRY(__efi_rt_asm_wrapper) adr x1, __efi_rt_vectors msr vbar_el1, x1 isb + sepd1 x1 /* * We are lucky enough that no EFI runtime services take more than
@@ -46,6 +65,7 @@ ENTRY(__efi_rt_asm_wrapper) ldr x1, 2f msr vbar_el1, x1 isb + cepd1 x1 ldp x1, x2, [sp, #16] cmp x2, x18
@@ -63,6 +83,7 @@ ENDPROC(__efi_rt_asm_wrapper) .align 7 .Lv\@ : stp x29, x30, [sp, #-16]! // preserve x29 and x30 mrs x29, elr_el1 // preserve ELR + cepd1 x30 adr x30, .Lret // take return address msr elr_el1, x30 // set ELR to return address ldr x30, 2b // take address of 'vectors'
@@ -76,6 +97,7 @@ ENDPROC(__efi_rt_asm_wrapper) adr x30, __efi_rt_vectors msr vbar_el1, x30 isb + sepd1 x30 ldp x29, x30, [sp], #16 eret
Signature · 1 line
2.11.0
PATCH 4/4 efi/arm64: unmap the kernel while executing UEFI services
Quoted 8 lines · Ard Biesheuvel
Now that all UEFI runtime service wrappers ensure that byref arguments are moved into the UEFI marshalling buffer (which is not part of the kernel mapping), we can proceed and unmap the kernel while UEFI runtime service calls are in progress.
This is done by setting the EPD1 bit and flushing the TLB of the local CPU. This makes it independent of KPTI or whether non-global mappings are being used.
One snag with this is that it will break SPE, so I'd prefer this behaviour to be predicated on kpti so that the arm64_kernel_unmapped_at_el0() check in drivers/perf/arm_spe_pmu.c remains valid.
Will
PATCH 4/4 efi/arm64: unmap the kernel while executing UEFI services
Quoted 6 lines · Will Deacon
Quoted 5 lines · Ard Biesheuvel
Now that all UEFI runtime service wrappers ensure that byref arguments are moved into the UEFI marshalling buffer (which is not part of the kernel mapping), we can proceed and unmap the kernel while UEFI runtime service calls are in progress.
Quoted 8 lines
Quoted 3 lines
This is done by setting the EPD1 bit and flushing the TLB of the local CPU. This makes it independent of KPTI or whether non-global mappings are being used.
One snag with this is that it will break SPE, so I'd prefer this behaviour to be predicated on kpti so that the
arm64_kernel_unmapped_at_el0()check indrivers/perf/arm_spe_pmu.cremains valid.
The problem with that is that they serve two different purposes: kpti protects against meltdown, this protects against Spectre variant 1.
PATCH 4/4 efi/arm64: unmap the kernel while executing UEFI services
Quoted 11 lines · Ard Biesheuvel
Quoted 10 lines · Will Deacon
Quoted 8 lines · Ard Biesheuvel
Now that all UEFI runtime service wrappers ensure that byref arguments are moved into the UEFI marshalling buffer (which is not part of the kernel mapping), we can proceed and unmap the kernel while UEFI runtime service calls are in progress.
This is done by setting the EPD1 bit and flushing the TLB of the local CPU. This makes it independent of KPTI or whether non-global mappings are being used.
Quoted 7 lines
Quoted 4 lines
One snag with this is that it will break SPE, so I'd prefer this behaviour to be predicated on kpti so that the
arm64_kernel_unmapped_at_el0()check indrivers/perf/arm_spe_pmu.cremains valid.The problem with that is that they serve two different purposes: kpti protects against meltdown, this protects against Spectre variant 1.
Fair enough, but we should do something because it renders SPE unusable and it can be a really handy profiling feature. Having the new EFI behaviour optional in some way would be my preference.
Will
cover efi/arm64: unmap the kernel during runtime service calls
In reply to Ard BiesheuvelQuoted 6 lines · Ard Biesheuvel
Note that capsule update has been omitted. This is a bit involved, and I'd rather get some feedback before burning too many cycles on that. All other services should be functional, with the caveat that EFI variable names are now limited to 1024 [wide] characters, and UEFI variables themselves to 64 KB.
I'm curious, why these limitations?
We don't have any concern about the name length limitation at this time, but the variable size one is concerning.
We have a feature which creates a variable greater than 64KB that we want to utilize from Linux. The size limitation would seem to prevent that.
Signature · 5 lines
Jeffrey Hugo Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
cover efi/arm64: unmap the kernel during runtime service calls
Quoted 11 lines · Jeffrey Hugo
Quoted 6 lines · Ard Biesheuvel
Note that capsule update has been omitted. This is a bit involved, and I'd rather get some feedback before burning too many cycles on that. All other services should be functional, with the caveat that EFI variable names are now limited to 1024 [wide] characters, and UEFI variables themselves to 64 KB.
I'm curious, why these limitations?
Because mapping and unmapping things on the fly in the UEFI page tables is a bit complicated, although I need to do something of the kind for capsules anyway, so once I solve that, we can use the same approach for variables.
Quoted 6 lines
We don't have any concern about the name length limitation at this time, but the variable size one is concerning.
We have a feature which creates a variable greater than 64KB that we want to utilize from Linux. The size limitation would seem to prevent that.
Well, the limit itself is rather arbitrary, and putting >64 KB into the fault tolerant, write ahead logging NOR flash seems mildly inappropriate to me. But you are right, the spec currently has no such limitations, so ideally, nor should the OS.