Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
19static struct efivars *__efivars;
20
21static DEFINE_SEMAPHORE(efivars_lock);
22
23efi_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}
34EXPORT_SYMBOL_NS_GPL(check_var_size, EFIVAR);
35
36efi_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}
47EXPORT_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 */
55struct kobject *efivars_kobject(void)
56{
57 if (!__efivars)
58 return NULL;
59
60 return __efivars->kobject;
61}
62EXPORT_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 */
72int 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}
90EXPORT_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 */
99int 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;
121out:
122 up(&efivars_lock);
123 return rv;
124}
125EXPORT_SYMBOL_GPL(efivars_unregister);
126
127int efivar_supports_writes(void)
128{
129 return __efivars && __efivars->ops->set_variable;
130}
131EXPORT_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 */
137int 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}
147EXPORT_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 */
153int 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}
163EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
164
165/*
166 * efivar_unlock() - release the efivar lock
167 */
168void efivar_unlock(void)
169{
170 up(&efivars_lock);
171}
172EXPORT_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 */
179efi_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}
184EXPORT_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 */
191efi_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}
196EXPORT_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 */
203static efi_status_t
204efivar_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 */
224efi_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}
250EXPORT_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 */
259efi_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}
271EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);