@ -103,6 +103,11 @@ MODULE_VERSION(EFIVARS_VERSION);
*/
# define GUID_LEN 36
static bool efivars_pstore_disable =
IS_ENABLED ( CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE ) ;
module_param_named ( pstore_disable , efivars_pstore_disable , bool , 0644 ) ;
/*
* The maximum size of VariableName + Data = 1024
* Therefore , it ' s reasonable to save that much
@ -165,6 +170,7 @@ efivar_create_sysfs_entry(struct efivars *efivars,
static void efivar_update_sysfs_entries ( struct work_struct * ) ;
static DECLARE_WORK ( efivar_work , efivar_update_sysfs_entries ) ;
static bool efivar_wq_enabled = true ;
/* Return the number of unicode characters in data */
static unsigned long
@ -1309,9 +1315,7 @@ static const struct inode_operations efivarfs_dir_inode_operations = {
. create = efivarfs_create ,
} ;
static struct pstore_info efi_pstore_info ;
# ifdef CONFIG_PSTORE
# ifdef CONFIG_EFI_VARS_PSTORE
static int efi_pstore_open ( struct pstore_info * psi )
{
@ -1441,7 +1445,7 @@ static int efi_pstore_write(enum pstore_type_id type,
spin_unlock_irqrestore ( & efivars - > lock , flags ) ;
if ( reason = = KMSG_DUMP_OOPS )
if ( reason = = KMSG_DUMP_OOPS & & efivar_wq_enabled )
schedule_work ( & efivar_work ) ;
* id = part ;
@ -1514,38 +1518,6 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
return 0 ;
}
# else
static int efi_pstore_open ( struct pstore_info * psi )
{
return 0 ;
}
static int efi_pstore_close ( struct pstore_info * psi )
{
return 0 ;
}
static ssize_t efi_pstore_read ( u64 * id , enum pstore_type_id * type , int * count ,
struct timespec * timespec ,
char * * buf , struct pstore_info * psi )
{
return - 1 ;
}
static int efi_pstore_write ( enum pstore_type_id type ,
enum kmsg_dump_reason reason , u64 * id ,
unsigned int part , int count , size_t size ,
struct pstore_info * psi )
{
return 0 ;
}
static int efi_pstore_erase ( enum pstore_type_id type , u64 id , int count ,
struct timespec time , struct pstore_info * psi )
{
return 0 ;
}
# endif
static struct pstore_info efi_pstore_info = {
. owner = THIS_MODULE ,
@ -1557,6 +1529,24 @@ static struct pstore_info efi_pstore_info = {
. erase = efi_pstore_erase ,
} ;
static void efivar_pstore_register ( struct efivars * efivars )
{
efivars - > efi_pstore_info = efi_pstore_info ;
efivars - > efi_pstore_info . buf = kmalloc ( 4096 , GFP_KERNEL ) ;
if ( efivars - > efi_pstore_info . buf ) {
efivars - > efi_pstore_info . bufsize = 1024 ;
efivars - > efi_pstore_info . data = efivars ;
spin_lock_init ( & efivars - > efi_pstore_info . buf_lock ) ;
pstore_register ( & efivars - > efi_pstore_info ) ;
}
}
# else
static void efivar_pstore_register ( struct efivars * efivars )
{
return ;
}
# endif
static ssize_t efivar_create ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t pos , size_t count )
@ -1716,6 +1706,31 @@ static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)
return found ;
}
/*
* Returns the size of variable_name , in bytes , including the
* terminating NULL character , or variable_name_size if no NULL
* character is found among the first variable_name_size bytes .
*/
static unsigned long var_name_strnsize ( efi_char16_t * variable_name ,
unsigned long variable_name_size )
{
unsigned long len ;
efi_char16_t c ;
/*
* The variable name is , by definition , a NULL - terminated
* string , so make absolutely sure that variable_name_size is
* the value we expect it to be . If not , return the real size .
*/
for ( len = 2 ; len < = variable_name_size ; len + = sizeof ( c ) ) {
c = variable_name [ ( len / sizeof ( c ) ) - 1 ] ;
if ( ! c )
break ;
}
return min ( len , variable_name_size ) ;
}
static void efivar_update_sysfs_entries ( struct work_struct * work )
{
struct efivars * efivars = & __efivars ;
@ -1756,10 +1771,13 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
if ( ! found ) {
kfree ( variable_name ) ;
break ;
} else
} else {
variable_name_size = var_name_strnsize ( variable_name ,
variable_name_size ) ;
efivar_create_sysfs_entry ( efivars ,
variable_name_size ,
variable_name , & vendor ) ;
}
}
}
@ -1958,6 +1976,35 @@ void unregister_efivars(struct efivars *efivars)
}
EXPORT_SYMBOL_GPL ( unregister_efivars ) ;
/*
* Print a warning when duplicate EFI variables are encountered and
* disable the sysfs workqueue since the firmware is buggy .
*/
static void dup_variable_bug ( efi_char16_t * s16 , efi_guid_t * vendor_guid ,
unsigned long len16 )
{
size_t i , len8 = len16 / sizeof ( efi_char16_t ) ;
char * s8 ;
/*
* Disable the workqueue since the algorithm it uses for
* detecting new variables won ' t work with this buggy
* implementation of GetNextVariableName ( ) .
*/
efivar_wq_enabled = false ;
s8 = kzalloc ( len8 , GFP_KERNEL ) ;
if ( ! s8 )
return ;
for ( i = 0 ; i < len8 ; i + + )
s8 [ i ] = s16 [ i ] ;
printk ( KERN_WARNING " efivars: duplicate variable: %s-%pUl \n " ,
s8 , vendor_guid ) ;
kfree ( s8 ) ;
}
int register_efivars ( struct efivars * efivars ,
const struct efivar_operations * ops ,
struct kobject * parent_kobj )
@ -2006,6 +2053,24 @@ int register_efivars(struct efivars *efivars,
& vendor_guid ) ;
switch ( status ) {
case EFI_SUCCESS :
variable_name_size = var_name_strnsize ( variable_name ,
variable_name_size ) ;
/*
* Some firmware implementations return the
* same variable name on multiple calls to
* get_next_variable ( ) . Terminate the loop
* immediately as there is no guarantee that
* we ' ll ever see a different variable name ,
* and may end up looping here forever .
*/
if ( variable_is_present ( variable_name , & vendor_guid ) ) {
dup_variable_bug ( variable_name , & vendor_guid ,
variable_name_size ) ;
status = EFI_NOT_FOUND ;
break ;
}
efivar_create_sysfs_entry ( efivars ,
variable_name_size ,
variable_name ,
@ -2025,15 +2090,8 @@ int register_efivars(struct efivars *efivars,
if ( error )
unregister_efivars ( efivars ) ;
efivars - > efi_pstore_info = efi_pstore_info ;
efivars - > efi_pstore_info . buf = kmalloc ( 4096 , GFP_KERNEL ) ;
if ( efivars - > efi_pstore_info . buf ) {
efivars - > efi_pstore_info . bufsize = 1024 ;
efivars - > efi_pstore_info . data = efivars ;
spin_lock_init ( & efivars - > efi_pstore_info . buf_lock ) ;
pstore_register ( & efivars - > efi_pstore_info ) ;
}
if ( ! efivars_pstore_disable )
efivar_pstore_register ( efivars ) ;
register_filesystem ( & efivarfs_type ) ;