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 67 if (down_interruptible(&efivars_lock)) 68 return -EINTR; 69 70 if (__efivars) { 71 pr_warn("efivars already registered\n"); 72 rv = -EBUSY; 73 goto out; 74 } 75 76 efivars->ops = ops; 77 78 __efivars = efivars; 79 80 pr_info("Registered efivars operations\n"); 81 rv = 0; 82out: 83 up(&efivars_lock); 84 85 return rv; 86} 87EXPORT_SYMBOL_GPL(efivars_register); 88 89/** 90 * efivars_unregister - unregister an efivars 91 * @efivars: efivars to unregister 92 * 93 * The caller must have already removed every entry from the list, 94 * failure to do so is an error. 95 */ 96int efivars_unregister(struct efivars *efivars) 97{ 98 int rv; 99 100 if (down_interruptible(&efivars_lock)) 101 return -EINTR; 102 103 if (!__efivars) { 104 pr_err("efivars not registered\n"); 105 rv = -EINVAL; 106 goto out; 107 } 108 109 if (__efivars != efivars) { 110 rv = -EINVAL; 111 goto out; 112 } 113 114 pr_info("Unregistered efivars operations\n"); 115 __efivars = NULL; 116 117 rv = 0; 118out: 119 up(&efivars_lock); 120 return rv; 121} 122EXPORT_SYMBOL_GPL(efivars_unregister); 123 124bool efivar_supports_writes(void) 125{ 126 return __efivars && __efivars->ops->set_variable; 127} 128EXPORT_SYMBOL_GPL(efivar_supports_writes); 129 130/* 131 * efivar_lock() - obtain the efivar lock, wait for it if needed 132 * @return 0 on success, error code on failure 133 */ 134int efivar_lock(void) 135{ 136 if (down_interruptible(&efivars_lock)) 137 return -EINTR; 138 if (!__efivars->ops) { 139 up(&efivars_lock); 140 return -ENODEV; 141 } 142 return 0; 143} 144EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR); 145 146/* 147 * efivar_lock() - obtain the efivar lock if it is free 148 * @return 0 on success, error code on failure 149 */ 150int efivar_trylock(void) 151{ 152 if (down_trylock(&efivars_lock)) 153 return -EBUSY; 154 if (!__efivars->ops) { 155 up(&efivars_lock); 156 return -ENODEV; 157 } 158 return 0; 159} 160EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR); 161 162/* 163 * efivar_unlock() - release the efivar lock 164 */ 165void efivar_unlock(void) 166{ 167 up(&efivars_lock); 168} 169EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR); 170 171/* 172 * efivar_get_variable() - retrieve a variable identified by name/vendor 173 * 174 * Must be called with efivars_lock held. 175 */ 176efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor, 177 u32 *attr, unsigned long *size, void *data) 178{ 179 return __efivars->ops->get_variable(name, vendor, attr, size, data); 180} 181EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR); 182 183/* 184 * efivar_get_next_variable() - enumerate the next name/vendor pair 185 * 186 * Must be called with efivars_lock held. 187 */ 188efi_status_t efivar_get_next_variable(unsigned long *name_size, 189 efi_char16_t *name, efi_guid_t *vendor) 190{ 191 return __efivars->ops->get_next_variable(name_size, name, vendor); 192} 193EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR); 194 195/* 196 * efivar_set_variable_locked() - set a variable identified by name/vendor 197 * 198 * Must be called with efivars_lock held. If @nonblocking is set, it will use 199 * non-blocking primitives so it is guaranteed not to sleep. 200 */ 201efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor, 202 u32 attr, unsigned long data_size, 203 void *data, bool nonblocking) 204{ 205 efi_set_variable_t *setvar; 206 efi_status_t status; 207 208 if (data_size > 0) { 209 status = check_var_size(nonblocking, attr, 210 data_size + ucs2_strsize(name, 1024)); 211 if (status != EFI_SUCCESS) 212 return status; 213 } 214 215 /* 216 * If no _nonblocking variant exists, the ordinary one 217 * is assumed to be non-blocking. 218 */ 219 setvar = __efivars->ops->set_variable_nonblocking; 220 if (!setvar || !nonblocking) 221 setvar = __efivars->ops->set_variable; 222 223 return setvar(name, vendor, attr, data_size, data); 224} 225EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR); 226 227/* 228 * efivar_set_variable() - set a variable identified by name/vendor 229 * 230 * Can be called without holding the efivars_lock. Will sleep on obtaining the 231 * lock, or on obtaining other locks that are needed in order to complete the 232 * call. 233 */ 234efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, 235 u32 attr, unsigned long data_size, void *data) 236{ 237 efi_status_t status; 238 239 if (efivar_lock()) 240 return EFI_ABORTED; 241 242 status = efivar_set_variable_locked(name, vendor, attr, data_size, 243 data, false); 244 efivar_unlock(); 245 return status; 246} 247EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR); 248 249efi_status_t efivar_query_variable_info(u32 attr, 250 u64 *storage_space, 251 u64 *remaining_space, 252 u64 *max_variable_size) 253{ 254 if (!__efivars->ops->query_variable_info) 255 return EFI_UNSUPPORTED; 256 return __efivars->ops->query_variable_info(attr, storage_space, 257 remaining_space, max_variable_size); 258} 259EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR); 260