Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+98 -54
+8
arch/arm64/include/asm/efi.h
··· 14 14 15 15 #ifdef CONFIG_EFI 16 16 extern void efi_init(void); 17 + 18 + bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg); 17 19 #else 18 20 #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 + } 19 27 #endif 20 28 21 29 int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+31 -2
arch/arm64/kernel/efi-rt-wrapper.S
··· 6 6 #include <linux/linkage.h> 7 7 8 8 SYM_FUNC_START(__efi_rt_asm_wrapper) 9 - stp x29, x30, [sp, #-32]! 9 + stp x29, x30, [sp, #-112]! 10 10 mov x29, sp 11 11 12 12 /* ··· 15 15 * (such as UEFI) should never touch it. 16 16 */ 17 17 stp x1, x18, [sp, #16] 18 + 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] 18 32 19 33 /* 20 34 * We are lucky enough that no EFI runtime services take more than ··· 45 31 46 32 ldp x1, x2, [sp, #16] 47 33 cmp x2, x18 48 - ldp x29, x30, [sp], #32 34 + ldp x29, x30, [sp], #112 49 35 b.ne 0f 50 36 ret 51 37 0: ··· 59 45 mov x18, x2 60 46 b efi_handle_corrupted_x18 // tail call 61 47 SYM_FUNC_END(__efi_rt_asm_wrapper) 48 + 49 + SYM_FUNC_START(__efi_rt_asm_recover) 50 + ldr_this_cpu x8, __efi_rt_asm_recover_sp, x9 51 + mov sp, x8 52 + 53 + ldp x0, x18, [sp, #16] 54 + ldp x19, x20, [sp, #32] 55 + ldp x21, x22, [sp, #48] 56 + ldp x23, x24, [sp, #64] 57 + ldp x25, x26, [sp, #80] 58 + ldp x27, x28, [sp, #96] 59 + ldp x29, x30, [sp], #112 60 + 61 + b efi_handle_runtime_exception 62 + SYM_FUNC_END(__efi_rt_asm_recover)
+26
arch/arm64/kernel/efi.c
··· 9 9 10 10 #include <linux/efi.h> 11 11 #include <linux/init.h> 12 + #include <linux/percpu.h> 12 13 13 14 #include <asm/efi.h> 14 15 ··· 128 127 { 129 128 pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f); 130 129 return s; 130 + } 131 + 132 + asmlinkage DEFINE_PER_CPU(u64, __efi_rt_asm_recover_sp); 133 + 134 + asmlinkage efi_status_t __efi_rt_asm_recover(void); 135 + 136 + asmlinkage efi_status_t efi_handle_runtime_exception(const char *f) 137 + { 138 + pr_err(FW_BUG "Synchronous exception occurred in EFI runtime service %s()\n", f); 139 + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); 140 + return EFI_ABORTED; 141 + } 142 + 143 + bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg) 144 + { 145 + /* Check whether the exception occurred while running the firmware */ 146 + if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64) 147 + return false; 148 + 149 + pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg); 150 + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); 151 + dump_stack(); 152 + 153 + regs->pc = (u64)__efi_rt_asm_recover; 154 + return true; 131 155 }
+4
arch/arm64/mm/fault.c
··· 30 30 #include <asm/bug.h> 31 31 #include <asm/cmpxchg.h> 32 32 #include <asm/cpufeature.h> 33 + #include <asm/efi.h> 33 34 #include <asm/exception.h> 34 35 #include <asm/daifflags.h> 35 36 #include <asm/debug-monitors.h> ··· 391 390 392 391 msg = "paging request"; 393 392 } 393 + 394 + if (efi_runtime_fixup_exception(regs, msg)) 395 + return; 394 396 395 397 die_kernel_fault(msg, addr, esr, regs); 396 398 }
+1 -1
drivers/firmware/efi/efi.c
··· 611 611 612 612 seed = early_memremap(efi_rng_seed, sizeof(*seed)); 613 613 if (seed != NULL) { 614 - size = READ_ONCE(seed->size); 614 + size = min(seed->size, EFI_RANDOM_SEED_SIZE); 615 615 early_memunmap(seed, sizeof(*seed)); 616 616 } else { 617 617 pr_err("Could not map UEFI random seed!\n");
+6 -1
drivers/firmware/efi/libstub/random.c
··· 75 75 if (status != EFI_SUCCESS) 76 76 return status; 77 77 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, 79 84 sizeof(*seed) + EFI_RANDOM_SEED_SIZE, 80 85 (void **)&seed); 81 86 if (status != EFI_SUCCESS)
+1 -1
drivers/firmware/efi/tpm.c
··· 97 97 goto out_calc; 98 98 } 99 99 100 - memblock_reserve((unsigned long)final_tbl, 100 + memblock_reserve(efi.tpm_final_log, 101 101 tbl_size + sizeof(*final_tbl)); 102 102 efi_tpm_final_log_size = tbl_size; 103 103
+20 -48
drivers/firmware/efi/vars.c
··· 21 21 22 22 static DEFINE_SEMAPHORE(efivars_lock); 23 23 24 - static efi_status_t check_var_size(u32 attributes, unsigned long size) 24 + static efi_status_t check_var_size(bool nonblocking, u32 attributes, 25 + unsigned long size) 25 26 { 26 27 const struct efivar_operations *fops; 28 + efi_status_t status; 27 29 28 30 fops = __efivars->ops; 29 31 30 32 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) 31 38 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) 38 - { 39 - const struct efivar_operations *fops; 40 - 41 - fops = __efivars->ops; 42 - 43 - if (!fops->query_variable_store) 44 - return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; 45 - 46 - return fops->query_variable_store(attributes, size, true); 39 + return status; 47 40 } 48 41 49 42 /** ··· 189 196 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR); 190 197 191 198 /* 192 - * efivar_set_variable_blocking() - local helper function for set_variable 193 - * 194 - * Must be called with efivars_lock held. 195 - */ 196 - static efi_status_t 197 - efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor, 198 - u32 attr, unsigned long data_size, void *data) 199 - { 200 - efi_status_t status; 201 - 202 - if (data_size > 0) { 203 - status = check_var_size(attr, data_size + 204 - ucs2_strsize(name, 1024)); 205 - if (status != EFI_SUCCESS) 206 - return status; 207 - } 208 - return __efivars->ops->set_variable(name, vendor, attr, data_size, data); 209 - } 210 - 211 - /* 212 199 * efivar_set_variable_locked() - set a variable identified by name/vendor 213 200 * 214 201 * Must be called with efivars_lock held. If @nonblocking is set, it will use ··· 201 228 efi_set_variable_t *setvar; 202 229 efi_status_t status; 203 230 204 - if (!nonblocking) 205 - return efivar_set_variable_blocking(name, vendor, attr, 206 - data_size, data); 231 + if (data_size > 0) { 232 + status = check_var_size(nonblocking, attr, 233 + data_size + ucs2_strsize(name, 1024)); 234 + if (status != EFI_SUCCESS) 235 + return status; 236 + } 207 237 208 238 /* 209 239 * If no _nonblocking variant exists, the ordinary one 210 240 * is assumed to be non-blocking. 211 241 */ 212 - setvar = __efivars->ops->set_variable_nonblocking ?: 213 - __efivars->ops->set_variable; 242 + setvar = __efivars->ops->set_variable_nonblocking; 243 + if (!setvar || !nonblocking) 244 + setvar = __efivars->ops->set_variable; 214 245 215 - if (data_size > 0) { 216 - status = check_var_size_nonblocking(attr, data_size + 217 - ucs2_strsize(name, 1024)); 218 - if (status != EFI_SUCCESS) 219 - return status; 220 - } 221 246 return setvar(name, vendor, attr, data_size, data); 222 247 } 223 248 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR); ··· 235 264 if (efivar_lock()) 236 265 return EFI_ABORTED; 237 266 238 - status = efivar_set_variable_blocking(name, vendor, attr, data_size, data); 267 + status = efivar_set_variable_locked(name, vendor, attr, data_size, 268 + data, false); 239 269 efivar_unlock(); 240 270 return status; 241 271 }
+1 -1
include/linux/efi.h
··· 1222 1222 arch_efi_call_virt_teardown(); \ 1223 1223 }) 1224 1224 1225 - #define EFI_RANDOM_SEED_SIZE 64U 1225 + #define EFI_RANDOM_SEED_SIZE 32U // BLAKE2S_HASH_SIZE 1226 1226 1227 1227 struct linux_efi_random_seed { 1228 1228 u32 size;