2019-02-02 12:41:15 +03:00
// SPDX-License-Identifier: GPL-2.0+
2005-04-17 02:20:36 +04:00
/*
2013-02-08 19:37:06 +04:00
* Originally from efivars . c
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 2001 , 2003 , 2004 Dell < Matt_Domsch @ dell . com >
* Copyright ( C ) 2004 Intel Corporation < matthew . e . tolentino @ intel . com >
*/
2023-01-19 19:42:52 +03:00
# define pr_fmt(fmt) "efivars: " fmt
2005-04-17 02:20:36 +04:00
# include <linux/types.h>
2022-10-20 00:29:58 +03:00
# include <linux/sizes.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/string.h>
# include <linux/smp.h>
# include <linux/efi.h>
2013-04-30 14:30:24 +04:00
# include <linux/ucs2_string.h>
2005-04-17 02:20:36 +04:00
2013-02-02 19:22:24 +04:00
/* Private pointer to registered efivars */
static struct efivars * __efivars ;
2012-10-05 09:54:56 +04:00
2023-03-29 13:14:42 +03:00
static DEFINE_SEMAPHORE ( efivars_lock , 1 ) ;
2016-07-15 22:36:29 +03:00
2022-10-27 16:52:31 +03:00
static efi_status_t check_var_size ( bool nonblocking , u32 attributes ,
unsigned long size )
2016-02-02 01:06:58 +03:00
{
2018-11-29 20:12:27 +03:00
const struct efivar_operations * fops ;
2022-10-27 16:52:31 +03:00
efi_status_t status ;
2018-11-29 20:12:27 +03:00
fops = __efivars - > ops ;
2016-02-02 01:06:58 +03:00
if ( ! fops - > query_variable_store )
2022-10-27 16:52:31 +03:00
status = EFI_UNSUPPORTED ;
else
status = fops - > query_variable_store ( attributes , size ,
nonblocking ) ;
if ( status = = EFI_UNSUPPORTED )
2022-10-20 00:29:58 +03:00
return ( size < = SZ_64K ) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES ;
2022-10-27 16:52:31 +03:00
return status ;
2013-03-03 04:40:17 +04:00
}
2013-02-04 00:16:40 +04:00
/**
2023-01-17 15:43:09 +03:00
* efivar_is_available - check if efivars is available
2013-02-04 00:16:40 +04:00
*
2023-01-17 15:43:09 +03:00
* @ return true iff evivars is currently registered
2013-02-04 00:16:40 +04:00
*/
2023-01-17 15:43:09 +03:00
bool efivar_is_available ( void )
2013-02-04 00:16:40 +04:00
{
2023-01-17 15:43:09 +03:00
return __efivars ! = NULL ;
2013-02-04 00:16:40 +04:00
}
2023-01-17 15:43:09 +03:00
EXPORT_SYMBOL_GPL ( efivar_is_available ) ;
2013-02-04 00:16:40 +04:00
/**
* efivars_register - register an efivars
* @ efivars : efivars to register
* @ ops : efivars operations
*
* Only a single efivars can be registered at any time .
*/
int efivars_register ( struct efivars * efivars ,
2023-01-17 15:43:09 +03:00
const struct efivar_operations * ops )
2013-02-04 00:16:40 +04:00
{
2023-01-19 19:42:55 +03:00
int rv ;
2016-07-15 22:36:30 +03:00
if ( down_interruptible ( & efivars_lock ) )
return - EINTR ;
2023-01-19 19:42:55 +03:00
if ( __efivars ) {
pr_warn ( " efivars already registered \n " ) ;
rv = - EBUSY ;
goto out ;
}
2013-02-04 00:16:40 +04:00
efivars - > ops = ops ;
__efivars = efivars ;
2016-07-15 22:36:30 +03:00
pr_info ( " Registered efivars operations \n " ) ;
2023-01-19 19:42:55 +03:00
rv = 0 ;
out :
2016-07-15 22:36:30 +03:00
up ( & efivars_lock ) ;
2005-04-17 02:20:36 +04:00
2023-01-19 19:42:55 +03:00
return rv ;
2013-02-04 00:16:40 +04:00
}
EXPORT_SYMBOL_GPL ( efivars_register ) ;
2005-04-17 02:20:36 +04:00
2013-02-04 00:16:40 +04:00
/**
* efivars_unregister - unregister an efivars
* @ efivars : efivars to unregister
*
* The caller must have already removed every entry from the list ,
* failure to do so is an error .
*/
int efivars_unregister ( struct efivars * efivars )
{
int rv ;
2016-07-15 22:36:30 +03:00
if ( down_interruptible ( & efivars_lock ) )
return - EINTR ;
2013-02-04 00:16:40 +04:00
if ( ! __efivars ) {
2023-01-19 19:42:52 +03:00
pr_err ( " efivars not registered \n " ) ;
2013-02-04 00:16:40 +04:00
rv = - EINVAL ;
goto out ;
}
if ( __efivars ! = efivars ) {
rv = - EINVAL ;
goto out ;
}
2016-07-15 22:36:30 +03:00
pr_info ( " Unregistered efivars operations \n " ) ;
2013-02-04 00:16:40 +04:00
__efivars = NULL ;
rv = 0 ;
out :
2016-07-15 22:36:30 +03:00
up ( & efivars_lock ) ;
2013-02-04 00:16:40 +04:00
return rv ;
2011-03-12 04:43:16 +03:00
}
2013-02-04 00:16:40 +04:00
EXPORT_SYMBOL_GPL ( efivars_unregister ) ;
2020-07-08 13:01:57 +03:00
2023-01-17 15:43:10 +03:00
bool efivar_supports_writes ( void )
2020-07-08 13:01:57 +03:00
{
return __efivars & & __efivars - > ops - > set_variable ;
}
EXPORT_SYMBOL_GPL ( efivar_supports_writes ) ;
2022-06-20 14:17:20 +03:00
/*
* efivar_lock ( ) - obtain the efivar lock , wait for it if needed
* @ return 0 on success , error code on failure
*/
int efivar_lock ( void )
{
if ( down_interruptible ( & efivars_lock ) )
return - EINTR ;
if ( ! __efivars - > ops ) {
up ( & efivars_lock ) ;
return - ENODEV ;
}
return 0 ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_lock , EFIVAR ) ;
/*
* efivar_lock ( ) - obtain the efivar lock if it is free
* @ return 0 on success , error code on failure
*/
int efivar_trylock ( void )
{
if ( down_trylock ( & efivars_lock ) )
return - EBUSY ;
if ( ! __efivars - > ops ) {
up ( & efivars_lock ) ;
return - ENODEV ;
}
return 0 ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_trylock , EFIVAR ) ;
/*
* efivar_unlock ( ) - release the efivar lock
*/
void efivar_unlock ( void )
{
up ( & efivars_lock ) ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_unlock , EFIVAR ) ;
/*
* efivar_get_variable ( ) - retrieve a variable identified by name / vendor
*
* Must be called with efivars_lock held .
*/
efi_status_t efivar_get_variable ( efi_char16_t * name , efi_guid_t * vendor ,
u32 * attr , unsigned long * size , void * data )
{
return __efivars - > ops - > get_variable ( name , vendor , attr , size , data ) ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_get_variable , EFIVAR ) ;
/*
* efivar_get_next_variable ( ) - enumerate the next name / vendor pair
*
* Must be called with efivars_lock held .
*/
efi_status_t efivar_get_next_variable ( unsigned long * name_size ,
efi_char16_t * name , efi_guid_t * vendor )
{
return __efivars - > ops - > get_next_variable ( name_size , name , vendor ) ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_get_next_variable , EFIVAR ) ;
/*
* efivar_set_variable_locked ( ) - set a variable identified by name / vendor
*
* Must be called with efivars_lock held . If @ nonblocking is set , it will use
* non - blocking primitives so it is guaranteed not to sleep .
*/
efi_status_t efivar_set_variable_locked ( efi_char16_t * name , efi_guid_t * vendor ,
u32 attr , unsigned long data_size ,
void * data , bool nonblocking )
{
efi_set_variable_t * setvar ;
efi_status_t status ;
2022-10-27 16:52:31 +03:00
if ( data_size > 0 ) {
status = check_var_size ( nonblocking , attr ,
data_size + ucs2_strsize ( name , 1024 ) ) ;
if ( status ! = EFI_SUCCESS )
return status ;
}
2022-06-20 14:17:20 +03:00
/*
* If no _nonblocking variant exists , the ordinary one
* is assumed to be non - blocking .
*/
2022-10-27 16:52:31 +03:00
setvar = __efivars - > ops - > set_variable_nonblocking ;
if ( ! setvar | | ! nonblocking )
setvar = __efivars - > ops - > set_variable ;
2022-06-20 14:17:20 +03:00
return setvar ( name , vendor , attr , data_size , data ) ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_set_variable_locked , EFIVAR ) ;
/*
* efivar_set_variable ( ) - set a variable identified by name / vendor
*
* Can be called without holding the efivars_lock . Will sleep on obtaining the
* lock , or on obtaining other locks that are needed in order to complete the
* call .
*/
efi_status_t efivar_set_variable ( efi_char16_t * name , efi_guid_t * vendor ,
u32 attr , unsigned long data_size , void * data )
{
efi_status_t status ;
if ( efivar_lock ( ) )
return EFI_ABORTED ;
2022-10-27 16:52:31 +03:00
status = efivar_set_variable_locked ( name , vendor , attr , data_size ,
data , false ) ;
2022-06-20 14:17:20 +03:00
efivar_unlock ( ) ;
return status ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_set_variable , EFIVAR ) ;
2023-05-17 18:38:12 +03:00
efi_status_t efivar_query_variable_info ( u32 attr ,
u64 * storage_space ,
u64 * remaining_space ,
u64 * max_variable_size )
{
if ( ! __efivars - > ops - > query_variable_info )
return EFI_UNSUPPORTED ;
return __efivars - > ops - > query_variable_info ( attr , storage_space ,
remaining_space , max_variable_size ) ;
}
EXPORT_SYMBOL_NS_GPL ( efivar_query_variable_info , EFIVAR ) ;