diff options
author | Matt Fleming <matt.fleming@intel.com> | 2013-03-25 05:14:30 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-04-09 06:34:05 -0400 |
commit | a6e4d5a03e9e3587e88aba687d8f225f4f04c792 (patch) | |
tree | 5159dd594f8c70ef964ec0fef74a6e8482f2606e | |
parent | ca0ba26fbbd2d81c43085df49ce0abfe34535a90 (diff) |
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>
-rw-r--r-- | arch/x86/platform/efi/efi.c | 25 | ||||
-rw-r--r-- | drivers/firmware/efivars.c | 18 | ||||
-rw-r--r-- | include/linux/efi.h | 9 |
3 files changed, 36 insertions, 16 deletions
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 5f2ecaf3f9d8..c89c245eff40 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -999,3 +999,28 @@ u64 efi_mem_attributes(unsigned long phys_addr) | |||
999 | } | 999 | } |
1000 | return 0; | 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 | } | ||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7acafb80fd4c..bf15d81d74e1 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -436,24 +436,12 @@ static efi_status_t | |||
436 | check_var_size_locked(struct efivars *efivars, u32 attributes, | 436 | check_var_size_locked(struct efivars *efivars, u32 attributes, |
437 | unsigned long size) | 437 | unsigned long size) |
438 | { | 438 | { |
439 | u64 storage_size, remaining_size, max_size; | ||
440 | efi_status_t status; | ||
441 | const struct efivar_operations *fops = efivars->ops; | 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 | return EFI_UNSUPPORTED; | 442 | return EFI_UNSUPPORTED; |
445 | 443 | ||
446 | status = fops->query_variable_info(attributes, &storage_size, | 444 | return fops->query_variable_store(attributes, 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; | ||
457 | } | 445 | } |
458 | 446 | ||
459 | 447 | ||
@@ -2131,7 +2119,7 @@ efivars_init(void) | |||
2131 | ops.get_variable = efi.get_variable; | 2119 | ops.get_variable = efi.get_variable; |
2132 | ops.set_variable = efi.set_variable; | 2120 | ops.set_variable = efi.set_variable; |
2133 | ops.get_next_variable = efi.get_next_variable; | 2121 | ops.get_next_variable = efi.get_next_variable; |
2134 | ops.query_variable_info = efi.query_variable_info; | 2122 | ops.query_variable_store = efi_query_variable_store; |
2135 | 2123 | ||
2136 | error = register_efivars(&__efivars, &ops, efi_kobj); | 2124 | error = register_efivars(&__efivars, &ops, efi_kobj); |
2137 | if (error) | 2125 | if (error) |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 9bf2f1fcae27..3d7df3d32c66 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -333,6 +333,7 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, | |||
333 | unsigned long count, | 333 | unsigned long count, |
334 | u64 *max_size, | 334 | u64 *max_size, |
335 | int *reset_type); | 335 | int *reset_type); |
336 | typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size); | ||
336 | 337 | ||
337 | /* | 338 | /* |
338 | * EFI Configuration Table and GUID definitions | 339 | * EFI Configuration Table and GUID definitions |
@@ -575,9 +576,15 @@ extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if pos | |||
575 | #ifdef CONFIG_X86 | 576 | #ifdef CONFIG_X86 |
576 | extern void efi_late_init(void); | 577 | extern void efi_late_init(void); |
577 | extern void efi_free_boot_services(void); | 578 | extern void efi_free_boot_services(void); |
579 | extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size); | ||
578 | #else | 580 | #else |
579 | static inline void efi_late_init(void) {} | 581 | static inline void efi_late_init(void) {} |
580 | static inline void efi_free_boot_services(void) {} | 582 | static inline void efi_free_boot_services(void) {} |
583 | |||
584 | static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) | ||
585 | { | ||
586 | return EFI_SUCCESS; | ||
587 | } | ||
581 | #endif | 588 | #endif |
582 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); | 589 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); |
583 | extern u64 efi_get_iobase (void); | 590 | extern u64 efi_get_iobase (void); |
@@ -731,7 +738,7 @@ struct efivar_operations { | |||
731 | efi_get_variable_t *get_variable; | 738 | efi_get_variable_t *get_variable; |
732 | efi_get_next_variable_t *get_next_variable; | 739 | efi_get_next_variable_t *get_next_variable; |
733 | efi_set_variable_t *set_variable; | 740 | efi_set_variable_t *set_variable; |
734 | efi_query_variable_info_t *query_variable_info; | 741 | efi_query_variable_store_t *query_variable_store; |
735 | }; | 742 | }; |
736 | 743 | ||
737 | struct efivars { | 744 | struct efivars { |