diff options
-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 { |