Commit 1caa2f18 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 10d916c8 23715a26
...@@ -14,8 +14,16 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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; ...@@ -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;
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, false);
}
static
efi_status_t check_var_size_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, true);
} }
/** /**
...@@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size, ...@@ -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, ...@@ -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, ...@@ -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); ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment