Skip to content

Commit

Permalink
x86-microcode: generic interface refactoring
Browse files Browse the repository at this point in the history
This is the 1st patch in the series. Here the aim was to avoid any
significant changes, logically-wise.

So it's mainly about generic interface refactoring: e.g. make
microcode_{intel,amd}.c more about arch-specific details and less
about policies like make-sure-we-run-on-a-target-cpu
(no more set_cpus_allowed_ptr() here) and generic synchronization (no
more microcode_mutex here).

All in all, more line have been deleted than added.

4 files changed, 145 insertions(+), 198 deletions(-)

Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
Dmitry Adamushko authored and Ingo Molnar committed Aug 20, 2008
1 parent 8343ef2 commit d45de40
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 198 deletions.
155 changes: 102 additions & 53 deletions arch/x86/kernel/microcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
* Thanks to Stuart Swales for pointing out this bug.
*/

/*#define DEBUG pr_debug */
/* #define DEBUG pr_debug */
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/init.h>
Expand Down Expand Up @@ -104,8 +104,7 @@ MODULE_LICENSE("GPL");
struct microcode_ops *microcode_ops;

/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
DEFINE_MUTEX(microcode_mutex);
EXPORT_SYMBOL_GPL(microcode_mutex);
static DEFINE_MUTEX(microcode_mutex);

struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
EXPORT_SYMBOL_GPL(ucode_cpu_info);
Expand Down Expand Up @@ -234,22 +233,6 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
struct platform_device *microcode_pdev;
EXPORT_SYMBOL_GPL(microcode_pdev);

static void microcode_init_cpu(int cpu, int resume)
{
cpumask_t old;
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

old = current->cpus_allowed;

set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
mutex_lock(&microcode_mutex);
microcode_ops->collect_cpu_info(cpu);
if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
microcode_ops->cpu_request_microcode(cpu);
mutex_unlock(&microcode_mutex);
set_cpus_allowed_ptr(current, &old);
}

static ssize_t reload_store(struct sys_device *dev,
struct sysdev_attribute *attr,
const char *buf, size_t sz)
Expand All @@ -266,14 +249,15 @@ static ssize_t reload_store(struct sys_device *dev,
cpumask_t old = current->cpus_allowed;

get_online_cpus();
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));

mutex_lock(&microcode_mutex);
if (uci->valid)
err = microcode_ops->cpu_request_microcode(cpu);
mutex_unlock(&microcode_mutex);
if (cpu_online(cpu)) {
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
mutex_lock(&microcode_mutex);
if (uci->valid)
err = microcode_ops->cpu_request_microcode(cpu);
mutex_unlock(&microcode_mutex);
set_cpus_allowed_ptr(current, &old);
}
put_online_cpus();
set_cpus_allowed_ptr(current, &old);
}
if (err)
return err;
Expand All @@ -285,15 +269,15 @@ static ssize_t version_show(struct sys_device *dev,
{
struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;

return sprintf(buf, "0x%x\n", uci->rev);
return sprintf(buf, "0x%x\n", uci->cpu_sig.rev);
}

static ssize_t pf_show(struct sys_device *dev,
struct sysdev_attribute *attr, char *buf)
{
struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;

return sprintf(buf, "0x%x\n", uci->pf);
return sprintf(buf, "0x%x\n", uci->cpu_sig.pf);
}

static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
Expand All @@ -312,7 +296,85 @@ static struct attribute_group mc_attr_group = {
.name = "microcode",
};

static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
static void microcode_fini_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

mutex_lock(&microcode_mutex);
microcode_ops->microcode_fini_cpu(cpu);
uci->valid = 0;
mutex_unlock(&microcode_mutex);
}

static void collect_cpu_info(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

memset(uci, 0, sizeof(*uci));
if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig))
uci->valid = 1;
}

static void microcode_resume_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
struct cpu_signature nsig;

pr_debug("microcode: CPU%d resumed\n", cpu);

if (!uci->mc.valid_mc)
return;

/*
* Let's verify that the 'cached' ucode does belong
* to this cpu (a bit of paranoia):
*/
if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
microcode_fini_cpu(cpu);
return;
}

if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) {
microcode_fini_cpu(cpu);
/* Should we look for a new ucode here? */
return;
}

microcode_ops->apply_microcode(cpu);
}

void microcode_update_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

/* We should bind the task to the CPU */
BUG_ON(raw_smp_processor_id() != cpu);

mutex_lock(&microcode_mutex);
/*
* Check if the system resume is in progress (uci->valid != NULL),
* otherwise just request a firmware:
*/
if (uci->valid) {
microcode_resume_cpu(cpu);
} else {
collect_cpu_info(cpu);
if (uci->valid && system_state == SYSTEM_RUNNING)
microcode_ops->cpu_request_microcode(cpu);
}
mutex_unlock(&microcode_mutex);
}

static void microcode_init_cpu(int cpu)
{
cpumask_t old = current->cpus_allowed;

set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
microcode_update_cpu(cpu);
set_cpus_allowed_ptr(current, &old);
}

static int mc_sysdev_add(struct sys_device *sys_dev)
{
int err, cpu = sys_dev->id;
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
Expand All @@ -327,16 +389,10 @@ static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
if (err)
return err;

microcode_init_cpu(cpu, resume);

microcode_init_cpu(cpu);
return 0;
}

static int mc_sysdev_add(struct sys_device *sys_dev)
{
return __mc_sysdev_add(sys_dev, 0);
}

static int mc_sysdev_remove(struct sys_device *sys_dev)
{
int cpu = sys_dev->id;
Expand All @@ -345,7 +401,7 @@ static int mc_sysdev_remove(struct sys_device *sys_dev)
return 0;

pr_debug("microcode: CPU%d removed\n", cpu);
microcode_ops->microcode_fini_cpu(cpu);
microcode_fini_cpu(cpu);
sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
return 0;
}
Expand Down Expand Up @@ -376,33 +432,26 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)

sys_dev = get_cpu_sysdev(cpu);
switch (action) {
case CPU_UP_CANCELED_FROZEN:
/* The CPU refused to come up during a system resume */
microcode_ops->microcode_fini_cpu(cpu);
break;
case CPU_ONLINE:
case CPU_DOWN_FAILED:
mc_sysdev_add(sys_dev);
break;
case CPU_ONLINE_FROZEN:
/* System-wide resume is in progress, try to apply microcode */
if (microcode_ops->apply_microcode_check_cpu(cpu)) {
/* The application of microcode failed */
microcode_ops->microcode_fini_cpu(cpu);
__mc_sysdev_add(sys_dev, 1);
break;
}
microcode_init_cpu(cpu);
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN:
pr_debug("microcode: CPU%d added\n", cpu);
if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
printk(KERN_ERR "microcode: Failed to create the sysfs "
"group for CPU%d\n", cpu);
break;
case CPU_DOWN_PREPARE:
mc_sysdev_remove(sys_dev);
break;
case CPU_DOWN_PREPARE_FROZEN:
/* Suspend is in progress, only remove the interface */
sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
pr_debug("microcode: CPU%d removed\n", cpu);
break;
case CPU_DEAD:
case CPU_UP_CANCELED_FROZEN:
/* The CPU refused to come up during a system resume */
microcode_fini_cpu(cpu);
break;
}
return NOTIFY_OK;
Expand Down
77 changes: 12 additions & 65 deletions arch/x86/kernel/microcode_amd.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,38 +59,28 @@ MODULE_LICENSE("GPL v2");
/* serialize access to the physical write */
static DEFINE_SPINLOCK(microcode_update_lock);

/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
extern struct mutex (microcode_mutex);

struct equiv_cpu_entry *equiv_cpu_table;

extern struct ucode_cpu_info ucode_cpu_info[NR_CPUS];

static void collect_cpu_info_amd(int cpu)
static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

/* We should bind the task to the CPU */
BUG_ON(raw_smp_processor_id() != cpu);
uci->rev = 0;
uci->pf = 0;
uci->mc.mc_amd = NULL;
uci->valid = 1;
memset(csig, 0, sizeof(*csig));

if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
cpu);
uci->valid = 0;
return;
return -1;
}

asm volatile("movl %1, %%ecx; rdmsr"
: "=a" (uci->rev)
: "=a" (csig->rev)
: "i" (0x0000008B) : "ecx");

printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n",
uci->rev);
csig->rev);

return 0;
}

static int get_matching_microcode_amd(void *mc, int cpu)
Expand Down Expand Up @@ -119,7 +109,7 @@ static int get_matching_microcode_amd(void *mc, int cpu)
if (equiv_cpu_table == NULL) {
printk(KERN_INFO "microcode: CPU%d microcode update with "
"version 0x%x (current=0x%x)\n",
cpu, mc_header->patch_id, uci->rev);
cpu, mc_header->patch_id, uci->cpu_sig.rev);
goto out;
}

Expand Down Expand Up @@ -185,12 +175,12 @@ static int get_matching_microcode_amd(void *mc, int cpu)
pci_dev_put(sb_pci_dev);
}

if (mc_header->patch_id <= uci->rev)
if (mc_header->patch_id <= uci->cpu_sig.rev)
return 0;

printk(KERN_INFO "microcode: CPU%d found a matching microcode "
"update with version 0x%x (current=0x%x)\n",
cpu, mc_header->patch_id, uci->rev);
cpu, mc_header->patch_id, uci->cpu_sig.rev);

out:
new_mc = vmalloc(UCODE_MAX_SIZE);
Expand Down Expand Up @@ -250,9 +240,9 @@ static void apply_microcode_amd(int cpu)

printk(KERN_INFO "microcode: CPU%d updated from revision "
"0x%x to 0x%x \n",
cpu_num, uci->rev, uci->mc.mc_amd->hdr.patch_id);
cpu_num, uci->cpu_sig.rev, uci->mc.mc_amd->hdr.patch_id);

uci->rev = rev;
uci->cpu_sig.rev = rev;
}

#ifdef CONFIG_MICROCODE_OLD_INTERFACE
Expand Down Expand Up @@ -437,61 +427,18 @@ static int cpu_request_microcode_amd(int cpu)
return error;
}

static int apply_microcode_check_cpu_amd(int cpu)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
unsigned int rev;
cpumask_t old;
int err = 0;

/* Check if the microcode is available */
if (!uci->mc.mc_amd)
return 0;

old = current->cpus_allowed;
set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));

/* Check if the microcode we have in memory matches the CPU */
if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 16)
err = -EINVAL;

if (!err) {
asm volatile("movl %1, %%ecx; rdmsr"
: "=a" (rev)
: "i" (0x0000008B) : "ecx");

if (uci->rev != rev)
err = -EINVAL;
}

if (!err)
apply_microcode_amd(cpu);
else
printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:"
" rev=0x%x\n",
cpu, uci->rev);

set_cpus_allowed(current, old);
return err;
}

static void microcode_fini_cpu_amd(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;

mutex_lock(&microcode_mutex);
uci->valid = 0;
vfree(uci->mc.mc_amd);
uci->mc.mc_amd = NULL;
mutex_unlock(&microcode_mutex);
}

static struct microcode_ops microcode_amd_ops = {
.get_next_ucode = get_next_ucode_amd,
.get_matching_microcode = get_matching_microcode_amd,
.microcode_sanity_check = NULL,
.apply_microcode_check_cpu = apply_microcode_check_cpu_amd,
.cpu_request_microcode = cpu_request_microcode_amd,
.collect_cpu_info = collect_cpu_info_amd,
.apply_microcode = apply_microcode_amd,
Expand Down
Loading

0 comments on commit d45de40

Please sign in to comment.