Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Originally from efivars.c
0004  *
0005  * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
0006  * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
0007  */
0008 
0009 #include <linux/types.h>
0010 #include <linux/errno.h>
0011 #include <linux/init.h>
0012 #include <linux/module.h>
0013 #include <linux/string.h>
0014 #include <linux/smp.h>
0015 #include <linux/efi.h>
0016 #include <linux/ucs2_string.h>
0017 
0018 /* Private pointer to registered efivars */
0019 static struct efivars *__efivars;
0020 
0021 static DEFINE_SEMAPHORE(efivars_lock);
0022 
0023 efi_status_t check_var_size(u32 attributes, unsigned long size)
0024 {
0025     const struct efivar_operations *fops;
0026 
0027     fops = __efivars->ops;
0028 
0029     if (!fops->query_variable_store)
0030         return EFI_UNSUPPORTED;
0031 
0032     return fops->query_variable_store(attributes, size, false);
0033 }
0034 EXPORT_SYMBOL_NS_GPL(check_var_size, EFIVAR);
0035 
0036 efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size)
0037 {
0038     const struct efivar_operations *fops;
0039 
0040     fops = __efivars->ops;
0041 
0042     if (!fops->query_variable_store)
0043         return EFI_UNSUPPORTED;
0044 
0045     return fops->query_variable_store(attributes, size, true);
0046 }
0047 EXPORT_SYMBOL_NS_GPL(check_var_size_nonblocking, EFIVAR);
0048 
0049 /**
0050  * efivars_kobject - get the kobject for the registered efivars
0051  *
0052  * If efivars_register() has not been called we return NULL,
0053  * otherwise return the kobject used at registration time.
0054  */
0055 struct kobject *efivars_kobject(void)
0056 {
0057     if (!__efivars)
0058         return NULL;
0059 
0060     return __efivars->kobject;
0061 }
0062 EXPORT_SYMBOL_GPL(efivars_kobject);
0063 
0064 /**
0065  * efivars_register - register an efivars
0066  * @efivars: efivars to register
0067  * @ops: efivars operations
0068  * @kobject: @efivars-specific kobject
0069  *
0070  * Only a single efivars can be registered at any time.
0071  */
0072 int efivars_register(struct efivars *efivars,
0073              const struct efivar_operations *ops,
0074              struct kobject *kobject)
0075 {
0076     if (down_interruptible(&efivars_lock))
0077         return -EINTR;
0078 
0079     efivars->ops = ops;
0080     efivars->kobject = kobject;
0081 
0082     __efivars = efivars;
0083 
0084     pr_info("Registered efivars operations\n");
0085 
0086     up(&efivars_lock);
0087 
0088     return 0;
0089 }
0090 EXPORT_SYMBOL_GPL(efivars_register);
0091 
0092 /**
0093  * efivars_unregister - unregister an efivars
0094  * @efivars: efivars to unregister
0095  *
0096  * The caller must have already removed every entry from the list,
0097  * failure to do so is an error.
0098  */
0099 int efivars_unregister(struct efivars *efivars)
0100 {
0101     int rv;
0102 
0103     if (down_interruptible(&efivars_lock))
0104         return -EINTR;
0105 
0106     if (!__efivars) {
0107         printk(KERN_ERR "efivars not registered\n");
0108         rv = -EINVAL;
0109         goto out;
0110     }
0111 
0112     if (__efivars != efivars) {
0113         rv = -EINVAL;
0114         goto out;
0115     }
0116 
0117     pr_info("Unregistered efivars operations\n");
0118     __efivars = NULL;
0119 
0120     rv = 0;
0121 out:
0122     up(&efivars_lock);
0123     return rv;
0124 }
0125 EXPORT_SYMBOL_GPL(efivars_unregister);
0126 
0127 int efivar_supports_writes(void)
0128 {
0129     return __efivars && __efivars->ops->set_variable;
0130 }
0131 EXPORT_SYMBOL_GPL(efivar_supports_writes);
0132 
0133 /*
0134  * efivar_lock() - obtain the efivar lock, wait for it if needed
0135  * @return 0 on success, error code on failure
0136  */
0137 int efivar_lock(void)
0138 {
0139     if (down_interruptible(&efivars_lock))
0140         return -EINTR;
0141     if (!__efivars->ops) {
0142         up(&efivars_lock);
0143         return -ENODEV;
0144     }
0145     return 0;
0146 }
0147 EXPORT_SYMBOL_NS_GPL(efivar_lock, EFIVAR);
0148 
0149 /*
0150  * efivar_lock() - obtain the efivar lock if it is free
0151  * @return 0 on success, error code on failure
0152  */
0153 int efivar_trylock(void)
0154 {
0155     if (down_trylock(&efivars_lock))
0156          return -EBUSY;
0157     if (!__efivars->ops) {
0158         up(&efivars_lock);
0159         return -ENODEV;
0160     }
0161     return 0;
0162 }
0163 EXPORT_SYMBOL_NS_GPL(efivar_trylock, EFIVAR);
0164 
0165 /*
0166  * efivar_unlock() - release the efivar lock
0167  */
0168 void efivar_unlock(void)
0169 {
0170     up(&efivars_lock);
0171 }
0172 EXPORT_SYMBOL_NS_GPL(efivar_unlock, EFIVAR);
0173 
0174 /*
0175  * efivar_get_variable() - retrieve a variable identified by name/vendor
0176  *
0177  * Must be called with efivars_lock held.
0178  */
0179 efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
0180                  u32 *attr, unsigned long *size, void *data)
0181 {
0182     return __efivars->ops->get_variable(name, vendor, attr, size, data);
0183 }
0184 EXPORT_SYMBOL_NS_GPL(efivar_get_variable, EFIVAR);
0185 
0186 /*
0187  * efivar_get_next_variable() - enumerate the next name/vendor pair
0188  *
0189  * Must be called with efivars_lock held.
0190  */
0191 efi_status_t efivar_get_next_variable(unsigned long *name_size,
0192                       efi_char16_t *name, efi_guid_t *vendor)
0193 {
0194     return __efivars->ops->get_next_variable(name_size, name, vendor);
0195 }
0196 EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR);
0197 
0198 /*
0199  * efivar_set_variable_blocking() - local helper function for set_variable
0200  *
0201  * Must be called with efivars_lock held.
0202  */
0203 static efi_status_t
0204 efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor,
0205                  u32 attr, unsigned long data_size, void *data)
0206 {
0207     efi_status_t status;
0208 
0209     if (data_size > 0) {
0210         status = check_var_size(attr, data_size +
0211                           ucs2_strsize(name, 1024));
0212         if (status != EFI_SUCCESS)
0213             return status;
0214     }
0215     return __efivars->ops->set_variable(name, vendor, attr, data_size, data);
0216 }
0217 
0218 /*
0219  * efivar_set_variable_locked() - set a variable identified by name/vendor
0220  *
0221  * Must be called with efivars_lock held. If @nonblocking is set, it will use
0222  * non-blocking primitives so it is guaranteed not to sleep.
0223  */
0224 efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
0225                     u32 attr, unsigned long data_size,
0226                     void *data, bool nonblocking)
0227 {
0228     efi_set_variable_t *setvar;
0229     efi_status_t status;
0230 
0231     if (!nonblocking)
0232         return efivar_set_variable_blocking(name, vendor, attr,
0233                             data_size, data);
0234 
0235     /*
0236      * If no _nonblocking variant exists, the ordinary one
0237      * is assumed to be non-blocking.
0238      */
0239     setvar = __efivars->ops->set_variable_nonblocking ?:
0240          __efivars->ops->set_variable;
0241 
0242     if (data_size > 0) {
0243         status = check_var_size_nonblocking(attr, data_size +
0244                               ucs2_strsize(name, 1024));
0245         if (status != EFI_SUCCESS)
0246             return status;
0247     }
0248     return setvar(name, vendor, attr, data_size, data);
0249 }
0250 EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR);
0251 
0252 /*
0253  * efivar_set_variable() - set a variable identified by name/vendor
0254  *
0255  * Can be called without holding the efivars_lock. Will sleep on obtaining the
0256  * lock, or on obtaining other locks that are needed in order to complete the
0257  * call.
0258  */
0259 efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
0260                  u32 attr, unsigned long data_size, void *data)
0261 {
0262     efi_status_t status;
0263 
0264     if (efivar_lock())
0265         return EFI_ABORTED;
0266 
0267     status = efivar_set_variable_blocking(name, vendor, attr, data_size, data);
0268     efivar_unlock();
0269     return status;
0270 }
0271 EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);