diff options
-rw-r--r-- | drivers/firmware/efi/runtime-wrappers.c | 19 | ||||
-rw-r--r-- | drivers/firmware/efi/vars.c | 47 | ||||
-rw-r--r-- | include/linux/efi.h | 6 |
3 files changed, 72 insertions, 0 deletions
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 9694cba665c4..4349206198b2 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c | |||
@@ -200,6 +200,24 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |||
200 | return status; | 200 | return status; |
201 | } | 201 | } |
202 | 202 | ||
203 | static efi_status_t | ||
204 | virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, | ||
205 | u32 attr, unsigned long data_size, | ||
206 | void *data) | ||
207 | { | ||
208 | unsigned long flags; | ||
209 | efi_status_t status; | ||
210 | |||
211 | if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) | ||
212 | return EFI_NOT_READY; | ||
213 | |||
214 | status = efi_call_virt(set_variable, name, vendor, attr, data_size, | ||
215 | data); | ||
216 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
217 | return status; | ||
218 | } | ||
219 | |||
220 | |||
203 | static efi_status_t virt_efi_query_variable_info(u32 attr, | 221 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
204 | u64 *storage_space, | 222 | u64 *storage_space, |
205 | u64 *remaining_space, | 223 | u64 *remaining_space, |
@@ -287,6 +305,7 @@ void efi_native_runtime_setup(void) | |||
287 | efi.get_variable = virt_efi_get_variable; | 305 | efi.get_variable = virt_efi_get_variable; |
288 | efi.get_next_variable = virt_efi_get_next_variable; | 306 | efi.get_next_variable = virt_efi_get_next_variable; |
289 | efi.set_variable = virt_efi_set_variable; | 307 | efi.set_variable = virt_efi_set_variable; |
308 | efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; | ||
290 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; | 309 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; |
291 | efi.reset_system = virt_efi_reset_system; | 310 | efi.reset_system = virt_efi_reset_system; |
292 | efi.query_variable_info = virt_efi_query_variable_info; | 311 | efi.query_variable_info = virt_efi_query_variable_info; |
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 1fa724f31b0e..fa3c66bdc1e5 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c | |||
@@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | |||
595 | } | 595 | } |
596 | EXPORT_SYMBOL_GPL(efivar_entry_set); | 596 | EXPORT_SYMBOL_GPL(efivar_entry_set); |
597 | 597 | ||
598 | /* | ||
599 | * efivar_entry_set_nonblocking - call set_variable_nonblocking() | ||
600 | * | ||
601 | * This function is guaranteed to not block and is suitable for calling | ||
602 | * from crash/panic handlers. | ||
603 | * | ||
604 | * Crucially, this function will not block if it cannot acquire | ||
605 | * __efivars->lock. Instead, it returns -EBUSY. | ||
606 | */ | ||
607 | static int | ||
608 | efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | ||
609 | u32 attributes, unsigned long size, void *data) | ||
610 | { | ||
611 | const struct efivar_operations *ops = __efivars->ops; | ||
612 | unsigned long flags; | ||
613 | efi_status_t status; | ||
614 | |||
615 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) | ||
616 | return -EBUSY; | ||
617 | |||
618 | status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); | ||
619 | if (status != EFI_SUCCESS) { | ||
620 | spin_unlock_irqrestore(&__efivars->lock, flags); | ||
621 | return -ENOSPC; | ||
622 | } | ||
623 | |||
624 | status = ops->set_variable_nonblocking(name, &vendor, attributes, | ||
625 | size, data); | ||
626 | |||
627 | spin_unlock_irqrestore(&__efivars->lock, flags); | ||
628 | return efi_status_to_err(status); | ||
629 | } | ||
630 | |||
598 | /** | 631 | /** |
599 | * efivar_entry_set_safe - call set_variable() if enough space in firmware | 632 | * efivar_entry_set_safe - call set_variable() if enough space in firmware |
600 | * @name: buffer containing the variable name | 633 | * @name: buffer containing the variable name |
@@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | |||
622 | if (!ops->query_variable_store) | 655 | if (!ops->query_variable_store) |
623 | return -ENOSYS; | 656 | return -ENOSYS; |
624 | 657 | ||
658 | /* | ||
659 | * If the EFI variable backend provides a non-blocking | ||
660 | * ->set_variable() operation and we're in a context where we | ||
661 | * cannot block, then we need to use it to avoid live-locks, | ||
662 | * since the implication is that the regular ->set_variable() | ||
663 | * will block. | ||
664 | * | ||
665 | * If no ->set_variable_nonblocking() is provided then | ||
666 | * ->set_variable() is assumed to be non-blocking. | ||
667 | */ | ||
668 | if (!block && ops->set_variable_nonblocking) | ||
669 | return efivar_entry_set_nonblocking(name, vendor, attributes, | ||
670 | size, data); | ||
671 | |||
625 | if (!block) { | 672 | if (!block) { |
626 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) | 673 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) |
627 | return -EBUSY; | 674 | return -EBUSY; |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 78b29b133e14..0949f9c7e872 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -503,6 +503,10 @@ typedef efi_status_t efi_get_next_variable_t (unsigned long *name_size, efi_char | |||
503 | typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, | 503 | typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, |
504 | u32 attr, unsigned long data_size, | 504 | u32 attr, unsigned long data_size, |
505 | void *data); | 505 | void *data); |
506 | typedef efi_status_t | ||
507 | efi_set_variable_nonblocking_t(efi_char16_t *name, efi_guid_t *vendor, | ||
508 | u32 attr, unsigned long data_size, void *data); | ||
509 | |||
506 | typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); | 510 | typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); |
507 | typedef void efi_reset_system_t (int reset_type, efi_status_t status, | 511 | typedef void efi_reset_system_t (int reset_type, efi_status_t status, |
508 | unsigned long data_size, efi_char16_t *data); | 512 | unsigned long data_size, efi_char16_t *data); |
@@ -822,6 +826,7 @@ extern struct efi { | |||
822 | efi_get_variable_t *get_variable; | 826 | efi_get_variable_t *get_variable; |
823 | efi_get_next_variable_t *get_next_variable; | 827 | efi_get_next_variable_t *get_next_variable; |
824 | efi_set_variable_t *set_variable; | 828 | efi_set_variable_t *set_variable; |
829 | efi_set_variable_nonblocking_t *set_variable_nonblocking; | ||
825 | efi_query_variable_info_t *query_variable_info; | 830 | efi_query_variable_info_t *query_variable_info; |
826 | efi_update_capsule_t *update_capsule; | 831 | efi_update_capsule_t *update_capsule; |
827 | efi_query_capsule_caps_t *query_capsule_caps; | 832 | efi_query_capsule_caps_t *query_capsule_caps; |
@@ -1042,6 +1047,7 @@ struct efivar_operations { | |||
1042 | efi_get_variable_t *get_variable; | 1047 | efi_get_variable_t *get_variable; |
1043 | efi_get_next_variable_t *get_next_variable; | 1048 | efi_get_next_variable_t *get_next_variable; |
1044 | efi_set_variable_t *set_variable; | 1049 | efi_set_variable_t *set_variable; |
1050 | efi_set_variable_nonblocking_t *set_variable_nonblocking; | ||
1045 | efi_query_variable_store_t *query_variable_store; | 1051 | efi_query_variable_store_t *query_variable_store; |
1046 | }; | 1052 | }; |
1047 | 1053 | ||