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

x86, efivars: firmware bug workarounds should be in platform code

Let's not burden ia64 with checks in the common efivars code that we're not
writing too much data to the variable store. That kind of thing is an x86
firmware bug, plain and simple.

efi_query_variable_store() provides platforms with a wrapper in which they can
perform checks and workarounds for EFI variable storage bugs.

Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>

+36 -16
+25
arch/x86/platform/efi/efi.c
··· 999 999 } 1000 1000 return 0; 1001 1001 } 1002 + 1003 + /* 1004 + * Some firmware has serious problems when using more than 50% of the EFI 1005 + * variable store, i.e. it triggers bugs that can brick machines. Ensure that 1006 + * we never use more than this safe limit. 1007 + * 1008 + * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable 1009 + * store. 1010 + */ 1011 + efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) 1012 + { 1013 + efi_status_t status; 1014 + u64 storage_size, remaining_size, max_size; 1015 + 1016 + status = efi.query_variable_info(attributes, &storage_size, 1017 + &remaining_size, &max_size); 1018 + if (status != EFI_SUCCESS) 1019 + return status; 1020 + 1021 + if (!storage_size || size > remaining_size || size > max_size || 1022 + (remaining_size - size) < (storage_size / 2)) 1023 + return EFI_OUT_OF_RESOURCES; 1024 + 1025 + return EFI_SUCCESS; 1026 + }
+3 -15
drivers/firmware/efivars.c
··· 436 436 check_var_size_locked(struct efivars *efivars, u32 attributes, 437 437 unsigned long size) 438 438 { 439 - u64 storage_size, remaining_size, max_size; 440 - efi_status_t status; 441 439 const struct efivar_operations *fops = efivars->ops; 442 440 443 - if (!efivars->ops->query_variable_info) 441 + if (!efivars->ops->query_variable_store) 444 442 return EFI_UNSUPPORTED; 445 443 446 - status = fops->query_variable_info(attributes, &storage_size, 447 - &remaining_size, &max_size); 448 - 449 - if (status != EFI_SUCCESS) 450 - return status; 451 - 452 - if (!storage_size || size > remaining_size || size > max_size || 453 - (remaining_size - size) < (storage_size / 2)) 454 - return EFI_OUT_OF_RESOURCES; 455 - 456 - return status; 444 + return fops->query_variable_store(attributes, size); 457 445 } 458 446 459 447 ··· 2119 2131 ops.get_variable = efi.get_variable; 2120 2132 ops.set_variable = efi.set_variable; 2121 2133 ops.get_next_variable = efi.get_next_variable; 2122 - ops.query_variable_info = efi.query_variable_info; 2134 + ops.query_variable_store = efi_query_variable_store; 2123 2135 2124 2136 error = register_efivars(&__efivars, &ops, efi_kobj); 2125 2137 if (error)
+8 -1
include/linux/efi.h
··· 333 333 unsigned long count, 334 334 u64 *max_size, 335 335 int *reset_type); 336 + typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size); 336 337 337 338 /* 338 339 * EFI Configuration Table and GUID definitions ··· 576 575 #ifdef CONFIG_X86 577 576 extern void efi_late_init(void); 578 577 extern void efi_free_boot_services(void); 578 + extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size); 579 579 #else 580 580 static inline void efi_late_init(void) {} 581 581 static inline void efi_free_boot_services(void) {} 582 + 583 + static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) 584 + { 585 + return EFI_SUCCESS; 586 + } 582 587 #endif 583 588 extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); 584 589 extern u64 efi_get_iobase (void); ··· 738 731 efi_get_variable_t *get_variable; 739 732 efi_get_next_variable_t *get_next_variable; 740 733 efi_set_variable_t *set_variable; 741 - efi_query_variable_info_t *query_variable_info; 734 + efi_query_variable_store_t *query_variable_store; 742 735 }; 743 736 744 737 struct efivars {