Commit a0a29b62 authored by Dmitry Adamushko's avatar Dmitry Adamushko Committed by Ingo Molnar

x86, microcode rework, v2

this is a rework of the microcode splitup in tip/x86/microcode

(1) I think this new interface is cleaner (look at the changes
    in 'struct microcode_ops' in microcode.h);

(2) it's -64 lines of code;
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 5b792d32
...@@ -110,50 +110,28 @@ struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; ...@@ -110,50 +110,28 @@ struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
EXPORT_SYMBOL_GPL(ucode_cpu_info); EXPORT_SYMBOL_GPL(ucode_cpu_info);
#ifdef CONFIG_MICROCODE_OLD_INTERFACE #ifdef CONFIG_MICROCODE_OLD_INTERFACE
void __user *user_buffer; /* user area microcode data buffer */ static int do_microcode_update(const void __user *buf, size_t size)
EXPORT_SYMBOL_GPL(user_buffer);
unsigned int user_buffer_size; /* it's size */
EXPORT_SYMBOL_GPL(user_buffer_size);
static int do_microcode_update(void)
{ {
long cursor = 0; cpumask_t old;
int error = 0; int error = 0;
void *new_mc = NULL;
int cpu; int cpu;
cpumask_t old;
old = current->cpus_allowed; old = current->cpus_allowed;
while ((cursor = microcode_ops->get_next_ucode(&new_mc, cursor)) > 0) { for_each_online_cpu(cpu) {
if (microcode_ops->microcode_sanity_check != NULL) struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
error = microcode_ops->microcode_sanity_check(new_mc);
if (error) if (!uci->valid)
continue;
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
error = microcode_ops->request_microcode_user(cpu, buf, size);
if (error < 0)
goto out; goto out;
/* if (!error)
* It's possible the data file has multiple matching ucode, microcode_ops->apply_microcode(cpu);
* lets keep searching till the latest version
*/
for_each_online_cpu(cpu) {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
if (!uci->valid)
continue;
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
error = microcode_ops->get_matching_microcode(new_mc,
cpu);
if (error < 0)
goto out;
if (error == 1)
microcode_ops->apply_microcode(cpu);
}
vfree(new_mc);
} }
out: out:
if (cursor > 0)
vfree(new_mc);
if (cursor < 0)
error = cursor;
set_cpus_allowed_ptr(current, &old); set_cpus_allowed_ptr(current, &old);
return error; return error;
} }
...@@ -178,10 +156,7 @@ static ssize_t microcode_write(struct file *file, const char __user *buf, ...@@ -178,10 +156,7 @@ static ssize_t microcode_write(struct file *file, const char __user *buf,
get_online_cpus(); get_online_cpus();
mutex_lock(&microcode_mutex); mutex_lock(&microcode_mutex);
user_buffer = (void __user *) buf; ret = do_microcode_update(buf, len);
user_buffer_size = (int) len;
ret = do_microcode_update();
if (!ret) if (!ret)
ret = (ssize_t)len; ret = (ssize_t)len;
...@@ -231,7 +206,6 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); ...@@ -231,7 +206,6 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
/* fake device for request_firmware */ /* fake device for request_firmware */
struct platform_device *microcode_pdev; struct platform_device *microcode_pdev;
EXPORT_SYMBOL_GPL(microcode_pdev);
static ssize_t reload_store(struct sys_device *dev, static ssize_t reload_store(struct sys_device *dev,
struct sysdev_attribute *attr, struct sysdev_attribute *attr,
...@@ -252,8 +226,12 @@ static ssize_t reload_store(struct sys_device *dev, ...@@ -252,8 +226,12 @@ static ssize_t reload_store(struct sys_device *dev,
if (cpu_online(cpu)) { if (cpu_online(cpu)) {
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
mutex_lock(&microcode_mutex); mutex_lock(&microcode_mutex);
if (uci->valid) if (uci->valid) {
err = microcode_ops->cpu_request_microcode(cpu); err = microcode_ops->request_microcode_fw(cpu,
&microcode_pdev->dev);
if (!err)
microcode_ops->apply_microcode(cpu);
}
mutex_unlock(&microcode_mutex); mutex_unlock(&microcode_mutex);
set_cpus_allowed_ptr(current, &old); set_cpus_allowed_ptr(current, &old);
} }
...@@ -315,7 +293,7 @@ static void collect_cpu_info(int cpu) ...@@ -315,7 +293,7 @@ static void collect_cpu_info(int cpu)
uci->valid = 1; uci->valid = 1;
} }
static void microcode_resume_cpu(int cpu) static int microcode_resume_cpu(int cpu)
{ {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
struct cpu_signature nsig; struct cpu_signature nsig;
...@@ -323,7 +301,7 @@ static void microcode_resume_cpu(int cpu) ...@@ -323,7 +301,7 @@ static void microcode_resume_cpu(int cpu)
pr_debug("microcode: CPU%d resumed\n", cpu); pr_debug("microcode: CPU%d resumed\n", cpu);
if (!uci->mc.valid_mc) if (!uci->mc.valid_mc)
return; return 1;
/* /*
* Let's verify that the 'cached' ucode does belong * Let's verify that the 'cached' ucode does belong
...@@ -331,21 +309,22 @@ static void microcode_resume_cpu(int cpu) ...@@ -331,21 +309,22 @@ static void microcode_resume_cpu(int cpu)
*/ */
if (microcode_ops->collect_cpu_info(cpu, &nsig)) { if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
microcode_fini_cpu(cpu); microcode_fini_cpu(cpu);
return; return -1;
} }
if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) { if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) {
microcode_fini_cpu(cpu); microcode_fini_cpu(cpu);
/* Should we look for a new ucode here? */ /* Should we look for a new ucode here? */
return; return 1;
} }
microcode_ops->apply_microcode(cpu); return 0;
} }
void microcode_update_cpu(int cpu) void microcode_update_cpu(int cpu)
{ {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
int err = 0;
/* We should bind the task to the CPU */ /* We should bind the task to the CPU */
BUG_ON(raw_smp_processor_id() != cpu); BUG_ON(raw_smp_processor_id() != cpu);
...@@ -356,12 +335,17 @@ void microcode_update_cpu(int cpu) ...@@ -356,12 +335,17 @@ void microcode_update_cpu(int cpu)
* otherwise just request a firmware: * otherwise just request a firmware:
*/ */
if (uci->valid) { if (uci->valid) {
microcode_resume_cpu(cpu); err = microcode_resume_cpu(cpu);
} else { } else {
collect_cpu_info(cpu); collect_cpu_info(cpu);
if (uci->valid && system_state == SYSTEM_RUNNING) if (uci->valid && system_state == SYSTEM_RUNNING)
microcode_ops->cpu_request_microcode(cpu); err = microcode_ops->request_microcode_fw(cpu,
&microcode_pdev->dev);
} }
if (!err)
microcode_ops->apply_microcode(cpu);
mutex_unlock(&microcode_mutex); mutex_unlock(&microcode_mutex);
} }
...@@ -414,7 +398,7 @@ static int mc_sysdev_resume(struct sys_device *dev) ...@@ -414,7 +398,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 */
microcode_ops->apply_microcode(0); microcode_update_cpu(0);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -155,15 +155,15 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) ...@@ -155,15 +155,15 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
return 0; return 0;
} }
static inline int microcode_update_match(int cpu_num, static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf)
struct microcode_header_intel *mc_header, int sig, int pf)
{ {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1;
}
if (!sigmatch(sig, uci->cpu_sig.sig, pf, uci->cpu_sig.pf) static inline int
|| mc_header->rev <= uci->cpu_sig.rev) update_match_revision(struct microcode_header_intel *mc_header, int rev)
return 0; {
return 1; return (mc_header->rev <= rev) ? 0 : 1;
} }
static int microcode_sanity_check(void *mc) static int microcode_sanity_check(void *mc)
...@@ -248,51 +248,36 @@ static int microcode_sanity_check(void *mc) ...@@ -248,51 +248,36 @@ static int microcode_sanity_check(void *mc)
/* /*
* return 0 - no update found * return 0 - no update found
* return 1 - found update * return 1 - found update
* return < 0 - error
*/ */
static int get_matching_microcode(void *mc, int cpu) static int
get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev)
{ {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
struct microcode_header_intel *mc_header = mc; struct microcode_header_intel *mc_header = mc;
struct extended_sigtable *ext_header; struct extended_sigtable *ext_header;
unsigned long total_size = get_totalsize(mc_header); unsigned long total_size = get_totalsize(mc_header);
int ext_sigcount, i; int ext_sigcount, i;
struct extended_signature *ext_sig; struct extended_signature *ext_sig;
void *new_mc;
if (microcode_update_match(cpu, mc_header, if (!update_match_revision(mc_header, rev))
mc_header->sig, mc_header->pf)) return 0;
goto find;
if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf))
return 1;
/* Look for ext. headers: */
if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
return 0; return 0;
ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
ext_sigcount = ext_header->count; ext_sigcount = ext_header->count;
ext_sig = (void *)ext_header + EXT_HEADER_SIZE; ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
for (i = 0; i < ext_sigcount; i++) { for (i = 0; i < ext_sigcount; i++) {
if (microcode_update_match(cpu, mc_header, if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf))
ext_sig->sig, ext_sig->pf)) return 1;
goto find;
ext_sig++; ext_sig++;
} }
return 0; return 0;
find:
pr_debug("microcode: CPU%d found a matching microcode update with"
" version 0x%x (current=0x%x)\n",
cpu, mc_header->rev, uci->cpu_sig.rev);
new_mc = vmalloc(total_size);
if (!new_mc) {
printk(KERN_ERR "microcode: error! Can not allocate memory\n");
return -ENOMEM;
}
/* free previous update file */
vfree(uci->mc.mc_intel);
memcpy(new_mc, mc, total_size);
uci->mc.mc_intel = new_mc;
return 1;
} }
static void apply_microcode(int cpu) static void apply_microcode(int cpu)
...@@ -300,7 +285,7 @@ static void apply_microcode(int cpu) ...@@ -300,7 +285,7 @@ static void apply_microcode(int cpu)
unsigned long flags; unsigned long flags;
unsigned int val[2]; unsigned int val[2];
int cpu_num = raw_smp_processor_id(); int cpu_num = raw_smp_processor_id();
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
/* We should bind the task to the CPU */ /* We should bind the task to the CPU */
BUG_ON(cpu_num != cpu); BUG_ON(cpu_num != cpu);
...@@ -338,116 +323,105 @@ static void apply_microcode(int cpu) ...@@ -338,116 +323,105 @@ static void apply_microcode(int cpu)
uci->cpu_sig.rev = val[1]; uci->cpu_sig.rev = val[1];
} }
#ifdef CONFIG_MICROCODE_OLD_INTERFACE static int generic_load_microcode(int cpu, void *data, size_t size,
extern void __user *user_buffer; /* user area microcode data buffer */ int (*get_ucode_data)(void *, const void *, size_t))
extern unsigned int user_buffer_size; /* it's size */
static long get_next_ucode(void **mc, long offset)
{ {
struct microcode_header_intel mc_header; struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
unsigned long total_size; u8 *ucode_ptr = data, *new_mc = NULL, *mc;
int new_rev = uci->cpu_sig.rev;
unsigned int leftover = size;
/* No more data */ while (leftover) {
if (offset >= user_buffer_size) struct microcode_header_intel mc_header;
return 0; unsigned int mc_size;
if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
printk(KERN_ERR "microcode: error! Can not read user data\n");
return -EFAULT;
}
total_size = get_totalsize(&mc_header);
if (offset + total_size > user_buffer_size) {
printk(KERN_ERR "microcode: error! Bad total size in microcode "
"data file\n");
return -EINVAL;
}
*mc = vmalloc(total_size);
if (!*mc)
return -ENOMEM;
if (copy_from_user(*mc, user_buffer + offset, total_size)) {
printk(KERN_ERR "microcode: error! Can not read user data\n");
vfree(*mc);
return -EFAULT;
}
return offset + total_size;
}
#endif
static long get_next_ucode_from_buffer(void **mc, const u8 *buf, if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header)))
unsigned long size, long offset) break;
{
struct microcode_header_intel *mc_header;
unsigned long total_size;
/* No more data */ mc_size = get_totalsize(&mc_header);
if (offset >= size) if (!mc_size || mc_size > leftover) {
return 0; printk(KERN_ERR "microcode: error!"
mc_header = (struct microcode_header_intel *)(buf + offset); "Bad data in microcode data file\n");
total_size = get_totalsize(mc_header); break;
}
if (offset + total_size > size) { mc = vmalloc(mc_size);
printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); if (!mc)
return -EINVAL; break;
if (get_ucode_data(mc, ucode_ptr, mc_size) ||
microcode_sanity_check(mc) < 0) {
vfree(mc);
break;
}
if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) {
new_rev = mc_header.rev;
new_mc = mc;
} else
vfree(mc);
ucode_ptr += mc_size;
leftover -= mc_size;
} }
*mc = vmalloc(total_size); if (new_mc) {
if (!*mc) { if (!leftover) {
printk(KERN_ERR "microcode: error! Can not allocate memory\n"); if (uci->mc.mc_intel)
return -ENOMEM; vfree(uci->mc.mc_intel);
uci->mc.mc_intel = (struct microcode_intel *)new_mc;
pr_debug("microcode: CPU%d found a matching microcode update with"
" version 0x%x (current=0x%x)\n",
cpu, uci->mc.mc_intel->hdr.rev, uci->cpu_sig.rev);
} else
vfree(new_mc);
} }
memcpy(*mc, buf + offset, total_size);
return offset + total_size; return (int)leftover;
} }
/* fake device for request_firmware */ static int get_ucode_fw(void *to, const void *from, size_t n)
extern struct platform_device *microcode_pdev; {
memcpy(to, from, n);
return 0;
}
static int cpu_request_microcode(int cpu) static int request_microcode_fw(int cpu, struct device *device)
{ {
char name[30]; char name[30];
struct cpuinfo_x86 *c = &cpu_data(cpu); struct cpuinfo_x86 *c = &cpu_data(cpu);
const struct firmware *firmware; const struct firmware *firmware;
const u8 *buf; int ret;
unsigned long size;
long offset = 0;
int error;
void *mc;
/* We should bind the task to the CPU */ /* We should bind the task to the CPU */
BUG_ON(cpu != raw_smp_processor_id()); BUG_ON(cpu != raw_smp_processor_id());
sprintf(name, "intel-ucode/%02x-%02x-%02x", sprintf(name, "intel-ucode/%02x-%02x-%02x",
c->x86, c->x86_model, c->x86_mask); c->x86, c->x86_model, c->x86_mask);
error = request_firmware(&firmware, name, &microcode_pdev->dev); ret = request_firmware(&firmware, name, device);
if (error) { if (ret) {
pr_debug("microcode: data file %s load failed\n", name); pr_debug("microcode: data file %s load failed\n", name);
return error; return ret;
}
buf = firmware->data;
size = firmware->size;
while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset))
> 0) {
error = microcode_sanity_check(mc);
if (error)
break;
error = get_matching_microcode(mc, cpu);
if (error < 0)
break;
/*
* It's possible the data file has multiple matching ucode,
* lets keep searching till the latest version
*/
if (error == 1) {
apply_microcode(cpu);
error = 0;
}
vfree(mc);
} }
if (offset > 0)
vfree(mc); ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
if (offset < 0) &get_ucode_fw);
error = offset;
release_firmware(firmware); release_firmware(firmware);
return error; return ret;
}
static int get_ucode_user(void *to, const void *from, size_t n)
{
return copy_from_user(to, from, n);
}
static int request_microcode_user(int cpu, const void __user *buf, size_t size)
{
/* We should bind the task to the CPU */
BUG_ON(cpu != raw_smp_processor_id());
return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user);
} }
static void microcode_fini_cpu(int cpu) static void microcode_fini_cpu(int cpu)
...@@ -459,10 +433,8 @@ static void microcode_fini_cpu(int cpu) ...@@ -459,10 +433,8 @@ static void microcode_fini_cpu(int cpu)
} }
static struct microcode_ops microcode_intel_ops = { static struct microcode_ops microcode_intel_ops = {
.get_next_ucode = get_next_ucode, .request_microcode_user = request_microcode_user,
.get_matching_microcode = get_matching_microcode, .request_microcode_fw = request_microcode_fw,
.microcode_sanity_check = microcode_sanity_check,
.cpu_request_microcode = cpu_request_microcode,
.collect_cpu_info = collect_cpu_info, .collect_cpu_info = collect_cpu_info,
.apply_microcode = apply_microcode, .apply_microcode = apply_microcode,
.microcode_fini_cpu = microcode_fini_cpu, .microcode_fini_cpu = microcode_fini_cpu,
......
...@@ -5,17 +5,16 @@ extern int microcode_init(void *opaque, struct module *module); ...@@ -5,17 +5,16 @@ extern int microcode_init(void *opaque, struct module *module);
extern void microcode_exit(void); extern void microcode_exit(void);
struct cpu_signature; struct cpu_signature;
struct device;
struct microcode_ops { struct microcode_ops {
long (*get_next_ucode)(void **mc, long offset); int (*request_microcode_user) (int cpu, const void __user *buf, size_t size);
long (*microcode_get_next_ucode)(void **mc, long offset); int (*request_microcode_fw) (int cpu, struct device *device);
int (*get_matching_microcode)(void *mc, int cpu);
int (*microcode_sanity_check)(void *mc); void (*apply_microcode) (int cpu);
int (*cpu_request_microcode)(int cpu);
int (*collect_cpu_info)(int cpu_num, struct cpu_signature *csig); int (*collect_cpu_info) (int cpu, struct cpu_signature *csig);
void (*apply_microcode)(int cpu); void (*microcode_fini_cpu) (int cpu);
void (*microcode_fini_cpu)(int cpu);
void (*clear_patch)(void *data);
}; };
struct microcode_header_intel { struct microcode_header_intel {
......
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