aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2013-03-25 05:14:30 -0400
committerMatt Fleming <matt.fleming@intel.com>2013-04-09 06:34:05 -0400
commita6e4d5a03e9e3587e88aba687d8f225f4f04c792 (patch)
tree5159dd594f8c70ef964ec0fef74a6e8482f2606e
parentca0ba26fbbd2d81c43085df49ce0abfe34535a90 (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.c25
-rw-r--r--drivers/firmware/efivars.c18
-rw-r--r--include/linux/efi.h9
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 */
1011efi_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
436check_var_size_locked(struct efivars *efivars, u32 attributes, 436check_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);
336typedef 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
576extern void efi_late_init(void); 577extern void efi_late_init(void);
577extern void efi_free_boot_services(void); 578extern void efi_free_boot_services(void);
579extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size);
578#else 580#else
579static inline void efi_late_init(void) {} 581static inline void efi_late_init(void) {}
580static inline void efi_free_boot_services(void) {} 582static inline void efi_free_boot_services(void) {}
583
584static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)
585{
586 return EFI_SUCCESS;
587}
581#endif 588#endif
582extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); 589extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
583extern u64 efi_get_iobase (void); 590extern 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
737struct efivars { 744struct efivars {