Merge tag 'sh-for-v6.6-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/glaubit...
[platform/kernel/linux-rpi.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
67         if (down_interruptible(&efivars_lock))
68                 return -EINTR;
69
70         if (__efivars) {
71                 pr_warn("efivars already registered\n");
72                 rv = -EBUSY;
73                 goto out;
74         }
75
76         efivars->ops = ops;
77
78         __efivars = efivars;
79
80         pr_info("Registered efivars operations\n");
81         rv = 0;
82 out:
83         up(&efivars_lock);
84
85         return rv;
86 }
87 EXPORT_SYMBOL_GPL(efivars_register);
88
89 /**
90  * efivars_unregister - unregister an efivars
91  * @efivars: efivars to unregister
92  *
93  * The caller must have already removed every entry from the list,
94  * failure to do so is an error.
95  */
96 int efivars_unregister(struct efivars *efivars)
97 {
98         int rv;
99
100         if (down_interruptible(&efivars_lock))
101                 return -EINTR;
102
103         if (!__efivars) {
104                 pr_err("efivars not registered\n");
105                 rv = -EINVAL;
106                 goto out;
107         }
108
109         if (__efivars != efivars) {
110                 rv = -EINVAL;
111                 goto out;
112         }
113
114         pr_info("Unregistered efivars operations\n");
115         __efivars = NULL;
116
117         rv = 0;
118 out:
119         up(&efivars_lock);
120         return rv;
121 }
122 EXPORT_SYMBOL_GPL(efivars_unregister);
123
124 bool efivar_supports_writes(void)
125 {
126         return __efivars && __efivars->ops->set_variable;
127 }
128 EXPORT_SYMBOL_GPL(efivar_supports_writes);
129
130 /*
131  * efivar_lock() - obtain the efivar lock, wait for it if needed
132  * @return 0 on success, error code on failure
133  */
134 int efivar_lock(void)
135 {
136         if (down_interruptible(&efivars_lock))
137                 return -EINTR;
138         if (!__efivars->ops) {
139                 up(&efivars_lock);
140                 return -ENODEV;
141         }
142         return 0;
143 }
144 EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR);
145
146 /*
147  * efivar_lock() - obtain the efivar lock if it is free
148  * @return 0 on success, error code on failure
149  */
150 int efivar_trylock(void)
151 {
152         if (down_trylock(&efivars_lock))
153                  return -EBUSY;
154         if (!__efivars->ops) {
155                 up(&efivars_lock);
156                 return -ENODEV;
157         }
158         return 0;
159 }
160 EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
161
162 /*
163  * efivar_unlock() - release the efivar lock
164  */
165 void efivar_unlock(void)
166 {
167         up(&efivars_lock);
168 }
169 EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR);
170
171 /*
172  * efivar_get_variable() - retrieve a variable identified by name/vendor
173  *
174  * Must be called with efivars_lock held.
175  */
176 efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
177                                  u32 *attr, unsigned long *size, void *data)
178 {
179         return __efivars->ops->get_variable(name, vendor, attr, size, data);
180 }
181 EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR);
182
183 /*
184  * efivar_get_next_variable() - enumerate the next name/vendor pair
185  *
186  * Must be called with efivars_lock held.
187  */
188 efi_status_t efivar_get_next_variable(unsigned long *name_size,
189                                       efi_char16_t *name, efi_guid_t *vendor)
190 {
191         return __efivars->ops->get_next_variable(name_size, name, vendor);
192 }
193 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
194
195 /*
196  * efivar_set_variable_locked() - set a variable identified by name/vendor
197  *
198  * Must be called with efivars_lock held. If @nonblocking is set, it will use
199  * non-blocking primitives so it is guaranteed not to sleep.
200  */
201 efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
202                                         u32 attr, unsigned long data_size,
203                                         void *data, bool nonblocking)
204 {
205         efi_set_variable_t *setvar;
206         efi_status_t status;
207
208         if (data_size > 0) {
209                 status = check_var_size(nonblocking, attr,
210                                         data_size + ucs2_strsize(name, 1024));
211                 if (status != EFI_SUCCESS)
212                         return status;
213         }
214
215         /*
216          * If no _nonblocking variant exists, the ordinary one
217          * is assumed to be non-blocking.
218          */
219         setvar = __efivars->ops->set_variable_nonblocking;
220         if (!setvar || !nonblocking)
221                  setvar = __efivars->ops->set_variable;
222
223         return setvar(name, vendor, attr, data_size, data);
224 }
225 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
226
227 /*
228  * efivar_set_variable() - set a variable identified by name/vendor
229  *
230  * Can be called without holding the efivars_lock. Will sleep on obtaining the
231  * lock, or on obtaining other locks that are needed in order to complete the
232  * call.
233  */
234 efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
235                                  u32 attr, unsigned long data_size, void *data)
236 {
237         efi_status_t status;
238
239         if (efivar_lock())
240                 return EFI_ABORTED;
241
242         status = efivar_set_variable_locked(name, vendor, attr, data_size,
243                                             data, false);
244         efivar_unlock();
245         return status;
246 }
247 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);
248
249 efi_status_t efivar_query_variable_info(u32 attr,
250                                         u64 *storage_space,
251                                         u64 *remaining_space,
252                                         u64 *max_variable_size)
253 {
254         if (!__efivars->ops->query_variable_info)
255                 return EFI_UNSUPPORTED;
256         return __efivars->ops->query_variable_info(attr, storage_space,
257                         remaining_space, max_variable_size);
258 }
259 EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR);