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 */
20 static struct efivars *__efivars;
21 
22 static DEFINE_SEMAPHORE(efivars_lock);
23 
check_var_size(bool nonblocking,u32 attributes,unsigned long size)24 static 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  */
efivars_kobject(void)48 struct kobject *efivars_kobject(void)
49 {
50 	if (!__efivars)
51 		return NULL;
52 
53 	return __efivars->kobject;
54 }
55 EXPORT_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  */
efivars_register(struct efivars * efivars,const struct efivar_operations * ops,struct kobject * kobject)65 int 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 }
83 EXPORT_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  */
efivars_unregister(struct efivars * efivars)92 int 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;
114 out:
115 	up(&efivars_lock);
116 	return rv;
117 }
118 EXPORT_SYMBOL_GPL(efivars_unregister);
119 
efivar_supports_writes(void)120 int efivar_supports_writes(void)
121 {
122 	return __efivars && __efivars->ops->set_variable;
123 }
124 EXPORT_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  */
efivar_lock(void)130 int 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 }
140 EXPORT_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  */
efivar_trylock(void)146 int 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 }
156 EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
157 
158 /*
159  * efivar_unlock() - release the efivar lock
160  */
efivar_unlock(void)161 void efivar_unlock(void)
162 {
163 	up(&efivars_lock);
164 }
165 EXPORT_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  */
efivar_get_variable(efi_char16_t * name,efi_guid_t * vendor,u32 * attr,unsigned long * size,void * data)172 efi_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 }
177 EXPORT_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  */
efivar_get_next_variable(unsigned long * name_size,efi_char16_t * name,efi_guid_t * vendor)184 efi_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 }
189 EXPORT_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  */
efivar_set_variable_locked(efi_char16_t * name,efi_guid_t * vendor,u32 attr,unsigned long data_size,void * data,bool nonblocking)197 efi_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 }
221 EXPORT_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  */
efivar_set_variable(efi_char16_t * name,efi_guid_t * vendor,u32 attr,unsigned long data_size,void * data)230 efi_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 }
243 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);
244