| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Originally from efivars.c
- *
- * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
- * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
- */
- #define pr_fmt(fmt) "efivars: " fmt
- #include <linux/types.h>
- #include <linux/sizes.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/string.h>
- #include <linux/smp.h>
- #include <linux/efi.h>
- #include <linux/ucs2_string.h>
- /* Private pointer to registered efivars */
- static struct efivars *__efivars;
- static DEFINE_SEMAPHORE(efivars_lock, 1);
- static efi_status_t check_var_size(bool nonblocking, u32 attributes,
- unsigned long size)
- {
- const struct efivar_operations *fops;
- efi_status_t status;
- fops = __efivars->ops;
- if (!fops->query_variable_store)
- status = EFI_UNSUPPORTED;
- else
- status = fops->query_variable_store(attributes, size,
- nonblocking);
- if (status == EFI_UNSUPPORTED)
- return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
- return status;
- }
- /**
- * efivar_is_available - check if efivars is available
- *
- * @return true iff evivars is currently registered
- */
- bool efivar_is_available(void)
- {
- return __efivars != NULL;
- }
- EXPORT_SYMBOL_GPL(efivar_is_available);
- /**
- * efivars_register - register an efivars
- * @efivars: efivars to register
- * @ops: efivars operations
- *
- * Only a single efivars can be registered at any time.
- */
- int efivars_register(struct efivars *efivars,
- const struct efivar_operations *ops)
- {
- int rv;
- int event;
- if (down_interruptible(&efivars_lock))
- return -EINTR;
- if (__efivars) {
- pr_warn("efivars already registered\n");
- rv = -EBUSY;
- goto out;
- }
- efivars->ops = ops;
- __efivars = efivars;
- if (efivar_supports_writes())
- event = EFIVAR_OPS_RDWR;
- else
- event = EFIVAR_OPS_RDONLY;
- blocking_notifier_call_chain(&efivar_ops_nh, event, NULL);
- pr_info("Registered efivars operations\n");
- rv = 0;
- out:
- up(&efivars_lock);
- return rv;
- }
- EXPORT_SYMBOL_GPL(efivars_register);
- /**
- * efivars_unregister - unregister an efivars
- * @efivars: efivars to unregister
- *
- * The caller must have already removed every entry from the list,
- * failure to do so is an error.
- */
- int efivars_unregister(struct efivars *efivars)
- {
- int rv;
- if (down_interruptible(&efivars_lock))
- return -EINTR;
- if (!__efivars) {
- pr_err("efivars not registered\n");
- rv = -EINVAL;
- goto out;
- }
- if (__efivars != efivars) {
- rv = -EINVAL;
- goto out;
- }
- pr_info("Unregistered efivars operations\n");
- __efivars = NULL;
- rv = 0;
- out:
- up(&efivars_lock);
- return rv;
- }
- EXPORT_SYMBOL_GPL(efivars_unregister);
- bool efivar_supports_writes(void)
- {
- return __efivars && __efivars->ops->set_variable;
- }
- EXPORT_SYMBOL_GPL(efivar_supports_writes);
- /*
- * efivar_lock() - obtain the efivar lock, wait for it if needed
- * @return 0 on success, error code on failure
- */
- int efivar_lock(void)
- {
- if (down_interruptible(&efivars_lock))
- return -EINTR;
- if (!__efivars->ops) {
- up(&efivars_lock);
- return -ENODEV;
- }
- return 0;
- }
- EXPORT_SYMBOL_NS_GPL(efivar_lock, "EFIVAR");
- /*
- * efivar_lock() - obtain the efivar lock if it is free
- * @return 0 on success, error code on failure
- */
- int efivar_trylock(void)
- {
- if (down_trylock(&efivars_lock))
- return -EBUSY;
- if (!__efivars->ops) {
- up(&efivars_lock);
- return -ENODEV;
- }
- return 0;
- }
- EXPORT_SYMBOL_NS_GPL(efivar_trylock, "EFIVAR");
- /*
- * efivar_unlock() - release the efivar lock
- */
- void efivar_unlock(void)
- {
- up(&efivars_lock);
- }
- EXPORT_SYMBOL_NS_GPL(efivar_unlock, "EFIVAR");
- /*
- * efivar_get_variable() - retrieve a variable identified by name/vendor
- *
- * Must be called with efivars_lock held.
- */
- efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
- u32 *attr, unsigned long *size, void *data)
- {
- return __efivars->ops->get_variable(name, vendor, attr, size, data);
- }
- EXPORT_SYMBOL_NS_GPL(efivar_get_variable, "EFIVAR");
- /*
- * efivar_get_next_variable() - enumerate the next name/vendor pair
- *
- * Must be called with efivars_lock held.
- */
- efi_status_t efivar_get_next_variable(unsigned long *name_size,
- efi_char16_t *name, efi_guid_t *vendor)
- {
- return __efivars->ops->get_next_variable(name_size, name, vendor);
- }
- EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, "EFIVAR");
- /*
- * efivar_set_variable_locked() - set a variable identified by name/vendor
- *
- * Must be called with efivars_lock held. If @nonblocking is set, it will use
- * non-blocking primitives so it is guaranteed not to sleep.
- */
- efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
- u32 attr, unsigned long data_size,
- void *data, bool nonblocking)
- {
- efi_set_variable_t *setvar;
- efi_status_t status;
- if (data_size > 0) {
- status = check_var_size(nonblocking, attr,
- data_size + ucs2_strsize(name, EFI_VAR_NAME_LEN));
- if (status != EFI_SUCCESS)
- return status;
- }
- /*
- * If no _nonblocking variant exists, the ordinary one
- * is assumed to be non-blocking.
- */
- setvar = __efivars->ops->set_variable_nonblocking;
- if (!setvar || !nonblocking)
- setvar = __efivars->ops->set_variable;
- return setvar(name, vendor, attr, data_size, data);
- }
- EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, "EFIVAR");
- /*
- * efivar_set_variable() - set a variable identified by name/vendor
- *
- * Can be called without holding the efivars_lock. Will sleep on obtaining the
- * lock, or on obtaining other locks that are needed in order to complete the
- * call.
- */
- efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
- u32 attr, unsigned long data_size, void *data)
- {
- efi_status_t status;
- if (efivar_lock())
- return EFI_ABORTED;
- status = efivar_set_variable_locked(name, vendor, attr, data_size,
- data, false);
- efivar_unlock();
- return status;
- }
- EXPORT_SYMBOL_NS_GPL(efivar_set_variable, "EFIVAR");
- efi_status_t efivar_query_variable_info(u32 attr,
- u64 *storage_space,
- u64 *remaining_space,
- u64 *max_variable_size)
- {
- if (!__efivars->ops->query_variable_info)
- return EFI_UNSUPPORTED;
- return __efivars->ops->query_variable_info(attr, storage_space,
- remaining_space, max_variable_size);
- }
- EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, "EFIVAR");
|