x86: consolidate microcode loading code

- memory was leaked on a CPU offline/online cycle (including S3)
- memory was leaked on AMD systems when microcode_update() ran a 2nd
  time with the same data that was used on the first run
- microcode never got restored on APs during S3 resume (or post-boot
  onlining of a CPU that was also online when microcode_update() first
  ran [in the event the prior microcode update got lost intermediately,
  which supposedly shouldn't happen]); this will still be the case when
  no other online CPU has an identical signature (which however is now
  consistent with bringing up such a CPU the very first time)
- resume was unimplemented in the AMD case
- there was a race between microcode_update_cpu() and
  microcode_resume_cpu()

This also moves vendor specific type declarations to the vendor source
file and sets the stage for boot time microcode loading (i.e. without
Dom0 involvement).

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
This commit is contained in:
Jan Beulich committed 2011-12-01 17:55:26 +01:00
1 parent 943730ecff
commit 0bda96825f
5 files changed
+204 -145

No files matched your search

+2 -1
View File
@@ -30,9 +30,10 @@ obj-y += io_apic.o
obj-y += msi.o
obj-y += ioport_emulate.o
obj-y += irq.o
obj-y += microcode.o
obj-y += microcode_amd.o
obj-y += microcode_intel.o
# This must come after the vendor specific files.
obj-y += microcode.o
obj-y += mm.o
obj-y += mpparse.o
obj-y += nmi.o
+62 -15
View File
@@ -22,17 +22,17 @@
*/
#include <xen/config.h>
#include <xen/cpu.h>
#include <xen/lib.h>
#include <xen/kernel.h>
#include <xen/init.h>
#include <xen/notifier.h>
#include <xen/sched.h>
#include <xen/smp.h>
#include <xen/spinlock.h>
#include <xen/guest_access.h>
#include <asm/current.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/microcode.h>
@@ -69,30 +69,50 @@ int microcode_resume_cpu(int cpu)
int err;
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
struct cpu_signature nsig;
unsigned int cpu2;
if ( !uci->mc.mc_valid )
return -EIO;
spin_lock(&microcode_mutex);
/*
* Let's verify that the 'cached' ucode does belong
* to this cpu (a bit of paranoia):
*/
err = microcode_ops->collect_cpu_info(cpu, &nsig);
err = microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig);
if ( err )
{
microcode_fini_cpu(cpu);
__microcode_fini_cpu(cpu);
spin_unlock(&microcode_mutex);
return err;
}
if ( microcode_ops->microcode_resume_match(cpu, &nsig) )
if ( uci->mc.mc_valid )
{
return microcode_ops->apply_microcode(cpu);
err = microcode_ops->microcode_resume_match(cpu, uci->mc.mc_valid);
if ( err >= 0 )
{
if ( err )
err = microcode_ops->apply_microcode(cpu);
spin_unlock(&microcode_mutex);
return err;
}
}
else
nsig = uci->cpu_sig;
__microcode_fini_cpu(cpu);
uci->cpu_sig = nsig;
err = -EIO;
for_each_online_cpu ( cpu2 )
{
microcode_fini_cpu(cpu);
return -EIO;
uci = &per_cpu(ucode_cpu_info, cpu2);
if ( uci->mc.mc_valid &&
microcode_ops->microcode_resume_match(cpu, uci->mc.mc_valid) > 0 )
{
err = microcode_ops->apply_microcode(cpu);
break;
}
}
__microcode_fini_cpu(cpu);
spin_unlock(&microcode_mutex);
return err;
}
static int microcode_update_cpu(const void *buf, size_t size)
@@ -162,3 +182,30 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf, unsigned long len)
return continue_hypercall_on_cpu(info->cpu, do_microcode_update, info);
}
static int microcode_percpu_callback(
struct notifier_block *nfb, unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch ( action )
{
case CPU_DEAD:
microcode_fini_cpu(cpu);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block microcode_percpu_nfb = {
.notifier_call = microcode_percpu_callback,
};
static int __init microcode_presmp_init(void)
{
if ( microcode_ops )
register_cpu_notifier(&microcode_percpu_nfb);
return 0;
}
presmp_initcall(microcode_presmp_init);
+96 -52
View File
@@ -23,27 +23,53 @@
#include <xen/spinlock.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/microcode.h>
#define pr_debug(x...) ((void)0)
struct equiv_cpu_entry {
uint32_t installed_cpu;
uint32_t fixed_errata_mask;
uint32_t fixed_errata_compare;
uint16_t equiv_cpu;
uint16_t reserved;
} __attribute__((packed));
struct microcode_header_amd {
uint32_t data_code;
uint32_t patch_id;
uint8_t mc_patch_data_id[2];
uint8_t mc_patch_data_len;
uint8_t init_flag;
uint32_t mc_patch_data_checksum;
uint32_t nb_dev_id;
uint32_t sb_dev_id;
uint16_t processor_rev_id;
uint8_t nb_rev_id;
uint8_t sb_rev_id;
uint8_t bios_api_rev;
uint8_t reserved1[3];
uint32_t match_reg[8];
} __attribute__((packed));
#define UCODE_MAGIC 0x00414d44
#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
#define UCODE_UCODE_TYPE 0x00000001
#define UCODE_MAX_SIZE (2048)
#define DEFAULT_UCODE_DATASIZE (896)
#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd))
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define DWSIZE (sizeof(uint32_t))
struct microcode_amd {
struct microcode_header_amd hdr;
unsigned int mpb[(UCODE_MAX_SIZE - MC_HEADER_SIZE) / 4];
unsigned int equiv_cpu_table_size;
struct equiv_cpu_entry equiv_cpu_table[];
};
/* serialize access to the physical write */
static DEFINE_SPINLOCK(microcode_update_lock);
struct equiv_cpu_entry *equiv_cpu_table;
static int collect_cpu_info(int cpu, struct cpu_signature *csig)
{
struct cpuinfo_x86 *c = &cpu_data[cpu];
@@ -65,10 +91,11 @@ static int collect_cpu_info(int cpu, struct cpu_signature *csig)
return 0;
}
static int microcode_fits(void *mc, int cpu)
static int microcode_fits(const struct microcode_amd *mc_amd, int cpu)
{
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
struct microcode_header_amd *mc_header = mc;
const struct microcode_header_amd *mc_header = &mc_amd->hdr;
const struct equiv_cpu_entry *equiv_cpu_table = mc_amd->equiv_cpu_table;
unsigned int current_cpu_id;
unsigned int equiv_cpu_id = 0x0;
unsigned int i;
@@ -99,7 +126,7 @@ static int microcode_fits(void *mc, int cpu)
}
if ( mc_header->patch_id <= uci->cpu_sig.rev )
return -EINVAL;
return 0;
printk(KERN_DEBUG "microcode: CPU%d found a matching microcode "
"update with version 0x%x (current=0x%x)\n",
@@ -186,17 +213,15 @@ static int get_next_ucode_from_buffer_amd(void *mc, const void *buf,
return 0;
}
static int install_equiv_cpu_table(const void *buf, uint32_t size,
unsigned long *offset)
static int install_equiv_cpu_table(
struct microcode_amd *mc_amd,
const uint32_t *buf_pos,
unsigned long *offset)
{
const uint32_t *buf_pos = buf;
unsigned long off;
off = *offset;
*offset = 0;
uint32_t size = buf_pos[2];
/* No more data */
if ( off >= size )
if ( size + 12 >= *offset )
return -EINVAL;
if ( buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE )
@@ -213,15 +238,8 @@ static int install_equiv_cpu_table(const void *buf, uint32_t size,
return -EINVAL;
}
equiv_cpu_table = xmalloc_bytes(size);
if ( equiv_cpu_table == NULL )
{
printk(KERN_ERR "microcode: error, can't allocate "
"memory for equiv CPU table\n");
return -ENOMEM;
}
memcpy(equiv_cpu_table, (const void *)&buf_pos[3], size);
memcpy(mc_amd->equiv_cpu_table, &buf_pos[3], size);
mc_amd->equiv_cpu_table_size = size;
*offset = size + 12; /* add header length */
@@ -231,11 +249,11 @@ static int install_equiv_cpu_table(const void *buf, uint32_t size,
static int cpu_request_microcode(int cpu, const void *buf, size_t size)
{
const uint32_t *buf_pos;
unsigned long offset = 0;
struct microcode_amd *mc_amd, *mc_old;
unsigned long offset = size;
int error = 0;
int ret;
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
void *mc;
/* We should bind the task to the CPU */
BUG_ON(cpu != raw_smp_processor_id());
@@ -249,59 +267,85 @@ static int cpu_request_microcode(int cpu, const void *buf, size_t size)
return -EINVAL;
}
error = install_equiv_cpu_table(buf, (uint32_t)(buf_pos[2]), &offset);
mc_amd = xmalloc_bytes(sizeof(*mc_amd) + buf_pos[2]);
if ( !mc_amd )
{
printk(KERN_ERR "microcode: error! "
"Can not allocate memory for microcode patch\n");
return -ENOMEM;
}
error = install_equiv_cpu_table(mc_amd, buf, &offset);
if ( error )
{
xfree(mc_amd);
printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
return -EINVAL;
}
mc = xmalloc_bytes(UCODE_MAX_SIZE);
if ( mc == NULL )
{
printk(KERN_ERR "microcode: error! "
"Can not allocate memory for microcode patch\n");
error = -ENOMEM;
goto out;
}
mc_old = uci->mc.mc_amd;
/* implicitely validates uci->mc.mc_valid */
uci->mc.mc_amd = mc;
uci->mc.mc_amd = mc_amd;
/*
* It's possible the data file has multiple matching ucode,
* lets keep searching till the latest version
*/
while ( (ret = get_next_ucode_from_buffer_amd(mc, buf, size, &offset)) == 0)
while ( (ret = get_next_ucode_from_buffer_amd(&mc_amd->hdr, buf, size,
&offset)) == 0 )
{
error = microcode_fits(mc, cpu);
error = microcode_fits(mc_amd, cpu);
if (error <= 0)
continue;
error = apply_microcode(cpu);
if (error == 0)
{
error = 1;
break;
}
}
if ( ret < 0 )
error = ret;
/* On success keep the microcode patch for
* re-apply on resume.
*/
if (error) {
xfree(mc);
mc = NULL;
if (error == 1)
{
xfree(mc_old);
return 0;
}
uci->mc.mc_amd = mc;
out:
xfree(equiv_cpu_table);
equiv_cpu_table = NULL;
xfree(mc_amd);
uci->mc.mc_amd = mc_old;
return error;
}
static int microcode_resume_match(int cpu, struct cpu_signature *nsig)
static int microcode_resume_match(int cpu, const void *mc)
{
return 0;
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
struct microcode_amd *mc_amd = uci->mc.mc_amd;
const struct microcode_amd *src = mc;
int res = microcode_fits(src, cpu);
if ( res <= 0 )
return res;
if ( src != mc_amd )
{
xfree(mc_amd);
mc_amd = xmalloc_bytes(sizeof(*src) + src->equiv_cpu_table_size);
uci->mc.mc_amd = mc_amd;
if ( !mc_amd )
return -ENOMEM;
memcpy(mc_amd, src, UCODE_MAX_SIZE);
memcpy(mc_amd->equiv_cpu_table, src->equiv_cpu_table,
src->equiv_cpu_table_size);
}
return 1;
}
static const struct microcode_ops microcode_amd_ops = {
@@ -317,4 +361,4 @@ static __init int microcode_init_amd(void)
microcode_ops = &microcode_amd_ops;
return 0;
}
__initcall(microcode_init_amd);
presmp_initcall(microcode_init_amd);
+43 -14
View File
@@ -30,12 +30,43 @@
#include <xen/spinlock.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/microcode.h>
#define pr_debug(x...) ((void)0)
struct microcode_header_intel {
unsigned int hdrver;
unsigned int rev;
unsigned int date;
unsigned int sig;
unsigned int cksum;
unsigned int ldrver;
unsigned int pf;
unsigned int datasize;
unsigned int totalsize;
unsigned int reserved[3];
};
struct microcode_intel {
struct microcode_header_intel hdr;
unsigned int bits[0];
};
/* microcode format is extended from prescott processors */
struct extended_signature {
unsigned int sig;
unsigned int pf;
unsigned int cksum;
};
struct extended_sigtable {
unsigned int count;
unsigned int cksum;
unsigned int reserved[3];
struct extended_signature sigs[0];
};
#define DEFAULT_UCODE_DATASIZE (2000)
#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel))
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
@@ -98,7 +129,8 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
}
static inline int microcode_update_match(
int cpu_num, struct microcode_header_intel *mc_header, int sig, int pf)
int cpu_num, const struct microcode_header_intel *mc_header,
int sig, int pf)
{
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu_num);
@@ -200,11 +232,11 @@ static int microcode_sanity_check(void *mc)
* return 1 - found update
* return < 0 - error
*/
static int get_matching_microcode(void *mc, int cpu)
static int get_matching_microcode(const void *mc, int cpu)
{
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
struct microcode_header_intel *mc_header = mc;
struct extended_sigtable *ext_header;
const struct microcode_header_intel *mc_header = mc;
const struct extended_sigtable *ext_header;
unsigned long total_size = get_totalsize(mc_header);
int ext_sigcount, i;
struct extended_signature *ext_sig;
@@ -229,6 +261,8 @@ static int get_matching_microcode(void *mc, int cpu)
}
return 0;
find:
if ( uci->mc.mc_intel && uci->mc.mc_intel->hdr.rev >= mc_header->rev )
return 0;
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);
@@ -239,10 +273,8 @@ static int get_matching_microcode(void *mc, int cpu)
return -ENOMEM;
}
/* free previous update file */
xfree(uci->mc.mc_intel);
memcpy(new_mc, mc, total_size);
xfree(uci->mc.mc_intel);
uci->mc.mc_intel = new_mc;
return 1;
}
@@ -361,12 +393,9 @@ static int cpu_request_microcode(int cpu, const void *buf, size_t size)
return error;
}
static int microcode_resume_match(int cpu, struct cpu_signature *nsig)
static int microcode_resume_match(int cpu, const void *mc)
{
struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu);
return (sigmatch(nsig->sig, uci->cpu_sig.sig, nsig->pf, uci->cpu_sig.pf) &&
(uci->cpu_sig.rev > nsig->rev));
return get_matching_microcode(mc, cpu);
}
static const struct microcode_ops microcode_intel_ops = {
@@ -382,4 +411,4 @@ static __init int microcode_init_intel(void)
microcode_ops = &microcode_intel_ops;
return 0;
}
__initcall(microcode_init_intel);
presmp_initcall(microcode_init_intel);
+1 -63
View File
@@ -7,74 +7,12 @@ struct cpu_signature;
struct ucode_cpu_info;
struct microcode_ops {
int (*microcode_resume_match)(int cpu, struct cpu_signature *nsig);
int (*microcode_resume_match)(int cpu, const void *mc);
int (*cpu_request_microcode)(int cpu, const void *buf, size_t size);
int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
int (*apply_microcode)(int cpu);
};
struct microcode_header_intel {
unsigned int hdrver;
unsigned int rev;
unsigned int date;
unsigned int sig;
unsigned int cksum;
unsigned int ldrver;
unsigned int pf;
unsigned int datasize;
unsigned int totalsize;
unsigned int reserved[3];
};
struct microcode_intel {
struct microcode_header_intel hdr;
unsigned int bits[0];
};
/* microcode format is extended from prescott processors */
struct extended_signature {
unsigned int sig;
unsigned int pf;
unsigned int cksum;
};
struct extended_sigtable {
unsigned int count;
unsigned int cksum;
unsigned int reserved[3];
struct extended_signature sigs[0];
};
struct equiv_cpu_entry {
uint32_t installed_cpu;
uint32_t fixed_errata_mask;
uint32_t fixed_errata_compare;
uint16_t equiv_cpu;
uint16_t reserved;
} __attribute__((packed));
struct microcode_header_amd {
uint32_t data_code;
uint32_t patch_id;
uint8_t mc_patch_data_id[2];
uint8_t mc_patch_data_len;
uint8_t init_flag;
uint32_t mc_patch_data_checksum;
uint32_t nb_dev_id;
uint32_t sb_dev_id;
uint16_t processor_rev_id;
uint8_t nb_rev_id;
uint8_t sb_rev_id;
uint8_t bios_api_rev;
uint8_t reserved1[3];
uint32_t match_reg[8];
} __attribute__((packed));
struct microcode_amd {
struct microcode_header_amd hdr;
unsigned int mpb[0];
};
struct cpu_signature {
unsigned int sig;
unsigned int pf;
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.