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