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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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