Skip to content

Commit

Permalink
Merge tag 'efi-for-3.9-rc3' into x86/urgent
Browse files Browse the repository at this point in the history
EFI changes for v3.9-rc3,

  * A new config option and efivars module parameter have been added to
    allow the efivars pstore backend to be disabled as we're still seeing
    issues on machines despite adding heuristics to workaround known bugs.
    Distributions now have a way to disable EFI variable pstore code
    completely - from Seth Forshee.

  * A couple of efivars patches to workaround quirky implementations
    of GetNextVariableName() which resulted in machines hanging and
    sysfs files being created with garbage names.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
  • Loading branch information
H. Peter Anvin committed Mar 22, 2013
2 parents f564c24 + e971318 commit 625f6ac
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 46 deletions.
18 changes: 18 additions & 0 deletions drivers/firmware/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ config EFI_VARS
Subsequent efibootmgr releases may be found at:
<http://linux.dell.com/efibootmgr>

config EFI_VARS_PSTORE
bool "Register efivars backend for pstore"
depends on EFI_VARS && PSTORE
default y
help
Say Y here to enable use efivars as a backend to pstore. This
will allow writing console messages, crash dumps, or anything
else supported by pstore to EFI variables.

config EFI_VARS_PSTORE_DEFAULT_DISABLE
bool "Disable using efivars as a pstore backend by default"
depends on EFI_VARS_PSTORE
default n
help
Saying Y here will disable the use of efivars as a storage
backend for pstore by default. This setting can be overridden
using the efivars module's pstore_disable parameter.

config EFI_PCDP
bool "Console device selection via EFI PCDP or HCDP table"
depends on ACPI && EFI && IA64
Expand Down
150 changes: 104 additions & 46 deletions drivers/firmware/efivars.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ MODULE_VERSION(EFIVARS_VERSION);
*/
#define GUID_LEN 36

static bool efivars_pstore_disable =
IS_ENABLED(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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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);

Expand Down

0 comments on commit 625f6ac

Please sign in to comment.