x86: major refactoring
Refactored code by introducing a two-module solution. There is one general module in which vendor specific modules can hook into. However, that is exclusive, there is only one vendor specific module allowed at a time. A CPU vendor check makes sure only the correct module for the underlying system gets called. Functinally in terms of patch loading itself there are no changes. This refactoring provides a basis for future implementations of other vendors' patch loaders. Signed-off-by: Peter Oruba <peter.oruba@amd.com> Cc: Tigran Aivazian <tigran@aivazian.fsnet.co.uk> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
26bf7a48c3
commit
8d86f390d9
|
@ -782,7 +782,7 @@ config X86_REBOOTFIXUPS
|
||||||
Say N otherwise.
|
Say N otherwise.
|
||||||
|
|
||||||
config MICROCODE
|
config MICROCODE
|
||||||
tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support"
|
tristate "/dev/cpu/microcode - microcode support"
|
||||||
select FW_LOADER
|
select FW_LOADER
|
||||||
---help---
|
---help---
|
||||||
If you say Y here, you will be able to update the microcode on
|
If you say Y here, you will be able to update the microcode on
|
||||||
|
@ -791,14 +791,29 @@ config MICROCODE
|
||||||
actual microcode binary data itself which is not shipped with the
|
actual microcode binary data itself which is not shipped with the
|
||||||
Linux kernel.
|
Linux kernel.
|
||||||
|
|
||||||
For latest news and information on obtaining all the required
|
This option selects the general module only, you need to select
|
||||||
ingredients for this driver, check:
|
at least one vendor specific module as well.
|
||||||
<http://www.urbanmyth.org/microcode/>.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called microcode.
|
module will be called microcode.
|
||||||
|
|
||||||
config MICROCODE_OLD_INTERFACE
|
config MICROCODE_INTEL
|
||||||
|
tristate "Intel microcode patch loading support"
|
||||||
|
depends on MICROCODE
|
||||||
|
default MICROCODE
|
||||||
|
select FW_LOADER
|
||||||
|
--help---
|
||||||
|
This options enables microcode patch loading support for Intel
|
||||||
|
processors.
|
||||||
|
|
||||||
|
For latest news and information on obtaining all the required
|
||||||
|
Intel ingredients for this driver, check:
|
||||||
|
<http://www.urbanmyth.org/microcode/>.
|
||||||
|
|
||||||
|
This driver is only available as a module: the module
|
||||||
|
will be called microcode_intel.
|
||||||
|
|
||||||
|
config MICROCODE_OLD_INTERFACE
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on MICROCODE
|
depends on MICROCODE
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,8 @@ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o
|
||||||
obj-$(CONFIG_MCA) += mca_32.o
|
obj-$(CONFIG_MCA) += mca_32.o
|
||||||
obj-$(CONFIG_X86_MSR) += msr.o
|
obj-$(CONFIG_X86_MSR) += msr.o
|
||||||
obj-$(CONFIG_X86_CPUID) += cpuid.o
|
obj-$(CONFIG_X86_CPUID) += cpuid.o
|
||||||
obj-$(CONFIG_MICROCODE) += ucode.o
|
obj-$(CONFIG_MICROCODE) += microcode.o
|
||||||
ucode-objs := microcode.o microcode_intel.o
|
obj-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
|
||||||
obj-$(CONFIG_PCI) += early-quirks.o
|
obj-$(CONFIG_PCI) += early-quirks.o
|
||||||
apm-y := apm_32.o
|
apm-y := apm_32.o
|
||||||
obj-$(CONFIG_APM) += apm.o
|
obj-$(CONFIG_APM) += apm.o
|
||||||
|
|
|
@ -99,25 +99,22 @@ MODULE_DESCRIPTION("Microcode Update Driver");
|
||||||
MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
|
MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
#define MICROCODE_VERSION "1.14a"
|
#define MICROCODE_VERSION "2.00"
|
||||||
|
|
||||||
|
struct microcode_ops *microcode_ops;
|
||||||
|
|
||||||
/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
|
/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
|
||||||
DEFINE_MUTEX(microcode_mutex);
|
static DEFINE_MUTEX(microcode_mutex);
|
||||||
|
EXPORT_SYMBOL_GPL(microcode_mutex);
|
||||||
|
|
||||||
struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
|
static struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
|
||||||
|
EXPORT_SYMBOL_GPL(ucode_cpu_info);
|
||||||
extern long get_next_ucode(void **mc, long offset);
|
|
||||||
extern int microcode_sanity_check(void *mc);
|
|
||||||
extern int get_matching_microcode(void *mc, int cpu);
|
|
||||||
extern void collect_cpu_info(int cpu_num);
|
|
||||||
extern int cpu_request_microcode(int cpu);
|
|
||||||
extern void microcode_fini_cpu(int cpu);
|
|
||||||
extern void apply_microcode(int cpu);
|
|
||||||
extern int apply_microcode_check_cpu(int cpu);
|
|
||||||
|
|
||||||
#ifdef CONFIG_MICROCODE_OLD_INTERFACE
|
#ifdef CONFIG_MICROCODE_OLD_INTERFACE
|
||||||
void __user *user_buffer; /* user area microcode data buffer */
|
static void __user *user_buffer; /* user area microcode data buffer */
|
||||||
unsigned int user_buffer_size; /* it's size */
|
EXPORT_SYMBOL_GPL(user_buffer);
|
||||||
|
static unsigned int user_buffer_size; /* it's size */
|
||||||
|
EXPORT_SYMBOL_GPL(user_buffer_size);
|
||||||
|
|
||||||
static int do_microcode_update (void)
|
static int do_microcode_update (void)
|
||||||
{
|
{
|
||||||
|
@ -130,8 +127,8 @@ static int do_microcode_update (void)
|
||||||
|
|
||||||
old = current->cpus_allowed;
|
old = current->cpus_allowed;
|
||||||
|
|
||||||
while ((cursor = get_next_ucode(&new_mc, cursor)) > 0) {
|
while ((cursor = microcode_ops->get_next_ucode(&new_mc, cursor)) > 0) {
|
||||||
error = microcode_sanity_check(new_mc);
|
error = microcode_ops->microcode_sanity_check(new_mc);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
/*
|
/*
|
||||||
|
@ -145,11 +142,12 @@ static int do_microcode_update (void)
|
||||||
continue;
|
continue;
|
||||||
cpumask_of_cpu_ptr_next(newmask, cpu);
|
cpumask_of_cpu_ptr_next(newmask, cpu);
|
||||||
set_cpus_allowed_ptr(current, newmask);
|
set_cpus_allowed_ptr(current, newmask);
|
||||||
error = get_maching_microcode(new_mc, cpu);
|
error = microcode_ops->get_matching_microcode(new_mc,
|
||||||
|
cpu);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (error == 1)
|
if (error == 1)
|
||||||
apply_microcode(cpu);
|
microcode_ops->apply_microcode(cpu);
|
||||||
}
|
}
|
||||||
vfree(new_mc);
|
vfree(new_mc);
|
||||||
}
|
}
|
||||||
|
@ -232,7 +230,8 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* fake device for request_firmware */
|
/* fake device for request_firmware */
|
||||||
struct platform_device *microcode_pdev;
|
static struct platform_device *microcode_pdev;
|
||||||
|
EXPORT_SYMBOL_GPL(microcode_pdev);
|
||||||
|
|
||||||
static void microcode_init_cpu(int cpu, int resume)
|
static void microcode_init_cpu(int cpu, int resume)
|
||||||
{
|
{
|
||||||
|
@ -244,9 +243,9 @@ static void microcode_init_cpu(int cpu, int resume)
|
||||||
|
|
||||||
set_cpus_allowed_ptr(current, newmask);
|
set_cpus_allowed_ptr(current, newmask);
|
||||||
mutex_lock(µcode_mutex);
|
mutex_lock(µcode_mutex);
|
||||||
collect_cpu_info(cpu);
|
microcode_ops->collect_cpu_info(cpu);
|
||||||
if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
|
if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
|
||||||
cpu_request_microcode(cpu);
|
microcode_ops->cpu_request_microcode(cpu);
|
||||||
mutex_unlock(µcode_mutex);
|
mutex_unlock(µcode_mutex);
|
||||||
set_cpus_allowed_ptr(current, &old);
|
set_cpus_allowed_ptr(current, &old);
|
||||||
}
|
}
|
||||||
|
@ -274,7 +273,7 @@ static ssize_t reload_store(struct sys_device *dev,
|
||||||
|
|
||||||
mutex_lock(µcode_mutex);
|
mutex_lock(µcode_mutex);
|
||||||
if (uci->valid)
|
if (uci->valid)
|
||||||
err = cpu_request_microcode(cpu);
|
err = microcode_ops->cpu_request_microcode(cpu);
|
||||||
mutex_unlock(µcode_mutex);
|
mutex_unlock(µcode_mutex);
|
||||||
put_online_cpus();
|
put_online_cpus();
|
||||||
set_cpus_allowed_ptr(current, &old);
|
set_cpus_allowed_ptr(current, &old);
|
||||||
|
@ -349,7 +348,7 @@ static int mc_sysdev_remove(struct sys_device *sys_dev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pr_debug("microcode: CPU%d removed\n", cpu);
|
pr_debug("microcode: CPU%d removed\n", cpu);
|
||||||
microcode_fini_cpu(cpu);
|
microcode_ops->microcode_fini_cpu(cpu);
|
||||||
sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
|
sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +361,7 @@ static int mc_sysdev_resume(struct sys_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
pr_debug("microcode: CPU%d resumed\n", cpu);
|
pr_debug("microcode: CPU%d resumed\n", cpu);
|
||||||
/* only CPU 0 will apply ucode here */
|
/* only CPU 0 will apply ucode here */
|
||||||
apply_microcode(0);
|
microcode_ops->apply_microcode(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +381,7 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case CPU_UP_CANCELED_FROZEN:
|
case CPU_UP_CANCELED_FROZEN:
|
||||||
/* The CPU refused to come up during a system resume */
|
/* The CPU refused to come up during a system resume */
|
||||||
microcode_fini_cpu(cpu);
|
microcode_ops->microcode_fini_cpu(cpu);
|
||||||
break;
|
break;
|
||||||
case CPU_ONLINE:
|
case CPU_ONLINE:
|
||||||
case CPU_DOWN_FAILED:
|
case CPU_DOWN_FAILED:
|
||||||
|
@ -390,9 +389,9 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
|
||||||
break;
|
break;
|
||||||
case CPU_ONLINE_FROZEN:
|
case CPU_ONLINE_FROZEN:
|
||||||
/* System-wide resume is in progress, try to apply microcode */
|
/* System-wide resume is in progress, try to apply microcode */
|
||||||
if (apply_microcode_check_cpu(cpu)) {
|
if (microcode_ops->apply_microcode_check_cpu(cpu)) {
|
||||||
/* The application of microcode failed */
|
/* The application of microcode failed */
|
||||||
microcode_fini_cpu(cpu);
|
microcode_ops->microcode_fini_cpu(cpu);
|
||||||
__mc_sysdev_add(sys_dev, 1);
|
__mc_sysdev_add(sys_dev, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -416,12 +415,17 @@ static struct notifier_block __refdata mc_cpu_notifier = {
|
||||||
.notifier_call = mc_cpu_callback,
|
.notifier_call = mc_cpu_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init microcode_init (void)
|
static int microcode_init(void *opaque, struct module *module)
|
||||||
{
|
{
|
||||||
|
struct microcode_ops *ops = (struct microcode_ops *)opaque;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
printk(KERN_INFO
|
if (microcode_ops) {
|
||||||
"IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n");
|
printk(KERN_ERR "microcode: already loaded the other module\n");
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
microcode_ops = ops;
|
||||||
|
|
||||||
error = microcode_dev_init();
|
error = microcode_dev_init();
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -443,8 +447,15 @@ static int __init microcode_init (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
register_hotcpu_notifier(&mc_cpu_notifier);
|
register_hotcpu_notifier(&mc_cpu_notifier);
|
||||||
|
|
||||||
|
printk(KERN_INFO
|
||||||
|
"Microcode Update Driver: v" MICROCODE_VERSION
|
||||||
|
" <tigran@aivazian.fsnet.co.uk>"
|
||||||
|
" <peter.oruba@amd.com>\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(microcode_init);
|
||||||
|
|
||||||
static void __exit microcode_exit (void)
|
static void __exit microcode_exit (void)
|
||||||
{
|
{
|
||||||
|
@ -457,7 +468,10 @@ static void __exit microcode_exit (void)
|
||||||
put_online_cpus();
|
put_online_cpus();
|
||||||
|
|
||||||
platform_device_unregister(microcode_pdev);
|
platform_device_unregister(microcode_pdev);
|
||||||
}
|
|
||||||
|
|
||||||
module_init(microcode_init)
|
microcode_ops = NULL;
|
||||||
module_exit(microcode_exit)
|
|
||||||
|
printk(KERN_INFO
|
||||||
|
"Microcode Update Driver: v" MICROCODE_VERSION " removed.\n");
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(microcode_exit);
|
||||||
|
|
|
@ -127,7 +127,7 @@ extern struct mutex microcode_mutex;
|
||||||
|
|
||||||
extern struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
|
extern struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
|
||||||
|
|
||||||
void collect_cpu_info(int cpu_num)
|
static void collect_cpu_info(int cpu_num)
|
||||||
{
|
{
|
||||||
struct cpuinfo_x86 *c = &cpu_data(cpu_num);
|
struct cpuinfo_x86 *c = &cpu_data(cpu_num);
|
||||||
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
|
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
|
||||||
|
@ -175,7 +175,7 @@ static inline int microcode_update_match(int cpu_num,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int microcode_sanity_check(void *mc)
|
static int microcode_sanity_check(void *mc)
|
||||||
{
|
{
|
||||||
struct microcode_header_intel *mc_header = mc;
|
struct microcode_header_intel *mc_header = mc;
|
||||||
struct extended_sigtable *ext_header = NULL;
|
struct extended_sigtable *ext_header = NULL;
|
||||||
|
@ -259,7 +259,7 @@ int microcode_sanity_check(void *mc)
|
||||||
* return 1 - found update
|
* return 1 - found update
|
||||||
* return < 0 - error
|
* return < 0 - error
|
||||||
*/
|
*/
|
||||||
int get_matching_microcode(void *mc, int cpu)
|
static int get_matching_microcode(void *mc, int cpu)
|
||||||
{
|
{
|
||||||
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
||||||
struct microcode_header_intel *mc_header = mc;
|
struct microcode_header_intel *mc_header = mc;
|
||||||
|
@ -288,7 +288,8 @@ int get_matching_microcode(void *mc, int cpu)
|
||||||
return 0;
|
return 0;
|
||||||
find:
|
find:
|
||||||
pr_debug("microcode: CPU%d found a matching microcode update with"
|
pr_debug("microcode: CPU%d found a matching microcode update with"
|
||||||
" version 0x%x (current=0x%x)\n", cpu, mc_header->rev, uci->rev);
|
" version 0x%x (current=0x%x)\n",
|
||||||
|
cpu, mc_header->rev, uci->rev);
|
||||||
new_mc = vmalloc(total_size);
|
new_mc = vmalloc(total_size);
|
||||||
if (!new_mc) {
|
if (!new_mc) {
|
||||||
printk(KERN_ERR "microcode: error! Can not allocate memory\n");
|
printk(KERN_ERR "microcode: error! Can not allocate memory\n");
|
||||||
|
@ -303,7 +304,7 @@ find:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_microcode(int cpu)
|
static void apply_microcode(int cpu)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int val[2];
|
unsigned int val[2];
|
||||||
|
@ -347,7 +348,7 @@ void apply_microcode(int cpu)
|
||||||
extern void __user *user_buffer; /* user area microcode data buffer */
|
extern void __user *user_buffer; /* user area microcode data buffer */
|
||||||
extern unsigned int user_buffer_size; /* it's size */
|
extern unsigned int user_buffer_size; /* it's size */
|
||||||
|
|
||||||
long get_next_ucode(void **mc, long offset)
|
static long get_next_ucode(void **mc, long offset)
|
||||||
{
|
{
|
||||||
struct microcode_header_intel mc_header;
|
struct microcode_header_intel mc_header;
|
||||||
unsigned long total_size;
|
unsigned long total_size;
|
||||||
|
@ -406,7 +407,7 @@ static long get_next_ucode_from_buffer(void **mc, const u8 *buf,
|
||||||
/* fake device for request_firmware */
|
/* fake device for request_firmware */
|
||||||
extern struct platform_device *microcode_pdev;
|
extern struct platform_device *microcode_pdev;
|
||||||
|
|
||||||
int cpu_request_microcode(int cpu)
|
static int cpu_request_microcode(int cpu)
|
||||||
{
|
{
|
||||||
char name[30];
|
char name[30];
|
||||||
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
||||||
|
@ -455,7 +456,7 @@ int cpu_request_microcode(int cpu)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apply_microcode_check_cpu(int cpu)
|
static int apply_microcode_check_cpu(int cpu)
|
||||||
{
|
{
|
||||||
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
||||||
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
||||||
|
@ -504,13 +505,42 @@ int apply_microcode_check_cpu(int cpu)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void microcode_fini_cpu(int cpu)
|
static void microcode_fini_cpu(int cpu)
|
||||||
{
|
{
|
||||||
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
|
||||||
|
|
||||||
mutex_lock(µcode_mutex);
|
mutex_lock(µcode_mutex);
|
||||||
uci->valid = 0;
|
uci->valid = 0;
|
||||||
kfree(uci->mc.mc_intel);
|
vfree(uci->mc.mc_intel);
|
||||||
uci->mc.mc_intel = NULL;
|
uci->mc.mc_intel = NULL;
|
||||||
mutex_unlock(µcode_mutex);
|
mutex_unlock(µcode_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct microcode_ops microcode_intel_ops = {
|
||||||
|
.get_next_ucode = get_next_ucode,
|
||||||
|
.get_matching_microcode = get_matching_microcode,
|
||||||
|
.microcode_sanity_check = microcode_sanity_check,
|
||||||
|
.apply_microcode_check_cpu = apply_microcode_check_cpu,
|
||||||
|
.cpu_request_microcode = cpu_request_microcode,
|
||||||
|
.collect_cpu_info = collect_cpu_info,
|
||||||
|
.apply_microcode = apply_microcode,
|
||||||
|
.microcode_fini_cpu = microcode_fini_cpu,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init microcode_intel_module_init(void)
|
||||||
|
{
|
||||||
|
struct cpuinfo_x86 *c = &cpu_data(get_cpu());
|
||||||
|
|
||||||
|
if (c->x86_vendor == X86_VENDOR_INTEL)
|
||||||
|
return microcode_init(µcode_intel_ops, THIS_MODULE);
|
||||||
|
else
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit microcode_intel_module_exit(void)
|
||||||
|
{
|
||||||
|
microcode_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(microcode_intel_module_init)
|
||||||
|
module_exit(microcode_intel_module_exit)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
extern int microcode_init(void *opaque, struct module *module);
|
||||||
|
extern void microcode_exit(void);
|
||||||
|
|
||||||
struct microcode_ops {
|
struct microcode_ops {
|
||||||
long (*get_next_ucode)(void **mc, long offset);
|
long (*get_next_ucode)(void **mc, long offset);
|
||||||
long (*microcode_get_next_ucode)(void **mc, long offset);
|
long (*microcode_get_next_ucode)(void **mc, long offset);
|
||||||
|
|
Loading…
Reference in New Issue