aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c19
-rw-r--r--drivers/firmware/efi/vars.c47
-rw-r--r--include/linux/efi.h6
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
203static efi_status_t
204virt_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
203static efi_status_t virt_efi_query_variable_info(u32 attr, 221static 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}
596EXPORT_SYMBOL_GPL(efivar_entry_set); 596EXPORT_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 */
607static int
608efivar_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
503typedef efi_status_t efi_set_variable_t (efi_char16_t *name, efi_guid_t *vendor, 503typedef 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);
506typedef efi_status_t
507efi_set_variable_nonblocking_t(efi_char16_t *name, efi_guid_t *vendor,
508 u32 attr, unsigned long data_size, void *data);
509
506typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count); 510typedef efi_status_t efi_get_next_high_mono_count_t (u32 *count);
507typedef void efi_reset_system_t (int reset_type, efi_status_t status, 511typedef 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