Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / firmware / efi / vars.c
blob3700e98697676d8e6f04f061f447391503f9abba
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Originally from efivars.c
5 * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
6 * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
7 */
9 #define pr_fmt(fmt) "efivars: " fmt
11 #include <linux/types.h>
12 #include <linux/sizes.h>
13 #include <linux/errno.h>
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/string.h>
17 #include <linux/smp.h>
18 #include <linux/efi.h>
19 #include <linux/ucs2_string.h>
21 /* Private pointer to registered efivars */
22 static struct efivars *__efivars;
24 static DEFINE_SEMAPHORE(efivars_lock, 1);
26 static efi_status_t check_var_size(bool nonblocking, u32 attributes,
27 unsigned long size)
29 const struct efivar_operations *fops;
30 efi_status_t status;
32 fops = __efivars->ops;
34 if (!fops->query_variable_store)
35 status = EFI_UNSUPPORTED;
36 else
37 status = fops->query_variable_store(attributes, size,
38 nonblocking);
39 if (status == EFI_UNSUPPORTED)
40 return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES;
41 return status;
44 /**
45 * efivar_is_available - check if efivars is available
47 * @return true iff evivars is currently registered
49 bool efivar_is_available(void)
51 return __efivars != NULL;
53 EXPORT_SYMBOL_GPL(efivar_is_available);
55 /**
56 * efivars_register - register an efivars
57 * @efivars: efivars to register
58 * @ops: efivars operations
60 * Only a single efivars can be registered at any time.
62 int efivars_register(struct efivars *efivars,
63 const struct efivar_operations *ops)
65 int rv;
66 int event;
68 if (down_interruptible(&efivars_lock))
69 return -EINTR;
71 if (__efivars) {
72 pr_warn("efivars already registered\n");
73 rv = -EBUSY;
74 goto out;
77 efivars->ops = ops;
79 __efivars = efivars;
81 if (efivar_supports_writes())
82 event = EFIVAR_OPS_RDWR;
83 else
84 event = EFIVAR_OPS_RDONLY;
86 blocking_notifier_call_chain(&efivar_ops_nh, event, NULL);
88 pr_info("Registered efivars operations\n");
89 rv = 0;
90 out:
91 up(&efivars_lock);
93 return rv;
95 EXPORT_SYMBOL_GPL(efivars_register);
97 /**
98 * efivars_unregister - unregister an efivars
99 * @efivars: efivars to unregister
101 * The caller must have already removed every entry from the list,
102 * failure to do so is an error.
104 int efivars_unregister(struct efivars *efivars)
106 int rv;
108 if (down_interruptible(&efivars_lock))
109 return -EINTR;
111 if (!__efivars) {
112 pr_err("efivars not registered\n");
113 rv = -EINVAL;
114 goto out;
117 if (__efivars != efivars) {
118 rv = -EINVAL;
119 goto out;
122 pr_info("Unregistered efivars operations\n");
123 __efivars = NULL;
125 rv = 0;
126 out:
127 up(&efivars_lock);
128 return rv;
130 EXPORT_SYMBOL_GPL(efivars_unregister);
132 bool efivar_supports_writes(void)
134 return __efivars && __efivars->ops->set_variable;
136 EXPORT_SYMBOL_GPL(efivar_supports_writes);
139 * efivar_lock() - obtain the efivar lock, wait for it if needed
140 * @return 0 on success, error code on failure
142 int efivar_lock(void)
144 if (down_interruptible(&efivars_lock))
145 return -EINTR;
146 if (!__efivars->ops) {
147 up(&efivars_lock);
148 return -ENODEV;
150 return 0;
152 EXPORT_SYMBOL_NS_GPL(efivar_lock, "EFIVAR");
155 * efivar_lock() - obtain the efivar lock if it is free
156 * @return 0 on success, error code on failure
158 int efivar_trylock(void)
160 if (down_trylock(&efivars_lock))
161 return -EBUSY;
162 if (!__efivars->ops) {
163 up(&efivars_lock);
164 return -ENODEV;
166 return 0;
168 EXPORT_SYMBOL_NS_GPL(efivar_trylock, "EFIVAR");
171 * efivar_unlock() - release the efivar lock
173 void efivar_unlock(void)
175 up(&efivars_lock);
177 EXPORT_SYMBOL_NS_GPL(efivar_unlock, "EFIVAR");
180 * efivar_get_variable() - retrieve a variable identified by name/vendor
182 * Must be called with efivars_lock held.
184 efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
185 u32 *attr, unsigned long *size, void *data)
187 return __efivars->ops->get_variable(name, vendor, attr, size, data);
189 EXPORT_SYMBOL_NS_GPL(efivar_get_variable, "EFIVAR");
192 * efivar_get_next_variable() - enumerate the next name/vendor pair
194 * Must be called with efivars_lock held.
196 efi_status_t efivar_get_next_variable(unsigned long *name_size,
197 efi_char16_t *name, efi_guid_t *vendor)
199 return __efivars->ops->get_next_variable(name_size, name, vendor);
201 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, "EFIVAR");
204 * efivar_set_variable_locked() - set a variable identified by name/vendor
206 * Must be called with efivars_lock held. If @nonblocking is set, it will use
207 * non-blocking primitives so it is guaranteed not to sleep.
209 efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
210 u32 attr, unsigned long data_size,
211 void *data, bool nonblocking)
213 efi_set_variable_t *setvar;
214 efi_status_t status;
216 if (data_size > 0) {
217 status = check_var_size(nonblocking, attr,
218 data_size + ucs2_strsize(name, EFI_VAR_NAME_LEN));
219 if (status != EFI_SUCCESS)
220 return status;
224 * If no _nonblocking variant exists, the ordinary one
225 * is assumed to be non-blocking.
227 setvar = __efivars->ops->set_variable_nonblocking;
228 if (!setvar || !nonblocking)
229 setvar = __efivars->ops->set_variable;
231 return setvar(name, vendor, attr, data_size, data);
233 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, "EFIVAR");
236 * efivar_set_variable() - set a variable identified by name/vendor
238 * Can be called without holding the efivars_lock. Will sleep on obtaining the
239 * lock, or on obtaining other locks that are needed in order to complete the
240 * call.
242 efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
243 u32 attr, unsigned long data_size, void *data)
245 efi_status_t status;
247 if (efivar_lock())
248 return EFI_ABORTED;
250 status = efivar_set_variable_locked(name, vendor, attr, data_size,
251 data, false);
252 efivar_unlock();
253 return status;
255 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, "EFIVAR");
257 efi_status_t efivar_query_variable_info(u32 attr,
258 u64 *storage_space,
259 u64 *remaining_space,
260 u64 *max_variable_size)
262 if (!__efivars->ops->query_variable_info)
263 return EFI_UNSUPPORTED;
264 return __efivars->ops->query_variable_info(attr, storage_space,
265 remaining_space, max_variable_size);
267 EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, "EFIVAR");