Merge tag 'efi-next-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi
[sfrench/cifs-2.6.git] / drivers / firmware / efi / vars.c
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 #define pr_fmt(fmt) "efivars: " fmt
10
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>
20
21 /* Private pointer to registered efivars */
22 static struct efivars *__efivars;
23
24 static DEFINE_SEMAPHORE(efivars_lock, 1);
25
26 static efi_status_t check_var_size(bool nonblocking, u32 attributes,
27                                    unsigned long size)
28 {
29         const struct efivar_operations *fops;
30         efi_status_t status;
31
32         fops = __efivars->ops;
33
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;
42 }
43
44 /**
45  * efivar_is_available - check if efivars is available
46  *
47  * @return true iff evivars is currently registered
48  */
49 bool efivar_is_available(void)
50 {
51         return __efivars != NULL;
52 }
53 EXPORT_SYMBOL_GPL(efivar_is_available);
54
55 /**
56  * efivars_register - register an efivars
57  * @efivars: efivars to register
58  * @ops: efivars operations
59  *
60  * Only a single efivars can be registered at any time.
61  */
62 int efivars_register(struct efivars *efivars,
63                      const struct efivar_operations *ops)
64 {
65         int rv;
66         int event;
67
68         if (down_interruptible(&efivars_lock))
69                 return -EINTR;
70
71         if (__efivars) {
72                 pr_warn("efivars already registered\n");
73                 rv = -EBUSY;
74                 goto out;
75         }
76
77         efivars->ops = ops;
78
79         __efivars = efivars;
80
81         if (efivar_supports_writes())
82                 event = EFIVAR_OPS_RDWR;
83         else
84                 event = EFIVAR_OPS_RDONLY;
85
86         blocking_notifier_call_chain(&efivar_ops_nh, event, NULL);
87
88         pr_info("Registered efivars operations\n");
89         rv = 0;
90 out:
91         up(&efivars_lock);
92
93         return rv;
94 }
95 EXPORT_SYMBOL_GPL(efivars_register);
96
97 /**
98  * efivars_unregister - unregister an efivars
99  * @efivars: efivars to unregister
100  *
101  * The caller must have already removed every entry from the list,
102  * failure to do so is an error.
103  */
104 int efivars_unregister(struct efivars *efivars)
105 {
106         int rv;
107
108         if (down_interruptible(&efivars_lock))
109                 return -EINTR;
110
111         if (!__efivars) {
112                 pr_err("efivars not registered\n");
113                 rv = -EINVAL;
114                 goto out;
115         }
116
117         if (__efivars != efivars) {
118                 rv = -EINVAL;
119                 goto out;
120         }
121
122         pr_info("Unregistered efivars operations\n");
123         __efivars = NULL;
124
125         rv = 0;
126 out:
127         up(&efivars_lock);
128         return rv;
129 }
130 EXPORT_SYMBOL_GPL(efivars_unregister);
131
132 bool efivar_supports_writes(void)
133 {
134         return __efivars && __efivars->ops->set_variable;
135 }
136 EXPORT_SYMBOL_GPL(efivar_supports_writes);
137
138 /*
139  * efivar_lock() - obtain the efivar lock, wait for it if needed
140  * @return 0 on success, error code on failure
141  */
142 int efivar_lock(void)
143 {
144         if (down_interruptible(&efivars_lock))
145                 return -EINTR;
146         if (!__efivars->ops) {
147                 up(&efivars_lock);
148                 return -ENODEV;
149         }
150         return 0;
151 }
152 EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR);
153
154 /*
155  * efivar_lock() - obtain the efivar lock if it is free
156  * @return 0 on success, error code on failure
157  */
158 int efivar_trylock(void)
159 {
160         if (down_trylock(&efivars_lock))
161                  return -EBUSY;
162         if (!__efivars->ops) {
163                 up(&efivars_lock);
164                 return -ENODEV;
165         }
166         return 0;
167 }
168 EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
169
170 /*
171  * efivar_unlock() - release the efivar lock
172  */
173 void efivar_unlock(void)
174 {
175         up(&efivars_lock);
176 }
177 EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR);
178
179 /*
180  * efivar_get_variable() - retrieve a variable identified by name/vendor
181  *
182  * Must be called with efivars_lock held.
183  */
184 efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
185                                  u32 *attr, unsigned long *size, void *data)
186 {
187         return __efivars->ops->get_variable(name, vendor, attr, size, data);
188 }
189 EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR);
190
191 /*
192  * efivar_get_next_variable() - enumerate the next name/vendor pair
193  *
194  * Must be called with efivars_lock held.
195  */
196 efi_status_t efivar_get_next_variable(unsigned long *name_size,
197                                       efi_char16_t *name, efi_guid_t *vendor)
198 {
199         return __efivars->ops->get_next_variable(name_size, name, vendor);
200 }
201 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
202
203 /*
204  * efivar_set_variable_locked() - set a variable identified by name/vendor
205  *
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.
208  */
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)
212 {
213         efi_set_variable_t *setvar;
214         efi_status_t status;
215
216         if (data_size > 0) {
217                 status = check_var_size(nonblocking, attr,
218                                         data_size + ucs2_strsize(name, 1024));
219                 if (status != EFI_SUCCESS)
220                         return status;
221         }
222
223         /*
224          * If no _nonblocking variant exists, the ordinary one
225          * is assumed to be non-blocking.
226          */
227         setvar = __efivars->ops->set_variable_nonblocking;
228         if (!setvar || !nonblocking)
229                  setvar = __efivars->ops->set_variable;
230
231         return setvar(name, vendor, attr, data_size, data);
232 }
233 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
234
235 /*
236  * efivar_set_variable() - set a variable identified by name/vendor
237  *
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.
241  */
242 efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
243                                  u32 attr, unsigned long data_size, void *data)
244 {
245         efi_status_t status;
246
247         if (efivar_lock())
248                 return EFI_ABORTED;
249
250         status = efivar_set_variable_locked(name, vendor, attr, data_size,
251                                             data, false);
252         efivar_unlock();
253         return status;
254 }
255 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);
256
257 efi_status_t efivar_query_variable_info(u32 attr,
258                                         u64 *storage_space,
259                                         u64 *remaining_space,
260                                         u64 *max_variable_size)
261 {
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);
266 }
267 EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR);