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, 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