Commit 33b73e9b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 fixes from Peter Anvin:
 "A collection of minor fixes, more EFI variables paranoia
  (anti-bricking) plus the ability to disable the pstore either as a
  runtime default or completely, due to bricking concerns."

* 'x86/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efivars: Fix check for CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE
  x86, microcode_intel_early: Mark apply_microcode_early() as cpuinit
  efivars: Handle duplicate names from get_next_variable()
  efivars: explicitly calculate length of VariableName
  efivars: Add module parameter to disable use as a pstore backend
  efivars: Allow disabling use as a pstore backend
  x86-32, microcode_intel_early: Fix crash with CONFIG_DEBUG_VIRTUAL
  x86-64: Fix the failure case in copy_user_handle_tail()
parents 8bb96604 b9726d9d
...@@ -90,13 +90,13 @@ microcode_phys(struct microcode_intel **mc_saved_tmp, ...@@ -90,13 +90,13 @@ microcode_phys(struct microcode_intel **mc_saved_tmp,
struct microcode_intel ***mc_saved; struct microcode_intel ***mc_saved;
mc_saved = (struct microcode_intel ***) mc_saved = (struct microcode_intel ***)
__pa_symbol(&mc_saved_data->mc_saved); __pa_nodebug(&mc_saved_data->mc_saved);
for (i = 0; i < mc_saved_data->mc_saved_count; i++) { for (i = 0; i < mc_saved_data->mc_saved_count; i++) {
struct microcode_intel *p; struct microcode_intel *p;
p = *(struct microcode_intel **) p = *(struct microcode_intel **)
__pa(mc_saved_data->mc_saved + i); __pa_nodebug(mc_saved_data->mc_saved + i);
mc_saved_tmp[i] = (struct microcode_intel *)__pa(p); mc_saved_tmp[i] = (struct microcode_intel *)__pa_nodebug(p);
} }
} }
#endif #endif
...@@ -562,7 +562,7 @@ scan_microcode(unsigned long start, unsigned long end, ...@@ -562,7 +562,7 @@ scan_microcode(unsigned long start, unsigned long end,
struct cpio_data cd; struct cpio_data cd;
long offset = 0; long offset = 0;
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
char *p = (char *)__pa_symbol(ucode_name); char *p = (char *)__pa_nodebug(ucode_name);
#else #else
char *p = ucode_name; char *p = ucode_name;
#endif #endif
...@@ -630,8 +630,8 @@ static void __cpuinit print_ucode(struct ucode_cpu_info *uci) ...@@ -630,8 +630,8 @@ static void __cpuinit print_ucode(struct ucode_cpu_info *uci)
if (mc_intel == NULL) if (mc_intel == NULL)
return; return;
delay_ucode_info_p = (int *)__pa_symbol(&delay_ucode_info); delay_ucode_info_p = (int *)__pa_nodebug(&delay_ucode_info);
current_mc_date_p = (int *)__pa_symbol(&current_mc_date); current_mc_date_p = (int *)__pa_nodebug(&current_mc_date);
*delay_ucode_info_p = 1; *delay_ucode_info_p = 1;
*current_mc_date_p = mc_intel->hdr.date; *current_mc_date_p = mc_intel->hdr.date;
...@@ -659,8 +659,8 @@ static inline void __cpuinit print_ucode(struct ucode_cpu_info *uci) ...@@ -659,8 +659,8 @@ static inline void __cpuinit print_ucode(struct ucode_cpu_info *uci)
} }
#endif #endif
static int apply_microcode_early(struct mc_saved_data *mc_saved_data, static int __cpuinit apply_microcode_early(struct mc_saved_data *mc_saved_data,
struct ucode_cpu_info *uci) struct ucode_cpu_info *uci)
{ {
struct microcode_intel *mc_intel; struct microcode_intel *mc_intel;
unsigned int val[2]; unsigned int val[2];
...@@ -741,15 +741,15 @@ load_ucode_intel_bsp(void) ...@@ -741,15 +741,15 @@ load_ucode_intel_bsp(void)
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
struct boot_params *boot_params_p; struct boot_params *boot_params_p;
boot_params_p = (struct boot_params *)__pa_symbol(&boot_params); boot_params_p = (struct boot_params *)__pa_nodebug(&boot_params);
ramdisk_image = boot_params_p->hdr.ramdisk_image; ramdisk_image = boot_params_p->hdr.ramdisk_image;
ramdisk_size = boot_params_p->hdr.ramdisk_size; ramdisk_size = boot_params_p->hdr.ramdisk_size;
initrd_start_early = ramdisk_image; initrd_start_early = ramdisk_image;
initrd_end_early = initrd_start_early + ramdisk_size; initrd_end_early = initrd_start_early + ramdisk_size;
_load_ucode_intel_bsp( _load_ucode_intel_bsp(
(struct mc_saved_data *)__pa_symbol(&mc_saved_data), (struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
(unsigned long *)__pa_symbol(&mc_saved_in_initrd), (unsigned long *)__pa_nodebug(&mc_saved_in_initrd),
initrd_start_early, initrd_end_early, &uci); initrd_start_early, initrd_end_early, &uci);
#else #else
ramdisk_image = boot_params.hdr.ramdisk_image; ramdisk_image = boot_params.hdr.ramdisk_image;
...@@ -772,10 +772,10 @@ void __cpuinit load_ucode_intel_ap(void) ...@@ -772,10 +772,10 @@ void __cpuinit load_ucode_intel_ap(void)
unsigned long *initrd_start_p; unsigned long *initrd_start_p;
mc_saved_in_initrd_p = mc_saved_in_initrd_p =
(unsigned long *)__pa_symbol(mc_saved_in_initrd); (unsigned long *)__pa_nodebug(mc_saved_in_initrd);
mc_saved_data_p = (struct mc_saved_data *)__pa_symbol(&mc_saved_data); mc_saved_data_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
initrd_start_p = (unsigned long *)__pa_symbol(&initrd_start); initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
initrd_start_addr = (unsigned long)__pa_symbol(*initrd_start_p); initrd_start_addr = (unsigned long)__pa_nodebug(*initrd_start_p);
#else #else
mc_saved_data_p = &mc_saved_data; mc_saved_data_p = &mc_saved_data;
mc_saved_in_initrd_p = mc_saved_in_initrd; mc_saved_in_initrd_p = mc_saved_in_initrd;
......
...@@ -74,10 +74,10 @@ copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest) ...@@ -74,10 +74,10 @@ copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest)
char c; char c;
unsigned zero_len; unsigned zero_len;
for (; len; --len) { for (; len; --len, to++) {
if (__get_user_nocheck(c, from++, sizeof(char))) if (__get_user_nocheck(c, from++, sizeof(char)))
break; break;
if (__put_user_nocheck(c, to++, sizeof(char))) if (__put_user_nocheck(c, to, sizeof(char)))
break; break;
} }
......
...@@ -53,6 +53,24 @@ config EFI_VARS ...@@ -53,6 +53,24 @@ config EFI_VARS
Subsequent efibootmgr releases may be found at: Subsequent efibootmgr releases may be found at:
<http://linux.dell.com/efibootmgr> <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 config EFI_PCDP
bool "Console device selection via EFI PCDP or HCDP table" bool "Console device selection via EFI PCDP or HCDP table"
depends on ACPI && EFI && IA64 depends on ACPI && EFI && IA64
......
...@@ -103,6 +103,11 @@ MODULE_VERSION(EFIVARS_VERSION); ...@@ -103,6 +103,11 @@ MODULE_VERSION(EFIVARS_VERSION);
*/ */
#define GUID_LEN 36 #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 * The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much * Therefore, it's reasonable to save that much
...@@ -165,6 +170,7 @@ efivar_create_sysfs_entry(struct efivars *efivars, ...@@ -165,6 +170,7 @@ efivar_create_sysfs_entry(struct efivars *efivars,
static void efivar_update_sysfs_entries(struct work_struct *); static void efivar_update_sysfs_entries(struct work_struct *);
static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries);
static bool efivar_wq_enabled = true;
/* Return the number of unicode characters in data */ /* Return the number of unicode characters in data */
static unsigned long static unsigned long
...@@ -1309,9 +1315,7 @@ static const struct inode_operations efivarfs_dir_inode_operations = { ...@@ -1309,9 +1315,7 @@ static const struct inode_operations efivarfs_dir_inode_operations = {
.create = efivarfs_create, .create = efivarfs_create,
}; };
static struct pstore_info efi_pstore_info; #ifdef CONFIG_EFI_VARS_PSTORE
#ifdef CONFIG_PSTORE
static int efi_pstore_open(struct pstore_info *psi) static int efi_pstore_open(struct pstore_info *psi)
{ {
...@@ -1441,7 +1445,7 @@ static int efi_pstore_write(enum pstore_type_id type, ...@@ -1441,7 +1445,7 @@ static int efi_pstore_write(enum pstore_type_id type,
spin_unlock_irqrestore(&efivars->lock, flags); spin_unlock_irqrestore(&efivars->lock, flags);
if (reason == KMSG_DUMP_OOPS) if (reason == KMSG_DUMP_OOPS && efivar_wq_enabled)
schedule_work(&efivar_work); schedule_work(&efivar_work);
*id = part; *id = part;
...@@ -1514,38 +1518,6 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -1514,38 +1518,6 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
return 0; 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 = { static struct pstore_info efi_pstore_info = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -1557,6 +1529,24 @@ static struct pstore_info efi_pstore_info = { ...@@ -1557,6 +1529,24 @@ static struct pstore_info efi_pstore_info = {
.erase = efi_pstore_erase, .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, static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count) 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) ...@@ -1716,6 +1706,31 @@ static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor)
return found; 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) static void efivar_update_sysfs_entries(struct work_struct *work)
{ {
struct efivars *efivars = &__efivars; struct efivars *efivars = &__efivars;
...@@ -1756,10 +1771,13 @@ static void efivar_update_sysfs_entries(struct work_struct *work) ...@@ -1756,10 +1771,13 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
if (!found) { if (!found) {
kfree(variable_name); kfree(variable_name);
break; break;
} else } else {
variable_name_size = var_name_strnsize(variable_name,
variable_name_size);
efivar_create_sysfs_entry(efivars, efivar_create_sysfs_entry(efivars,
variable_name_size, variable_name_size,
variable_name, &vendor); variable_name, &vendor);
}
} }
} }
...@@ -1958,6 +1976,35 @@ void unregister_efivars(struct efivars *efivars) ...@@ -1958,6 +1976,35 @@ void unregister_efivars(struct efivars *efivars)
} }
EXPORT_SYMBOL_GPL(unregister_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, int register_efivars(struct efivars *efivars,
const struct efivar_operations *ops, const struct efivar_operations *ops,
struct kobject *parent_kobj) struct kobject *parent_kobj)
...@@ -2006,6 +2053,24 @@ int register_efivars(struct efivars *efivars, ...@@ -2006,6 +2053,24 @@ int register_efivars(struct efivars *efivars,
&vendor_guid); &vendor_guid);
switch (status) { switch (status) {
case EFI_SUCCESS: 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, efivar_create_sysfs_entry(efivars,
variable_name_size, variable_name_size,
variable_name, variable_name,
...@@ -2025,15 +2090,8 @@ int register_efivars(struct efivars *efivars, ...@@ -2025,15 +2090,8 @@ int register_efivars(struct efivars *efivars,
if (error) if (error)
unregister_efivars(efivars); unregister_efivars(efivars);
efivars->efi_pstore_info = efi_pstore_info; if (!efivars_pstore_disable)
efivar_pstore_register(efivars);
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);
}
register_filesystem(&efivarfs_type); register_filesystem(&efivarfs_type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment