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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJRSwDuAAoJEC84WcCNIz1Vf40QAKS9bjjvOZfuxJDM3yp88/rF txMKHofIt6RC3ckdDoBoz5TdJzZ0pk+885+tXfH+DpKnWxVgbh0IgIHUkqsGdoAc ickVsq4PzjI/YygldmTXJSA3BpjM5ENV9SOvMBqQXTj08oiNd5bTIBJrKjNYMk/1 5Ah/O+grSYwzaAo2Pfhd2VZhv0E5FpUgIsei7OP6J3gK8K2QcdQL1Q4lIZ6g3JR7 vPx24p1ZFbitR5iU6vVr7/8XxE6NP99ygQ8Md6jcLNA7FM6nzjidc2CZ6ezuk7ot uTYgl03ppnV8UOcKUt5EMww0LSBMM3/hEwNoRe7VVgJT734Ytms30w4AtLRCXxmB dAjMF4a50GSuVSzLYTv+NCq3tYIhtPC6UsxpAzdglk7d4WIA2TNmlJLEtiaqZht7 Uus1wrHv90bc2Uf3uN+0auT1XQqG2RnWxxqPIQicHsJOGVSTRWXCU5hb1wMsCry8 /ZO2Jg682uv/T5r9vWxX58evP/C3IU4nwcTTEhID8SKWEjxOsLY90xCvzd9aZavi 8pw/z2k6X0Ls1VxOnbV+GkGKlp9S+cg5h1E2kzi9Y01AnaqONGT13CnSuKDVb/cd +Qm2CodJynNg2QGXsy61XxthXP3qzLJ757Oo9w7JlyQ2wZghr5TnCVslkroMQHYb Jkx6//DRsInXj3I3Kegn =UPpR -----END PGP SIGNATURE----- Merge tag 'efi-for-3.9-rc3' into x86/urgent 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>
This commit is contained in:
commit
625f6ac9cf
2 changed files with 122 additions and 46 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue