xref: /linux/drivers/firmware/efi/vars.c (revision 23c48a124b469cee2eb0c75e6d22d366d1caa118)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Originally from efivars.c
4  *
5  * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
6  * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
7  */
8 
9 #include <linux/types.h>
10 #include <linux/errno.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/string.h>
14 #include <linux/smp.h>
15 #include <linux/efi.h>
16 #include <linux/ucs2_string.h>
17 
18 /* Private pointer to registered efivars */
19 static struct efivars *__efivars;
20 
21 static DEFINE_SEMAPHORE(efivars_lock);
22 
23 efi_status_t check_var_size(u32 attributes, unsigned long size)
24 {
25 	const struct efivar_operations *fops;
26 
27 	fops = __efivars->ops;
28 
29 	if (!fops->query_variable_store)
30 		return EFI_UNSUPPORTED;
31 
32 	return fops->query_variable_store(attributes, size, false);
33 }
34 EXPORT_SYMBOL_NS_GPL(check_var_size, EFIVAR);
35 
36 efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size)
37 {
38 	const struct efivar_operations *fops;
39 
40 	fops = __efivars->ops;
41 
42 	if (!fops->query_variable_store)
43 		return EFI_UNSUPPORTED;
44 
45 	return fops->query_variable_store(attributes, size, true);
46 }
47 EXPORT_SYMBOL_NS_GPL(check_var_size_nonblocking, EFIVAR);
48 
49 /**
50  * efivars_kobject - get the kobject for the registered efivars
51  *
52  * If efivars_register() has not been called we return NULL,
53  * otherwise return the kobject used at registration time.
54  */
55 struct kobject *efivars_kobject(void)
56 {
57 	if (!__efivars)
58 		return NULL;
59 
60 	return __efivars->kobject;
61 }
62 EXPORT_SYMBOL_GPL(efivars_kobject);
63 
64 /**
65  * efivars_register - register an efivars
66  * @efivars: efivars to register
67  * @ops: efivars operations
68  * @kobject: @efivars-specific kobject
69  *
70  * Only a single efivars can be registered at any time.
71  */
72 int efivars_register(struct efivars *efivars,
73 		     const struct efivar_operations *ops,
74 		     struct kobject *kobject)
75 {
76 	if (down_interruptible(&efivars_lock))
77 		return -EINTR;
78 
79 	efivars->ops = ops;
80 	efivars->kobject = kobject;
81 
82 	__efivars = efivars;
83 
84 	pr_info("Registered efivars operations\n");
85 
86 	up(&efivars_lock);
87 
88 	return 0;
89 }
90 EXPORT_SYMBOL_GPL(efivars_register);
91 
92 /**
93  * efivars_unregister - unregister an efivars
94  * @efivars: efivars to unregister
95  *
96  * The caller must have already removed every entry from the list,
97  * failure to do so is an error.
98  */
99 int efivars_unregister(struct efivars *efivars)
100 {
101 	int rv;
102 
103 	if (down_interruptible(&efivars_lock))
104 		return -EINTR;
105 
106 	if (!__efivars) {
107 		printk(KERN_ERR "efivars not registered\n");
108 		rv = -EINVAL;
109 		goto out;
110 	}
111 
112 	if (__efivars != efivars) {
113 		rv = -EINVAL;
114 		goto out;
115 	}
116 
117 	pr_info("Unregistered efivars operations\n");
118 	__efivars = NULL;
119 
120 	rv = 0;
121 out:
122 	up(&efivars_lock);
123 	return rv;
124 }
125 EXPORT_SYMBOL_GPL(efivars_unregister);
126 
127 int efivar_supports_writes(void)
128 {
129 	return __efivars && __efivars->ops->set_variable;
130 }
131 EXPORT_SYMBOL_GPL(efivar_supports_writes);
132 
133 /*
134  * efivar_lock() - obtain the efivar lock, wait for it if needed
135  * @return 0 on success, error code on failure
136  */
137 int efivar_lock(void)
138 {
139 	if (down_interruptible(&efivars_lock))
140 		return -EINTR;
141 	if (!__efivars->ops) {
142 		up(&efivars_lock);
143 		return -ENODEV;
144 	}
145 	return 0;
146 }
147 EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR);
148 
149 /*
150  * efivar_lock() - obtain the efivar lock if it is free
151  * @return 0 on success, error code on failure
152  */
153 int efivar_trylock(void)
154 {
155 	if (down_trylock(&efivars_lock))
156 		 return -EBUSY;
157 	if (!__efivars->ops) {
158 		up(&efivars_lock);
159 		return -ENODEV;
160 	}
161 	return 0;
162 }
163 EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
164 
165 /*
166  * efivar_unlock() - release the efivar lock
167  */
168 void efivar_unlock(void)
169 {
170 	up(&efivars_lock);
171 }
172 EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR);
173 
174 /*
175  * efivar_get_variable() - retrieve a variable identified by name/vendor
176  *
177  * Must be called with efivars_lock held.
178  */
179 efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
180 				 u32 *attr, unsigned long *size, void *data)
181 {
182 	return __efivars->ops->get_variable(name, vendor, attr, size, data);
183 }
184 EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR);
185 
186 /*
187  * efivar_get_next_variable() - enumerate the next name/vendor pair
188  *
189  * Must be called with efivars_lock held.
190  */
191 efi_status_t efivar_get_next_variable(unsigned long *name_size,
192 				      efi_char16_t *name, efi_guid_t *vendor)
193 {
194 	return __efivars->ops->get_next_variable(name_size, name, vendor);
195 }
196 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
197 
198 /*
199  * efivar_set_variable_blocking() - local helper function for set_variable
200  *
201  * Must be called with efivars_lock held.
202  */
203 static efi_status_t
204 efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor,
205 			     u32 attr, unsigned long data_size, void *data)
206 {
207 	efi_status_t status;
208 
209 	if (data_size > 0) {
210 		status = check_var_size(attr, data_size +
211 					      ucs2_strsize(name, 1024));
212 		if (status != EFI_SUCCESS)
213 			return status;
214 	}
215 	return __efivars->ops->set_variable(name, vendor, attr, data_size, data);
216 }
217 
218 /*
219  * efivar_set_variable_locked() - set a variable identified by name/vendor
220  *
221  * Must be called with efivars_lock held. If @nonblocking is set, it will use
222  * non-blocking primitives so it is guaranteed not to sleep.
223  */
224 efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
225 					u32 attr, unsigned long data_size,
226 					void *data, bool nonblocking)
227 {
228 	efi_set_variable_t *setvar;
229 	efi_status_t status;
230 
231 	if (!nonblocking)
232 		return efivar_set_variable_blocking(name, vendor, attr,
233 						    data_size, data);
234 
235 	/*
236 	 * If no _nonblocking variant exists, the ordinary one
237 	 * is assumed to be non-blocking.
238 	 */
239 	setvar = __efivars->ops->set_variable_nonblocking ?:
240 		 __efivars->ops->set_variable;
241 
242 	if (data_size > 0) {
243 		status = check_var_size_nonblocking(attr, data_size +
244 							  ucs2_strsize(name, 1024));
245 		if (status != EFI_SUCCESS)
246 			return status;
247 	}
248 	return setvar(name, vendor, attr, data_size, data);
249 }
250 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
251 
252 /*
253  * efivar_set_variable() - set a variable identified by name/vendor
254  *
255  * Can be called without holding the efivars_lock. Will sleep on obtaining the
256  * lock, or on obtaining other locks that are needed in order to complete the
257  * call.
258  */
259 efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
260 				 u32 attr, unsigned long data_size, void *data)
261 {
262 	efi_status_t status;
263 
264 	if (efivar_lock())
265 		return EFI_ABORTED;
266 
267 	status = efivar_set_variable_blocking(name, vendor, attr, data_size, data);
268 	efivar_unlock();
269 	return status;
270 }
271 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);
272