Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/vars.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
* Originally from efivars.c
4
*
5
* Copyright (C) 2001,2003,2004 Dell <[email protected]>
6
* Copyright (C) 2004 Intel Corporation <[email protected]>
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, EFI_VAR_NAME_LEN));
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");
268
269