diff options
Diffstat (limited to 'xen/arch/x86/microcode.c')
-rw-r--r-- | xen/arch/x86/microcode.c | 158 |
1 files changed, 143 insertions, 15 deletions
diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c index fbbf95bcec..e6344cf182 100644 --- a/xen/arch/x86/microcode.c +++ b/xen/arch/x86/microcode.c @@ -33,6 +33,7 @@ #include <xen/spinlock.h> #include <xen/tasklet.h> #include <xen/guest_access.h> +#include <xen/earlycpio.h> #include <asm/msr.h> #include <asm/processor.h> @@ -45,19 +46,123 @@ static signed int __initdata ucode_mod_idx; static bool_t __initdata ucode_mod_forced; static cpumask_t __initdata init_mask; +/* + * If we scan the initramfs.cpio for the early microcode code + * and find it, then 'ucode_blob' will contain the pointer + * and the size of said blob. It is allocated from Xen's heap + * memory. + */ +struct ucode_mod_blob { + void *data; + size_t size; +}; + +static struct ucode_mod_blob __initdata ucode_blob; +/* + * By default we will NOT parse the multiboot modules to see if there is + * cpio image with the microcode images. + */ +static bool_t __initdata ucode_scan; + void __init microcode_set_module(unsigned int idx) { ucode_mod_idx = idx; ucode_mod_forced = 1; } +/* + * The format is '[<integer>|scan]'. Both options are optional. + * If the EFI has forced which of the multiboot payloads is to be used, + * no parsing will be attempted. + */ static void __init parse_ucode(char *s) { - if ( !ucode_mod_forced ) + if ( ucode_mod_forced ) /* Forced by EFI */ + return; + + if ( !strncmp(s, "scan", 4) ) + ucode_scan = 1; + else ucode_mod_idx = simple_strtol(s, NULL, 0); } custom_param("ucode", parse_ucode); +/* + * 8MB ought to be enough. + */ +#define MAX_EARLY_CPIO_MICROCODE (8 << 20) + +void __init microcode_scan_module( + unsigned long *module_map, + const multiboot_info_t *mbi, + void *(*bootmap)(const module_t *)) +{ + module_t *mod = (module_t *)__va(mbi->mods_addr); + uint64_t *_blob_start; + unsigned long _blob_size; + struct cpio_data cd; + long offset; + const char *p = NULL; + int i; + + ucode_blob.size = 0; + if ( !ucode_scan ) + return; + + if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) + p = "kernel/x86/microcode/AuthenticAMD.bin"; + else if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL ) + p = "kernel/x86/microcode/GenuineIntel.bin"; + else + return; + + /* + * Try all modules and see whichever could be the microcode blob. + */ + for ( i = 1 /* Ignore dom0 kernel */; i < mbi->mods_count; i++ ) + { + if ( !test_bit(i, module_map) ) + continue; + + _blob_start = bootmap(&mod[i]); + _blob_size = mod[i].mod_end; + if ( !_blob_start ) + { + printk("Could not map multiboot module #%d (size: %ld)\n", + i, _blob_size); + continue; + } + cd.data = NULL; + cd.size = 0; + cd = find_cpio_data(p, _blob_start, _blob_size, &offset /* ignore */); + if ( cd.data ) + { + /* + * This is an arbitrary check - it would be sad if the blob + * consumed most of the memory and did not allow guests + * to launch. + */ + if ( cd.size > MAX_EARLY_CPIO_MICROCODE ) + { + printk("Multiboot %d microcode payload too big! (%ld, we can do %d)\n", + i, cd.size, MAX_EARLY_CPIO_MICROCODE); + goto err; + } + ucode_blob.size = cd.size; + ucode_blob.data = xmalloc_bytes(cd.size); + if ( !ucode_blob.data ) + cd.data = NULL; + else + memcpy(ucode_blob.data, cd.data, cd.size); + } + bootmap(NULL); + if ( cd.data ) + break; + } + return; +err: + bootmap(NULL); +} void __init microcode_grab_module( unsigned long *module_map, const multiboot_info_t *mbi, @@ -69,9 +174,12 @@ void __init microcode_grab_module( ucode_mod_idx += mbi->mods_count; if ( ucode_mod_idx <= 0 || ucode_mod_idx >= mbi->mods_count || !__test_and_clear_bit(ucode_mod_idx, module_map) ) - return; + goto scan; ucode_mod = mod[ucode_mod_idx]; ucode_mod_map = map; +scan: + if ( ucode_scan ) + microcode_scan_module(module_map, mbi, map); } const struct microcode_ops *microcode_ops; @@ -236,7 +344,10 @@ int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len) static void __init _do_microcode_update(unsigned long data) { - microcode_update_cpu((void *)data, ucode_mod.mod_end); + void *_data = (void *)data; + size_t len = ucode_blob.size ? ucode_blob.size : ucode_mod.mod_end; + + microcode_update_cpu(_data, len); cpumask_set_cpu(smp_processor_id(), &init_mask); } @@ -246,18 +357,19 @@ static int __init microcode_init(void) static struct tasklet __initdata tasklet; unsigned int cpu; - if ( !microcode_ops || !ucode_mod.mod_end ) + if ( !microcode_ops ) + return 0; + + if ( !ucode_mod.mod_end && !ucode_blob.size ) return 0; - data = ucode_mod_map(&ucode_mod); + data = ucode_blob.size ? ucode_blob.data : ucode_mod_map(&ucode_mod); + if ( !data ) return -ENOMEM; if ( microcode_ops->start_update && microcode_ops->start_update() != 0 ) - { - ucode_mod_map(NULL); - return 0; - } + goto out; softirq_tasklet_init(&tasklet, _do_microcode_update, (unsigned long)data); @@ -269,7 +381,11 @@ static int __init microcode_init(void) } while ( !cpumask_test_cpu(cpu, &init_mask) ); } - ucode_mod_map(NULL); +out: + if ( ucode_blob.size ) + xfree(data); + else + ucode_mod_map(NULL); return 0; } @@ -298,14 +414,26 @@ static int __init microcode_presmp_init(void) { if ( microcode_ops ) { - if ( ucode_mod.mod_end ) + if ( ucode_mod.mod_end || ucode_blob.size ) { - void *data = ucode_mod_map(&ucode_mod); - + void *data; + size_t len; + + if ( ucode_blob.size ) + { + len = ucode_blob.size; + data = ucode_blob.data; + } + else + { + len = ucode_mod.mod_end; + data = ucode_mod_map(&ucode_mod); + } if ( data ) - microcode_update_cpu(data, ucode_mod.mod_end); + microcode_update_cpu(data, len); - ucode_mod_map(NULL); + if ( !ucode_blob.size ) + ucode_mod_map(NULL); } register_cpu_notifier(µcode_percpu_nfb); |