2019-06-04 08:11:33 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2014-04-16 01:59:30 +00:00
|
|
|
/*
|
|
|
|
* Extensible Firmware Interface
|
|
|
|
*
|
|
|
|
* Based on Extensible Firmware Interface Specification version 2.4
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013, 2014 Linaro Ltd.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/efi.h>
|
2015-11-30 12:28:18 +00:00
|
|
|
#include <linux/init.h>
|
2014-04-16 01:59:30 +00:00
|
|
|
|
|
|
|
#include <asm/efi.h>
|
2022-10-28 14:39:14 +00:00
|
|
|
#include <asm/stacktrace.h>
|
2014-10-04 15:46:43 +00:00
|
|
|
|
2022-11-06 14:53:54 +00:00
|
|
|
static bool region_is_misaligned(const efi_memory_desc_t *md)
|
|
|
|
{
|
|
|
|
if (PAGE_SIZE == EFI_PAGE_SIZE)
|
|
|
|
return false;
|
|
|
|
return !PAGE_ALIGNED(md->phys_addr) ||
|
|
|
|
!PAGE_ALIGNED(md->num_pages << EFI_PAGE_SHIFT);
|
|
|
|
}
|
|
|
|
|
2016-04-25 20:06:43 +00:00
|
|
|
/*
|
|
|
|
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
|
|
|
|
* executable, everything else can be mapped with the XN bits
|
|
|
|
* set. Also take the new (optional) RO/XP bits into account.
|
|
|
|
*/
|
|
|
|
static __init pteval_t create_mapping_protection(efi_memory_desc_t *md)
|
2015-11-30 12:28:19 +00:00
|
|
|
{
|
2016-04-25 20:06:43 +00:00
|
|
|
u64 attr = md->attribute;
|
|
|
|
u32 type = md->type;
|
2015-11-30 12:28:19 +00:00
|
|
|
|
2016-04-25 20:06:43 +00:00
|
|
|
if (type == EFI_MEMORY_MAPPED_IO)
|
|
|
|
return PROT_DEVICE_nGnRE;
|
|
|
|
|
2022-11-06 14:53:54 +00:00
|
|
|
if (region_is_misaligned(md)) {
|
|
|
|
static bool __initdata code_is_misaligned;
|
|
|
|
|
2016-04-25 20:06:43 +00:00
|
|
|
/*
|
2022-11-06 14:53:54 +00:00
|
|
|
* Regions that are not aligned to the OS page size cannot be
|
|
|
|
* mapped with strict permissions, as those might interfere
|
|
|
|
* with the permissions that are needed by the adjacent
|
|
|
|
* region's mapping. However, if we haven't encountered any
|
|
|
|
* misaligned runtime code regions so far, we can safely use
|
|
|
|
* non-executable permissions for non-code regions.
|
2016-04-25 20:06:43 +00:00
|
|
|
*/
|
2022-11-06 14:53:54 +00:00
|
|
|
code_is_misaligned |= (type == EFI_RUNTIME_SERVICES_CODE);
|
|
|
|
|
|
|
|
return code_is_misaligned ? pgprot_val(PAGE_KERNEL_EXEC)
|
|
|
|
: pgprot_val(PAGE_KERNEL);
|
|
|
|
}
|
2016-04-25 20:06:43 +00:00
|
|
|
|
|
|
|
/* R-- */
|
|
|
|
if ((attr & (EFI_MEMORY_XP | EFI_MEMORY_RO)) ==
|
|
|
|
(EFI_MEMORY_XP | EFI_MEMORY_RO))
|
|
|
|
return pgprot_val(PAGE_KERNEL_RO);
|
|
|
|
|
|
|
|
/* R-X */
|
|
|
|
if (attr & EFI_MEMORY_RO)
|
|
|
|
return pgprot_val(PAGE_KERNEL_ROX);
|
|
|
|
|
|
|
|
/* RW- */
|
2018-01-02 18:10:39 +00:00
|
|
|
if (((attr & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP)) ==
|
|
|
|
EFI_MEMORY_XP) ||
|
|
|
|
type != EFI_RUNTIME_SERVICES_CODE)
|
2016-04-25 20:06:43 +00:00
|
|
|
return pgprot_val(PAGE_KERNEL);
|
|
|
|
|
|
|
|
/* RWX */
|
|
|
|
return pgprot_val(PAGE_KERNEL_EXEC);
|
|
|
|
}
|
|
|
|
|
2016-04-25 20:06:52 +00:00
|
|
|
/* we will fill this structure from the stub, so don't put it in .bss */
|
2020-10-22 02:36:07 +00:00
|
|
|
struct screen_info screen_info __section(".data");
|
2021-08-04 15:52:38 +00:00
|
|
|
EXPORT_SYMBOL(screen_info);
|
2016-04-25 20:06:52 +00:00
|
|
|
|
2016-04-25 20:06:43 +00:00
|
|
|
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
|
|
|
{
|
|
|
|
pteval_t prot_val = create_mapping_protection(md);
|
2016-10-21 11:22:57 +00:00
|
|
|
bool page_mappings_only = (md->type == EFI_RUNTIME_SERVICES_CODE ||
|
|
|
|
md->type == EFI_RUNTIME_SERVICES_DATA);
|
2015-11-30 12:28:19 +00:00
|
|
|
|
2022-11-06 14:53:54 +00:00
|
|
|
/*
|
|
|
|
* If this region is not aligned to the page size used by the OS, the
|
|
|
|
* mapping will be rounded outwards, and may end up sharing a page
|
|
|
|
* frame with an adjacent runtime memory region. Given that the page
|
|
|
|
* table descriptor covering the shared page will be rewritten when the
|
|
|
|
* adjacent region gets mapped, we must avoid block mappings here so we
|
|
|
|
* don't have to worry about splitting them when that happens.
|
|
|
|
*/
|
|
|
|
if (region_is_misaligned(md))
|
2016-10-21 11:22:57 +00:00
|
|
|
page_mappings_only = true;
|
2016-06-29 12:51:28 +00:00
|
|
|
|
2015-11-30 12:28:19 +00:00
|
|
|
create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
|
|
|
|
md->num_pages << EFI_PAGE_SHIFT,
|
2016-10-21 11:22:57 +00:00
|
|
|
__pgprot(prot_val | PTE_NG), page_mappings_only);
|
2015-11-30 12:28:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-02-01 08:50:07 +00:00
|
|
|
struct set_perm_data {
|
|
|
|
const efi_memory_desc_t *md;
|
|
|
|
bool has_bti;
|
|
|
|
};
|
|
|
|
|
2019-07-12 03:58:43 +00:00
|
|
|
static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data)
|
2016-06-29 12:51:27 +00:00
|
|
|
{
|
2023-02-01 08:50:07 +00:00
|
|
|
struct set_perm_data *spd = data;
|
|
|
|
const efi_memory_desc_t *md = spd->md;
|
2018-02-15 11:14:56 +00:00
|
|
|
pte_t pte = READ_ONCE(*ptep);
|
2016-06-29 12:51:27 +00:00
|
|
|
|
|
|
|
if (md->attribute & EFI_MEMORY_RO)
|
|
|
|
pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
|
|
|
|
if (md->attribute & EFI_MEMORY_XP)
|
|
|
|
pte = set_pte_bit(pte, __pgprot(PTE_PXN));
|
2023-02-01 08:50:07 +00:00
|
|
|
else if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) &&
|
|
|
|
system_supports_bti() && spd->has_bti)
|
|
|
|
pte = set_pte_bit(pte, __pgprot(PTE_GP));
|
2016-06-29 12:51:27 +00:00
|
|
|
set_pte(ptep, pte);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __init efi_set_mapping_permissions(struct mm_struct *mm,
|
2023-02-01 08:48:12 +00:00
|
|
|
efi_memory_desc_t *md,
|
|
|
|
bool has_bti)
|
2016-06-29 12:51:27 +00:00
|
|
|
{
|
2023-02-01 08:50:07 +00:00
|
|
|
struct set_perm_data data = { md, has_bti };
|
|
|
|
|
2016-06-29 12:51:27 +00:00
|
|
|
BUG_ON(md->type != EFI_RUNTIME_SERVICES_CODE &&
|
|
|
|
md->type != EFI_RUNTIME_SERVICES_DATA);
|
|
|
|
|
2022-11-06 14:53:54 +00:00
|
|
|
if (region_is_misaligned(md))
|
|
|
|
return 0;
|
|
|
|
|
2016-06-29 12:51:27 +00:00
|
|
|
/*
|
|
|
|
* Calling apply_to_page_range() is only safe on regions that are
|
|
|
|
* guaranteed to be mapped down to pages. Since we are only called
|
|
|
|
* for regions that have been mapped using efi_create_mapping() above
|
|
|
|
* (and this is checked by the generic Memory Attributes table parsing
|
|
|
|
* routines), there is no need to check that again here.
|
|
|
|
*/
|
|
|
|
return apply_to_page_range(mm, md->virt_addr,
|
|
|
|
md->num_pages << EFI_PAGE_SHIFT,
|
2023-02-01 08:50:07 +00:00
|
|
|
set_permissions, &data);
|
2016-06-29 12:51:27 +00:00
|
|
|
}
|
|
|
|
|
2015-03-06 14:49:24 +00:00
|
|
|
/*
|
|
|
|
* UpdateCapsule() depends on the system being shutdown via
|
|
|
|
* ResetSystem().
|
|
|
|
*/
|
|
|
|
bool efi_poweroff_required(void)
|
|
|
|
{
|
|
|
|
return efi_enabled(EFI_RUNTIME_SERVICES);
|
|
|
|
}
|
2018-03-08 08:00:13 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2022-12-05 10:31:25 +00:00
|
|
|
|
arm64: efi: Make efi_rt_lock a raw_spinlock
Running a rt-kernel base on 6.2.0-rc3-rt1 on an Ampere Altra outputs
the following:
BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:46
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 9, name: kworker/u320:0
preempt_count: 2, expected: 0
RCU nest depth: 0, expected: 0
3 locks held by kworker/u320:0/9:
#0: ffff3fff8c27d128 ((wq_completion)efi_rts_wq){+.+.}-{0:0}, at: process_one_work (./include/linux/atomic/atomic-long.h:41)
#1: ffff80000861bdd0 ((work_completion)(&efi_rts_work.work)){+.+.}-{0:0}, at: process_one_work (./include/linux/atomic/atomic-long.h:41)
#2: ffffdf7e1ed3e460 (efi_rt_lock){+.+.}-{3:3}, at: efi_call_rts (drivers/firmware/efi/runtime-wrappers.c:101)
Preemption disabled at:
efi_virtmap_load (./arch/arm64/include/asm/mmu_context.h:248)
CPU: 0 PID: 9 Comm: kworker/u320:0 Tainted: G W 6.2.0-rc3-rt1
Hardware name: WIWYNN Mt.Jade Server System B81.03001.0005/Mt.Jade Motherboard, BIOS 1.08.20220218 (SCP: 1.08.20220218) 2022/02/18
Workqueue: efi_rts_wq efi_call_rts
Call trace:
dump_backtrace (arch/arm64/kernel/stacktrace.c:158)
show_stack (arch/arm64/kernel/stacktrace.c:165)
dump_stack_lvl (lib/dump_stack.c:107 (discriminator 4))
dump_stack (lib/dump_stack.c:114)
__might_resched (kernel/sched/core.c:10134)
rt_spin_lock (kernel/locking/rtmutex.c:1769 (discriminator 4))
efi_call_rts (drivers/firmware/efi/runtime-wrappers.c:101)
[...]
This seems to come from commit ff7a167961d1 ("arm64: efi: Execute
runtime services from a dedicated stack") which adds a spinlock. This
spinlock is taken through:
efi_call_rts()
\-efi_call_virt()
\-efi_call_virt_pointer()
\-arch_efi_call_virt_setup()
Make 'efi_rt_lock' a raw_spinlock to avoid being preempted.
[ardb: The EFI runtime services are called with a different set of
translation tables, and are permitted to use the SIMD registers.
The context switch code preserves/restores neither, and so EFI
calls must be made with preemption disabled, rather than only
disabling migration.]
Fixes: ff7a167961d1 ("arm64: efi: Execute runtime services from a dedicated stack")
Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Cc: <stable@vger.kernel.org> # v6.1+
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
2023-02-15 16:10:47 +00:00
|
|
|
DEFINE_RAW_SPINLOCK(efi_rt_lock);
|
2022-12-05 10:31:25 +00:00
|
|
|
|
|
|
|
asmlinkage u64 *efi_rt_stack_top __ro_after_init;
|
|
|
|
|
arm64: efi: Recover from synchronous exceptions occurring in firmware
Unlike x86, which has machinery to deal with page faults that occur
during the execution of EFI runtime services, arm64 has nothing like
that, and a synchronous exception raised by firmware code brings down
the whole system.
With more EFI based systems appearing that were not built to run Linux
(such as the Windows-on-ARM laptops based on Qualcomm SOCs), as well as
the introduction of PRM (platform specific firmware routines that are
callable just like EFI runtime services), we are more likely to run into
issues of this sort, and it is much more likely that we can identify and
work around such issues if they don't bring down the system entirely.
Since we already use a EFI runtime services call wrapper in assembler,
we can quite easily add some code that captures the execution state at
the point where the call is made, allowing us to revert to this state
and proceed execution if the call triggered a synchronous exception.
Given that the kernel and the firmware don't share any data structures
that could end up in an indeterminate state, we can happily continue
running, as long as we mark the EFI runtime services as unavailable from
that point on.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
2022-10-28 14:39:14 +00:00
|
|
|
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 */
|
2022-10-28 14:39:14 +00:00
|
|
|
if (!current_in_efi() || regs->pc >= TASK_SIZE_64)
|
arm64: efi: Recover from synchronous exceptions occurring in firmware
Unlike x86, which has machinery to deal with page faults that occur
during the execution of EFI runtime services, arm64 has nothing like
that, and a synchronous exception raised by firmware code brings down
the whole system.
With more EFI based systems appearing that were not built to run Linux
(such as the Windows-on-ARM laptops based on Qualcomm SOCs), as well as
the introduction of PRM (platform specific firmware routines that are
callable just like EFI runtime services), we are more likely to run into
issues of this sort, and it is much more likely that we can identify and
work around such issues if they don't bring down the system entirely.
Since we already use a EFI runtime services call wrapper in assembler,
we can quite easily add some code that captures the execution state at
the point where the call is made, allowing us to revert to this state
and proceed execution if the call triggered a synchronous exception.
Given that the kernel and the firmware don't share any data structures
that could end up in an indeterminate state, we can happily continue
running, as long as we mark the EFI runtime services as unavailable from
that point on.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
2022-10-28 14:39:14 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-12-05 10:31:25 +00:00
|
|
|
/* 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,
|
|
|
|
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);
|