diff options
author | Jan Beulich <jbeulich@suse.com> | 2011-12-01 17:55:26 +0100 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2011-12-01 17:55:26 +0100 |
commit | 0bda96825f7f8fcb32a609b3d507e8f0a0adf8a2 (patch) | |
tree | 7c2659998f0dd83ac266787b5b373be4769861f4 /xen/arch/x86/microcode_amd.c | |
parent | 943730ecffa8807ebb5c47f01a686e021f3c0390 (diff) | |
download | xen-0bda96825f7f8fcb32a609b3d507e8f0a0adf8a2.tar.gz xen-0bda96825f7f8fcb32a609b3d507e8f0a0adf8a2.tar.bz2 xen-0bda96825f7f8fcb32a609b3d507e8f0a0adf8a2.zip |
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>
Diffstat (limited to 'xen/arch/x86/microcode_amd.c')
-rw-r--r-- | xen/arch/x86/microcode_amd.c | 148 |
1 files changed, 96 insertions, 52 deletions
diff --git a/xen/arch/x86/microcode_amd.c b/xen/arch/x86/microcode_amd.c index 9dcdb9d079..d868a132ed 100644 --- a/xen/arch/x86/microcode_amd.c +++ b/xen/arch/x86/microcode_amd.c @@ -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); - if ( error ) + mc_amd = xmalloc_bytes(sizeof(*mc_amd) + buf_pos[2]); + if ( !mc_amd ) { - printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); - return -EINVAL; + printk(KERN_ERR "microcode: error! " + "Can not allocate memory for microcode patch\n"); + return -ENOMEM; } - mc = xmalloc_bytes(UCODE_MAX_SIZE); - if ( mc == NULL ) + error = install_equiv_cpu_table(mc_amd, buf, &offset); + if ( error ) { - printk(KERN_ERR "microcode: error! " - "Can not allocate memory for microcode patch\n"); - error = -ENOMEM; - goto out; + xfree(mc_amd); + printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); + return -EINVAL; } + 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 = µcode_amd_ops; return 0; } -__initcall(microcode_init_amd); +presmp_initcall(microcode_init_amd); |