mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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()
This commit is contained in:
commit
33b73e9b3e
4 changed files with 139 additions and 63 deletions
|
@ -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,
|
||||||
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)
|
||||||
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(¤t_mc_date);
|
current_mc_date_p = (int *)__pa_nodebug(¤t_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)
|
||||||
}
|
}
|
||||||
#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)
|
||||||
#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)
|
||||||
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)
|
||||||
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
|
||||||
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);
|
||||||
*/
|
*/
|
||||||
#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,
|
||||||
|
|
||||||
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 = {
|
||||||
.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,
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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 = {
|
||||||
.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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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,
|
||||||
&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,
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue