Skip to content

Commit 1caa2f1

Browse files
committed
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
2 parents 10d916c + 23715a2 commit 1caa2f1

File tree

9 files changed

+98
-54
lines changed

9 files changed

+98
-54
lines changed

arch/arm64/include/asm/efi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,16 @@
1414

1515
#ifdef CONFIG_EFI
1616
extern void efi_init(void);
17+
18+
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg);
1719
#else
1820
#define efi_init()
21+
22+
static inline
23+
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
24+
{
25+
return false;
26+
}
1927
#endif
2028

2129
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);

arch/arm64/kernel/efi-rt-wrapper.S

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <linux/linkage.h>
77

88
SYM_FUNC_START(__efi_rt_asm_wrapper)
9-
stp x29, x30, [sp, #-32]!
9+
stp x29, x30, [sp, #-112]!
1010
mov x29, sp
1111

1212
/*
@@ -16,6 +16,20 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
1616
*/
1717
stp x1, x18, [sp, #16]
1818

19+
/*
20+
* Preserve all callee saved registers and record the stack pointer
21+
* value in a per-CPU variable so we can recover from synchronous
22+
* exceptions occurring while running the firmware routines.
23+
*/
24+
stp x19, x20, [sp, #32]
25+
stp x21, x22, [sp, #48]
26+
stp x23, x24, [sp, #64]
27+
stp x25, x26, [sp, #80]
28+
stp x27, x28, [sp, #96]
29+
30+
adr_this_cpu x8, __efi_rt_asm_recover_sp, x9
31+
str x29, [x8]
32+
1933
/*
2034
* We are lucky enough that no EFI runtime services take more than
2135
* 5 arguments, so all are passed in registers rather than via the
@@ -31,7 +45,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
3145

3246
ldp x1, x2, [sp, #16]
3347
cmp x2, x18
34-
ldp x29, x30, [sp], #32
48+
ldp x29, x30, [sp], #112
3549
b.ne 0f
3650
ret
3751
0:
@@ -45,3 +59,18 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
4559
mov x18, x2
4660
b efi_handle_corrupted_x18 // tail call
4761
SYM_FUNC_END(__efi_rt_asm_wrapper)
62+
63+
SYM_FUNC_START(__efi_rt_asm_recover)
64+
ldr_this_cpu x8, __efi_rt_asm_recover_sp, x9
65+
mov sp, x8
66+
67+
ldp x0, x18, [sp, #16]
68+
ldp x19, x20, [sp, #32]
69+
ldp x21, x22, [sp, #48]
70+
ldp x23, x24, [sp, #64]
71+
ldp x25, x26, [sp, #80]
72+
ldp x27, x28, [sp, #96]
73+
ldp x29, x30, [sp], #112
74+
75+
b efi_handle_runtime_exception
76+
SYM_FUNC_END(__efi_rt_asm_recover)

arch/arm64/kernel/efi.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <linux/efi.h>
1111
#include <linux/init.h>
12+
#include <linux/percpu.h>
1213

1314
#include <asm/efi.h>
1415

@@ -128,3 +129,28 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)
128129
pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f);
129130
return s;
130131
}
132+
133+
asmlinkage DEFINE_PER_CPU(u64, __efi_rt_asm_recover_sp);
134+
135+
asmlinkage efi_status_t __efi_rt_asm_recover(void);
136+
137+
asmlinkage efi_status_t efi_handle_runtime_exception(const char *f)
138+
{
139+
pr_err(FW_BUG "Synchronous exception occurred in EFI runtime service %s()\n", f);
140+
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
141+
return EFI_ABORTED;
142+
}
143+
144+
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
145+
{
146+
/* Check whether the exception occurred while running the firmware */
147+
if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64)
148+
return false;
149+
150+
pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);
151+
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
152+
dump_stack();
153+
154+
regs->pc = (u64)__efi_rt_asm_recover;
155+
return true;
156+
}

arch/arm64/mm/fault.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <asm/bug.h>
3131
#include <asm/cmpxchg.h>
3232
#include <asm/cpufeature.h>
33+
#include <asm/efi.h>
3334
#include <asm/exception.h>
3435
#include <asm/daifflags.h>
3536
#include <asm/debug-monitors.h>
@@ -391,6 +392,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr,
391392
msg = "paging request";
392393
}
393394

395+
if (efi_runtime_fixup_exception(regs, msg))
396+
return;
397+
394398
die_kernel_fault(msg, addr, esr, regs);
395399
}
396400

drivers/firmware/efi/efi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
611611

612612
seed = early_memremap(efi_rng_seed, sizeof(*seed));
613613
if (seed != NULL) {
614-
size = READ_ONCE(seed->size);
614+
size = min(seed->size, EFI_RANDOM_SEED_SIZE);
615615
early_memunmap(seed, sizeof(*seed));
616616
} else {
617617
pr_err("Could not map UEFI random seed!\n");

drivers/firmware/efi/libstub/random.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ efi_status_t efi_random_get_seed(void)
7575
if (status != EFI_SUCCESS)
7676
return status;
7777

78-
status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA,
78+
/*
79+
* Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the
80+
* allocation will survive a kexec reboot (although we refresh the seed
81+
* beforehand)
82+
*/
83+
status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
7984
sizeof(*seed) + EFI_RANDOM_SEED_SIZE,
8085
(void **)&seed);
8186
if (status != EFI_SUCCESS)

drivers/firmware/efi/tpm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ int __init efi_tpm_eventlog_init(void)
9797
goto out_calc;
9898
}
9999

100-
memblock_reserve((unsigned long)final_tbl,
100+
memblock_reserve(efi.tpm_final_log,
101101
tbl_size + sizeof(*final_tbl));
102102
efi_tpm_final_log_size = tbl_size;
103103

drivers/firmware/efi/vars.c

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,22 @@ static struct efivars *__efivars;
2121

2222
static DEFINE_SEMAPHORE(efivars_lock);
2323

24-
static efi_status_t check_var_size(u32 attributes, unsigned long size)
25-
{
26-
const struct efivar_operations *fops;
27-
28-
fops = __efivars->ops;
29-
30-
if (!fops->query_variable_store)
31-
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
32-
33-
return fops->query_variable_store(attributes, size, false);
34-
}
35-
36-
static
37-
efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size)
24+
static efi_status_t check_var_size(bool nonblocking, u32 attributes,
25+
unsigned long size)
3826
{
3927
const struct efivar_operations *fops;
28+
efi_status_t status;
4029

4130
fops = __efivars->ops;
4231

4332
if (!fops->query_variable_store)
33+
status = EFI_UNSUPPORTED;
34+
else
35+
status = fops->query_variable_store(attributes, size,
36+
nonblocking);
37+
if (status == EFI_UNSUPPORTED)
4438
return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
45-
46-
return fops->query_variable_store(attributes, size, true);
39+
return status;
4740
}
4841

4942
/**
@@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size,
195188
}
196189
EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
197190

198-
/*
199-
* efivar_set_variable_blocking() - local helper function for set_variable
200-
*
201-
* Must be called with efivars_lock held.
202-
*/
203-
static efi_status_t
204-
efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor,
205-
u32 attr, unsigned long data_size, void *data)
206-
{
207-
efi_status_t status;
208-
209-
if (data_size > 0) {
210-
status = check_var_size(attr, data_size +
211-
ucs2_strsize(name, 1024));
212-
if (status != EFI_SUCCESS)
213-
return status;
214-
}
215-
return __efivars->ops->set_variable(name, vendor, attr, data_size, data);
216-
}
217-
218191
/*
219192
* efivar_set_variable_locked() - set a variable identified by name/vendor
220193
*
@@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
228201
efi_set_variable_t *setvar;
229202
efi_status_t status;
230203

231-
if (!nonblocking)
232-
return efivar_set_variable_blocking(name, vendor, attr,
233-
data_size, data);
204+
if (data_size > 0) {
205+
status = check_var_size(nonblocking, attr,
206+
data_size + ucs2_strsize(name, 1024));
207+
if (status != EFI_SUCCESS)
208+
return status;
209+
}
234210

235211
/*
236212
* If no _nonblocking variant exists, the ordinary one
237213
* is assumed to be non-blocking.
238214
*/
239-
setvar = __efivars->ops->set_variable_nonblocking ?:
240-
__efivars->ops->set_variable;
215+
setvar = __efivars->ops->set_variable_nonblocking;
216+
if (!setvar || !nonblocking)
217+
setvar = __efivars->ops->set_variable;
241218

242-
if (data_size > 0) {
243-
status = check_var_size_nonblocking(attr, data_size +
244-
ucs2_strsize(name, 1024));
245-
if (status != EFI_SUCCESS)
246-
return status;
247-
}
248219
return setvar(name, vendor, attr, data_size, data);
249220
}
250221
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,
264235
if (efivar_lock())
265236
return EFI_ABORTED;
266237

267-
status = efivar_set_variable_blocking(name, vendor, attr, data_size, data);
238+
status = efivar_set_variable_locked(name, vendor, attr, data_size,
239+
data, false);
268240
efivar_unlock();
269241
return status;
270242
}

include/linux/efi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ efi_status_t efi_random_get_seed(void);
12221222
arch_efi_call_virt_teardown(); \
12231223
})
12241224

1225-
#define EFI_RANDOM_SEED_SIZE 64U
1225+
#define EFI_RANDOM_SEED_SIZE 32U // BLAKE2S_HASH_SIZE
12261226

12271227
struct linux_efi_random_seed {
12281228
u32 size;

0 commit comments

Comments
 (0)