Unverified Commit ec9df4f5 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!335 efi: fix crash due to EFI runtime service page faults

Merge Pull Request from: @ch-cityhunter 
 
Fix bugzilla: https://gitee.com/openeuler/kernel/issues/I67RIP

For both arm64 and x86, handle and recovery from page faults due to
EFI runtime service, and disable subsequent invoking, we can avoid
crash the whole system when running on buggy EFI firmware, and get
log like this:

kernel: [Firmware Bug]: Unable to handle paging request in EFI runtime service
kernel: CPU: 54 PID: 8 Comm: kworker/u256:0 Kdump: loaded Tainted: G          IOE     4.19.90-2112.8.0.0131.oe1.aarch64.debug #66
kernel: Hardware name: O.D.M     FT-2500 Platform/T1DMFT-E4 , BIOS KL4.26.ODM.S.032.210904.R 09/04/21 13:28:40
kernel: Workqueue: efi_rts_wq efi_call_rts
kernel: Call trace:
kernel:  dump_backtrace+0x0/0x170
kernel:  show_stack+0x24/0x30
kernel:  dump_stack+0xa4/0xe8
kernel:  efi_runtime_fixup_exception+0x74/0x8c
kernel:  __do_kernel_fault+0x8c/0x150
kernel:  do_page_fault+0x78/0x4c8
kernel:  do_translation_fault+0xa8/0xbc
kernel:  do_mem_abort+0x50/0xe0
kernel:  el1_da+0x20/0x94
kernel:  0x213f0c24
kernel:  0x213f0d64
kernel:  0x213f044c
kernel:  0x213f04b4
kernel:  0x213f0178
kernel:  0x212e0664
kernel:  __efi_rt_asm_wrapper+0x50/0x6c
kernel:  efi_call_rts+0x414/0x430
kernel:  process_one_work+0x1f8/0x490
kernel:  worker_thread+0x50/0x4b8
kernel:  kthread+0x134/0x138
kernel:  ret_from_fork+0x10/0x18
kernel: [Firmware Bug]: Synchronous exception occurred in EFI runtime service get_time()
kernel: rtc-efi rtc-efi: can't read time
kernel: efi: EFI Runtime Services are disabled!

Anders Roxell (1):
  efi: Fix build error due to enum collision between efi.h and ima.h

Ard Biesheuvel (3):
  arm64: efi: Execute runtime services from a dedicated stack
  arm64: efi: Recover from synchronous exceptions occurring in firmware
  efi: rt-wrapper: Add missing include

Ding Hui (1):
  efi: fix userspace infinite retry read efivars after EFI runtime
    services page fault

Pierre Gondois (1):
  arm64: efi: Make efi_rt_lock a raw_spinlock

Qian Cai (1):
  x86/efi: fix a -Wtype-limits compilation warning

Sai Praneeth (2):
  efi: Make efi_rts_work accessible to efi page fault handler
  efi/x86: Handle page faults occurring while running EFI runtime
    services

Sami Tolvanen (1):
  arm64: efi: Restore register x18 if it was corrupted

Waiman Long (1):
  efi: Fix debugobjects warning on 'efi_rts_work' 
 
Link:https://gitee.com/openeuler/kernel/pulls/335

 

Reviewed-by: default avatarWei Li <liwei391@huawei.com>
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parents 1c944d2d 213aa9a9
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -11,11 +11,20 @@
#include <asm/neon.h>
#include <asm/ptrace.h>
#include <asm/tlbflush.h>
#include <linux/efi.h>

#ifdef CONFIG_EFI
extern void efi_init(void);

bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg);
#else
#define efi_init()

static inline
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
{
	return false;
}
#endif

int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
@@ -25,6 +34,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
({									\
	efi_virtmap_load();						\
	__efi_fpsimd_begin();						\
	raw_spin_lock(&efi_rt_lock);					\
})

#define arch_efi_call_virt(p, f, args...)				\
@@ -36,10 +46,12 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);

#define arch_efi_call_virt_teardown()					\
({									\
	raw_spin_unlock(&efi_rt_lock);					\
	__efi_fpsimd_end();						\
	efi_virtmap_unload();						\
})

extern raw_spinlock_t efi_rt_lock;
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)
+46 −3
Original line number Diff line number Diff line
@@ -7,9 +7,10 @@
 */

#include <linux/linkage.h>
#include <asm/assembler.h>

ENTRY(__efi_rt_asm_wrapper)
	stp	x29, x30, [sp, #-32]!
	stp	x29, x30, [sp, #-112]!
	mov	x29, sp

	/*
@@ -19,6 +20,22 @@ ENTRY(__efi_rt_asm_wrapper)
	 */
	stp	x1, x18, [sp, #16]

	/*
	 * Preserve all callee saved registers and preserve the stack pointer
	 * value at the base of the EFI runtime stack so we can recover from
	 * synchronous exceptions occurring while executing the firmware
	 * routines.
	 */
	stp	x19, x20, [sp, #32]
	stp	x21, x22, [sp, #48]
	stp	x23, x24, [sp, #64]
	stp	x25, x26, [sp, #80]
	stp	x27, x28, [sp, #96]

	ldr_l	x16, efi_rt_stack_top
	mov	sp, x16
	stp	x18, x29, [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
@@ -32,10 +49,36 @@ ENTRY(__efi_rt_asm_wrapper)
	mov	x4, x6
	blr	x8

	mov	sp, x29
	ldp	x1, x2, [sp, #16]
	cmp	x2, x18
	ldp	x29, x30, [sp], #32
	ldp	x29, x30, [sp], #112
	b.ne	0f
	ret
0:	b	efi_handle_corrupted_x18	// tail call
0:
	/*
	 * With CONFIG_SHADOW_CALL_STACK, the kernel uses x18 to store a
	 * shadow stack pointer, which we need to restore before returning to
	 * potentially instrumented code. This is safe because the wrapper is
	 * called with preemption disabled and a separate shadow stack is used
	 * for interrupts.
	 */
#ifdef CONFIG_SHADOW_CALL_STACK
	ldr_l	x18, efi_rt_stack_top
	ldr	x18, [x18, #-16]
#endif

	b	efi_handle_corrupted_x18	// tail call
ENDPROC(__efi_rt_asm_wrapper)

ENTRY(__efi_rt_asm_recover)
	mov     sp, x30

	ldp     x19, x20, [sp, #32]
	ldp     x21, x22, [sp, #48]
	ldp     x23, x24, [sp, #64]
	ldp     x25, x26, [sp, #80]
	ldp     x27, x28, [sp, #96]
	ldp     x29, x30, [sp], #112
	ret
ENDPROC(__efi_rt_asm_recover)
+50 −0
Original line number Diff line number Diff line
@@ -132,3 +132,53 @@ 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;
}

DEFINE_RAW_SPINLOCK(efi_rt_lock);

asmlinkage u64 *efi_rt_stack_top __ro_after_init;

asmlinkage efi_status_t __efi_rt_asm_recover(void);

bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
{
	 /* Check whether the exception occurred while running the firmware */
	if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64)
		return false;

	pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);
	add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
	clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);

	regs->regs[0]	= EFI_ABORTED;
	regs->regs[30]	= efi_rt_stack_top[-1];
	regs->pc	= (u64)__efi_rt_asm_recover;

	if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK))
		regs->regs[18] = efi_rt_stack_top[-2];

	return true;
}

/* EFI requires 8 KiB of stack space for runtime services */
static_assert(THREAD_SIZE >= SZ_8K);

static int __init arm64_efi_rt_init(void)
{
	void *p;

	if (!efi_enabled(EFI_RUNTIME_SERVICES))
		return 0;

	p = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, GFP_KERNEL,
			   PAGE_KERNEL, 0,
			   NUMA_NO_NODE, &&l);
l:	if (!p) {
		pr_warn("Failed to allocate EFI runtime stack\n");
		clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
		return -ENOMEM;
	}

	efi_rt_stack_top = p + THREAD_SIZE;
	return 0;
}
core_initcall(arm64_efi_rt_init);
+4 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include <asm/bug.h>
#include <asm/cmpxchg.h>
#include <asm/cpufeature.h>
#include <asm/efi.h>
#include <asm/exception.h>
#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
@@ -332,6 +333,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
		msg = "paging request";
	}

	if (efi_runtime_fixup_exception(regs, msg))
		return;

	die_kernel_fault(msg, addr, esr, regs);
}

+1 −0
Original line number Diff line number Diff line
@@ -137,6 +137,7 @@ extern void __init efi_apply_memmap_quirks(void);
extern int __init efi_reuse_config(u64 tables, int nr_tables);
extern void efi_delete_dummy_variable(void);
extern void efi_switch_mm(struct mm_struct *mm);
extern void efi_recover_from_page_fault(unsigned long phys_addr);

struct efi_setup_data {
	u64 fw_vendor;
Loading