brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 4056ba7 Raw
268 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Originally from efivars.c4 *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: " fmt10 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	else37		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 available46 *47 * @return true iff evivars is currently registered48 */49bool efivar_is_available(void)50{51	return __efivars != NULL;52}53EXPORT_SYMBOL_GPL(efivar_is_available);54 55/**56 * efivars_register - register an efivars57 * @efivars: efivars to register58 * @ops: efivars operations59 *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	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	else84		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;90out:91	up(&efivars_lock);92 93	return rv;94}95EXPORT_SYMBOL_GPL(efivars_register);96 97/**98 * efivars_unregister - unregister an efivars99 * @efivars: efivars to unregister100 *101 * The caller must have already removed every entry from the list,102 * failure to do so is an error.103 */104int 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;126out:127	up(&efivars_lock);128	return rv;129}130EXPORT_SYMBOL_GPL(efivars_unregister);131 132bool efivar_supports_writes(void)133{134	return __efivars && __efivars->ops->set_variable;135}136EXPORT_SYMBOL_GPL(efivar_supports_writes);137 138/*139 * efivar_lock() - obtain the efivar lock, wait for it if needed140 * @return 0 on success, error code on failure141 */142int 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}152EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR);153 154/*155 * efivar_lock() - obtain the efivar lock if it is free156 * @return 0 on success, error code on failure157 */158int 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}168EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);169 170/*171 * efivar_unlock() - release the efivar lock172 */173void efivar_unlock(void)174{175	up(&efivars_lock);176}177EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR);178 179/*180 * efivar_get_variable() - retrieve a variable identified by name/vendor181 *182 * Must be called with efivars_lock held.183 */184efi_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}189EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR);190 191/*192 * efivar_get_next_variable() - enumerate the next name/vendor pair193 *194 * Must be called with efivars_lock held.195 */196efi_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}201EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);202 203/*204 * efivar_set_variable_locked() - set a variable identified by name/vendor205 *206 * Must be called with efivars_lock held. If @nonblocking is set, it will use207 * non-blocking primitives so it is guaranteed not to sleep.208 */209efi_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 one225	 * 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}233EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);234 235/*236 * efivar_set_variable() - set a variable identified by name/vendor237 *238 * Can be called without holding the efivars_lock. Will sleep on obtaining the239 * lock, or on obtaining other locks that are needed in order to complete the240 * call.241 */242efi_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}255EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);256 257efi_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}267EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR);268