diff options
-rw-r--r-- | drivers/firmware/efivars.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 1e9d9b9d7a12..d64661fda4fd 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -170,6 +170,7 @@ efivar_create_sysfs_entry(struct efivars *efivars, | |||
170 | 170 | ||
171 | static void efivar_update_sysfs_entries(struct work_struct *); | 171 | static void efivar_update_sysfs_entries(struct work_struct *); |
172 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); | 172 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); |
173 | static bool efivar_wq_enabled = true; | ||
173 | 174 | ||
174 | /* Return the number of unicode characters in data */ | 175 | /* Return the number of unicode characters in data */ |
175 | static unsigned long | 176 | static unsigned long |
@@ -1444,7 +1445,7 @@ static int efi_pstore_write(enum pstore_type_id type, | |||
1444 | 1445 | ||
1445 | spin_unlock_irqrestore(&efivars->lock, flags); | 1446 | spin_unlock_irqrestore(&efivars->lock, flags); |
1446 | 1447 | ||
1447 | if (reason == KMSG_DUMP_OOPS) | 1448 | if (reason == KMSG_DUMP_OOPS && efivar_wq_enabled) |
1448 | schedule_work(&efivar_work); | 1449 | schedule_work(&efivar_work); |
1449 | 1450 | ||
1450 | *id = part; | 1451 | *id = part; |
@@ -1975,6 +1976,35 @@ void unregister_efivars(struct efivars *efivars) | |||
1975 | } | 1976 | } |
1976 | EXPORT_SYMBOL_GPL(unregister_efivars); | 1977 | EXPORT_SYMBOL_GPL(unregister_efivars); |
1977 | 1978 | ||
1979 | /* | ||
1980 | * Print a warning when duplicate EFI variables are encountered and | ||
1981 | * disable the sysfs workqueue since the firmware is buggy. | ||
1982 | */ | ||
1983 | static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, | ||
1984 | unsigned long len16) | ||
1985 | { | ||
1986 | size_t i, len8 = len16 / sizeof(efi_char16_t); | ||
1987 | char *s8; | ||
1988 | |||
1989 | /* | ||
1990 | * Disable the workqueue since the algorithm it uses for | ||
1991 | * detecting new variables won't work with this buggy | ||
1992 | * implementation of GetNextVariableName(). | ||
1993 | */ | ||
1994 | efivar_wq_enabled = false; | ||
1995 | |||
1996 | s8 = kzalloc(len8, GFP_KERNEL); | ||
1997 | if (!s8) | ||
1998 | return; | ||
1999 | |||
2000 | for (i = 0; i < len8; i++) | ||
2001 | s8[i] = s16[i]; | ||
2002 | |||
2003 | printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", | ||
2004 | s8, vendor_guid); | ||
2005 | kfree(s8); | ||
2006 | } | ||
2007 | |||
1978 | int register_efivars(struct efivars *efivars, | 2008 | int register_efivars(struct efivars *efivars, |
1979 | const struct efivar_operations *ops, | 2009 | const struct efivar_operations *ops, |
1980 | struct kobject *parent_kobj) | 2010 | struct kobject *parent_kobj) |
@@ -2025,6 +2055,22 @@ int register_efivars(struct efivars *efivars, | |||
2025 | case EFI_SUCCESS: | 2055 | case EFI_SUCCESS: |
2026 | variable_name_size = var_name_strnsize(variable_name, | 2056 | variable_name_size = var_name_strnsize(variable_name, |
2027 | variable_name_size); | 2057 | variable_name_size); |
2058 | |||
2059 | /* | ||
2060 | * Some firmware implementations return the | ||
2061 | * same variable name on multiple calls to | ||
2062 | * get_next_variable(). Terminate the loop | ||
2063 | * immediately as there is no guarantee that | ||
2064 | * we'll ever see a different variable name, | ||
2065 | * and may end up looping here forever. | ||
2066 | */ | ||
2067 | if (variable_is_present(variable_name, &vendor_guid)) { | ||
2068 | dup_variable_bug(variable_name, &vendor_guid, | ||
2069 | variable_name_size); | ||
2070 | status = EFI_NOT_FOUND; | ||
2071 | break; | ||
2072 | } | ||
2073 | |||
2028 | efivar_create_sysfs_entry(efivars, | 2074 | efivar_create_sysfs_entry(efivars, |
2029 | variable_name_size, | 2075 | variable_name_size, |
2030 | variable_name, | 2076 | variable_name, |