mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'efi-fixes-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi
Pull EFI fixes from Ard Biesheuvel: - A pair of tweaks to the EFI random seed code so that externally provided version of this config table are handled more robustly - Another fix for the v6.0 EFI variable refactor that turned out to break Apple machines which don't provide QueryVariableInfo() - Add some guard rails to the EFI runtime service call wrapper so we can recover from synchronous exceptions caused by firmware * tag 'efi-fixes-for-v6.1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi: arm64: efi: Recover from synchronous exceptions occurring in firmware efi: efivars: Fix variable writes with unsupported query_variable_store() efi: random: Use 'ACPI reclaim' memory for random seed efi: random: reduce seed size to 32 bytes efi/tpm: Pass correct address to memblock_reserve
This commit is contained in:
@@ -14,8 +14,16 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_EFI
|
#ifdef CONFIG_EFI
|
||||||
extern void efi_init(void);
|
extern void efi_init(void);
|
||||||
|
|
||||||
|
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg);
|
||||||
#else
|
#else
|
||||||
#define efi_init()
|
#define efi_init()
|
||||||
|
|
||||||
|
static inline
|
||||||
|
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <linux/linkage.h>
|
#include <linux/linkage.h>
|
||||||
|
|
||||||
SYM_FUNC_START(__efi_rt_asm_wrapper)
|
SYM_FUNC_START(__efi_rt_asm_wrapper)
|
||||||
stp x29, x30, [sp, #-32]!
|
stp x29, x30, [sp, #-112]!
|
||||||
mov x29, sp
|
mov x29, sp
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -16,6 +16,20 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
|
|||||||
*/
|
*/
|
||||||
stp x1, x18, [sp, #16]
|
stp x1, x18, [sp, #16]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preserve all callee saved registers and record the stack pointer
|
||||||
|
* value in a per-CPU variable so we can recover from synchronous
|
||||||
|
* exceptions occurring while running 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]
|
||||||
|
|
||||||
|
adr_this_cpu x8, __efi_rt_asm_recover_sp, x9
|
||||||
|
str x29, [x8]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are lucky enough that no EFI runtime services take more than
|
* We are lucky enough that no EFI runtime services take more than
|
||||||
* 5 arguments, so all are passed in registers rather than via the
|
* 5 arguments, so all are passed in registers rather than via the
|
||||||
@@ -31,7 +45,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
|
|||||||
|
|
||||||
ldp x1, x2, [sp, #16]
|
ldp x1, x2, [sp, #16]
|
||||||
cmp x2, x18
|
cmp x2, x18
|
||||||
ldp x29, x30, [sp], #32
|
ldp x29, x30, [sp], #112
|
||||||
b.ne 0f
|
b.ne 0f
|
||||||
ret
|
ret
|
||||||
0:
|
0:
|
||||||
@@ -45,3 +59,18 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
|
|||||||
mov x18, x2
|
mov x18, x2
|
||||||
b efi_handle_corrupted_x18 // tail call
|
b efi_handle_corrupted_x18 // tail call
|
||||||
SYM_FUNC_END(__efi_rt_asm_wrapper)
|
SYM_FUNC_END(__efi_rt_asm_wrapper)
|
||||||
|
|
||||||
|
SYM_FUNC_START(__efi_rt_asm_recover)
|
||||||
|
ldr_this_cpu x8, __efi_rt_asm_recover_sp, x9
|
||||||
|
mov sp, x8
|
||||||
|
|
||||||
|
ldp x0, x18, [sp, #16]
|
||||||
|
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
|
||||||
|
|
||||||
|
b efi_handle_runtime_exception
|
||||||
|
SYM_FUNC_END(__efi_rt_asm_recover)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/percpu.h>
|
||||||
|
|
||||||
#include <asm/efi.h>
|
#include <asm/efi.h>
|
||||||
|
|
||||||
@@ -128,3 +129,28 @@ 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);
|
pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmlinkage DEFINE_PER_CPU(u64, __efi_rt_asm_recover_sp);
|
||||||
|
|
||||||
|
asmlinkage efi_status_t __efi_rt_asm_recover(void);
|
||||||
|
|
||||||
|
asmlinkage efi_status_t efi_handle_runtime_exception(const char *f)
|
||||||
|
{
|
||||||
|
pr_err(FW_BUG "Synchronous exception occurred in EFI runtime service %s()\n", f);
|
||||||
|
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||||
|
return EFI_ABORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
dump_stack();
|
||||||
|
|
||||||
|
regs->pc = (u64)__efi_rt_asm_recover;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <asm/bug.h>
|
#include <asm/bug.h>
|
||||||
#include <asm/cmpxchg.h>
|
#include <asm/cmpxchg.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
|
#include <asm/efi.h>
|
||||||
#include <asm/exception.h>
|
#include <asm/exception.h>
|
||||||
#include <asm/daifflags.h>
|
#include <asm/daifflags.h>
|
||||||
#include <asm/debug-monitors.h>
|
#include <asm/debug-monitors.h>
|
||||||
@@ -391,6 +392,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr,
|
|||||||
msg = "paging request";
|
msg = "paging request";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (efi_runtime_fixup_exception(regs, msg))
|
||||||
|
return;
|
||||||
|
|
||||||
die_kernel_fault(msg, addr, esr, regs);
|
die_kernel_fault(msg, addr, esr, regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -611,7 +611,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
|
|||||||
|
|
||||||
seed = early_memremap(efi_rng_seed, sizeof(*seed));
|
seed = early_memremap(efi_rng_seed, sizeof(*seed));
|
||||||
if (seed != NULL) {
|
if (seed != NULL) {
|
||||||
size = READ_ONCE(seed->size);
|
size = min(seed->size, EFI_RANDOM_SEED_SIZE);
|
||||||
early_memunmap(seed, sizeof(*seed));
|
early_memunmap(seed, sizeof(*seed));
|
||||||
} else {
|
} else {
|
||||||
pr_err("Could not map UEFI random seed!\n");
|
pr_err("Could not map UEFI random seed!\n");
|
||||||
|
|||||||
@@ -75,7 +75,12 @@ efi_status_t efi_random_get_seed(void)
|
|||||||
if (status != EFI_SUCCESS)
|
if (status != EFI_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
|
/*
|
||||||
|
* Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the
|
||||||
|
* allocation will survive a kexec reboot (although we refresh the seed
|
||||||
|
* beforehand)
|
||||||
|
*/
|
||||||
|
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
|
||||||
sizeof(*seed) + EFI_RANDOM_SEED_SIZE,
|
sizeof(*seed) + EFI_RANDOM_SEED_SIZE,
|
||||||
(void **)&seed);
|
(void **)&seed);
|
||||||
if (status != EFI_SUCCESS)
|
if (status != EFI_SUCCESS)
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ int __init efi_tpm_eventlog_init(void)
|
|||||||
goto out_calc;
|
goto out_calc;
|
||||||
}
|
}
|
||||||
|
|
||||||
memblock_reserve((unsigned long)final_tbl,
|
memblock_reserve(efi.tpm_final_log,
|
||||||
tbl_size + sizeof(*final_tbl));
|
tbl_size + sizeof(*final_tbl));
|
||||||
efi_tpm_final_log_size = tbl_size;
|
efi_tpm_final_log_size = tbl_size;
|
||||||
|
|
||||||
|
|||||||
@@ -21,29 +21,22 @@ static struct efivars *__efivars;
|
|||||||
|
|
||||||
static DEFINE_SEMAPHORE(efivars_lock);
|
static DEFINE_SEMAPHORE(efivars_lock);
|
||||||
|
|
||||||
static efi_status_t check_var_size(u32 attributes, unsigned long size)
|
static efi_status_t check_var_size(bool nonblocking, u32 attributes,
|
||||||
|
unsigned long size)
|
||||||
{
|
{
|
||||||
const struct efivar_operations *fops;
|
const struct efivar_operations *fops;
|
||||||
|
efi_status_t status;
|
||||||
|
|
||||||
fops = __efivars->ops;
|
fops = __efivars->ops;
|
||||||
|
|
||||||
if (!fops->query_variable_store)
|
if (!fops->query_variable_store)
|
||||||
|
status = EFI_UNSUPPORTED;
|
||||||
|
else
|
||||||
|
status = fops->query_variable_store(attributes, size,
|
||||||
|
nonblocking);
|
||||||
|
if (status == EFI_UNSUPPORTED)
|
||||||
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
||||||
|
return status;
|
||||||
return fops->query_variable_store(attributes, size, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size)
|
|
||||||
{
|
|
||||||
const struct efivar_operations *fops;
|
|
||||||
|
|
||||||
fops = __efivars->ops;
|
|
||||||
|
|
||||||
if (!fops->query_variable_store)
|
|
||||||
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
|
|
||||||
|
|
||||||
return fops->query_variable_store(attributes, size, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
|
EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
|
||||||
|
|
||||||
/*
|
|
||||||
* efivar_set_variable_blocking() - local helper function for set_variable
|
|
||||||
*
|
|
||||||
* Must be called with efivars_lock held.
|
|
||||||
*/
|
|
||||||
static efi_status_t
|
|
||||||
efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor,
|
|
||||||
u32 attr, unsigned long data_size, void *data)
|
|
||||||
{
|
|
||||||
efi_status_t status;
|
|
||||||
|
|
||||||
if (data_size > 0) {
|
|
||||||
status = check_var_size(attr, data_size +
|
|
||||||
ucs2_strsize(name, 1024));
|
|
||||||
if (status != EFI_SUCCESS)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return __efivars->ops->set_variable(name, vendor, attr, data_size, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* efivar_set_variable_locked() - set a variable identified by name/vendor
|
* efivar_set_variable_locked() - set a variable identified by name/vendor
|
||||||
*
|
*
|
||||||
@@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
|
|||||||
efi_set_variable_t *setvar;
|
efi_set_variable_t *setvar;
|
||||||
efi_status_t status;
|
efi_status_t status;
|
||||||
|
|
||||||
if (!nonblocking)
|
if (data_size > 0) {
|
||||||
return efivar_set_variable_blocking(name, vendor, attr,
|
status = check_var_size(nonblocking, attr,
|
||||||
data_size, data);
|
data_size + ucs2_strsize(name, 1024));
|
||||||
|
if (status != EFI_SUCCESS)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If no _nonblocking variant exists, the ordinary one
|
* If no _nonblocking variant exists, the ordinary one
|
||||||
* is assumed to be non-blocking.
|
* is assumed to be non-blocking.
|
||||||
*/
|
*/
|
||||||
setvar = __efivars->ops->set_variable_nonblocking ?:
|
setvar = __efivars->ops->set_variable_nonblocking;
|
||||||
__efivars->ops->set_variable;
|
if (!setvar || !nonblocking)
|
||||||
|
setvar = __efivars->ops->set_variable;
|
||||||
|
|
||||||
if (data_size > 0) {
|
|
||||||
status = check_var_size_nonblocking(attr, data_size +
|
|
||||||
ucs2_strsize(name, 1024));
|
|
||||||
if (status != EFI_SUCCESS)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return setvar(name, vendor, attr, data_size, data);
|
return setvar(name, vendor, attr, data_size, data);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
|
EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
|
||||||
@@ -264,7 +235,8 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
|
|||||||
if (efivar_lock())
|
if (efivar_lock())
|
||||||
return EFI_ABORTED;
|
return EFI_ABORTED;
|
||||||
|
|
||||||
status = efivar_set_variable_blocking(name, vendor, attr, data_size, data);
|
status = efivar_set_variable_locked(name, vendor, attr, data_size,
|
||||||
|
data, false);
|
||||||
efivar_unlock();
|
efivar_unlock();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1222,7 +1222,7 @@ efi_status_t efi_random_get_seed(void);
|
|||||||
arch_efi_call_virt_teardown(); \
|
arch_efi_call_virt_teardown(); \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define EFI_RANDOM_SEED_SIZE 64U
|
#define EFI_RANDOM_SEED_SIZE 32U // BLAKE2S_HASH_SIZE
|
||||||
|
|
||||||
struct linux_efi_random_seed {
|
struct linux_efi_random_seed {
|
||||||
u32 size;
|
u32 size;
|
||||||
|
|||||||
Reference in New Issue
Block a user