/* * Intel CPU Microcode Update Driver for Linux * * Copyright (C) 2000-2006 Tigran Aivazian * 2006 Shaohua Li * * This driver allows to upgrade microcode on Intel processors * belonging to IA-32 family - PentiumPro, Pentium II, * Pentium III, Xeon, Pentium 4, etc. * * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture * Software Developer's Manual * Order Number 253668 or free download from: * * http://developer.intel.com/design/pentium4/manuals/253668.htm * * For more information, go to http://www.urbanmyth.org/microcode * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static module_t __initdata ucode_mod; static void *(*__initdata ucode_mod_map)(const module_t *); 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 '[|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 ) /* 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, void *(*map)(const module_t *)) { module_t *mod = (module_t *)__va(mbi->mods_addr); if ( ucode_mod_idx < 0 ) 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) ) 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; static DEFINE_SPINLOCK(microcode_mutex); DEFINE_PER_CPU(struct ucode_cpu_info, ucode_cpu_info); struct microcode_info { unsigned int cpu; uint32_t buffer_size; int error; char buffer[1]; }; static void __microcode_fini_cpu(int cpu) { struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); xfree(uci->mc.mc_valid); memset(uci, 0, sizeof(*uci)); } static void microcode_fini_cpu(int cpu) { spin_lock(µcode_mutex); __microcode_fini_cpu(cpu); spin_unlock(µcode_mutex); } 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 ( !microcode_ops ) return 0; spin_lock(µcode_mutex); err = microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig); if ( err ) { __microcode_fini_cpu(cpu); spin_unlock(µcode_mutex); return err; } if ( uci->mc.mc_valid ) { err = microcode_ops->microcode_resume_match(cpu, uci->mc.mc_valid); if ( err >= 0 ) { if ( err ) err = microcode_ops->apply_microcode(cpu); spin_unlock(µcode_mutex); return err; } } nsig = uci->cpu_sig; __microcode_fini_cpu(cpu); uci->cpu_sig = nsig; err = -EIO; for_each_online_cpu ( cpu2 ) { 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(µcode_mutex); return err; } static int microcode_update_cpu(const void *buf, size_t size) { int err; unsigned int cpu = smp_processor_id(); struct ucode_cpu_info *uci = &per_cpu(ucode_cpu_info, cpu); spin_lock(µcode_mutex); err = microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig); if ( likely(!err) ) err = microcode_ops->cpu_request_microcode(cpu, buf, size); else __microcode_fini_cpu(cpu); spin_unlock(µcode_mutex); return err; } static long do_microcode_update(void *_info) { struct microcode_info *info = _info; int error; BUG_ON(info->cpu != smp_processor_id()); error = microcode_update_cpu(info->buffer, info->buffer_size); if ( error ) info->error = error; info->cpu = cpumask_next(info->cpu, &cpu_online_map); if ( info->cpu < nr_cpu_ids ) return continue_hypercall_on_cpu(info->cpu, do_microcode_update, info); error = info->error; xfree(info); return error; } int microcode_update(XEN_GUEST_HANDLE_PARAM(const_void) buf, unsigned long len) { int ret; struct microcode_info *info; if ( len != (uint32_t)len ) return -E2BIG; if ( microcode_ops == NULL ) return -EINVAL; info = xmalloc_bytes(sizeof(*info) + len); if ( info == NULL ) return -ENOMEM; ret = copy_from_guest(info->buffer, buf, len); if ( ret != 0 ) { xfree(info); return ret; } info->buffer_size = len; info->error = 0; info->cpu = cpumask_first(&cpu_online_map); if ( microcode_ops->start_update ) { ret = microcode_ops->start_update(); if ( ret != 0 ) { xfree(info); return ret; } } return continue_hypercall_on_cpu(info->cpu, do_microcode_update, info); } static void __init _do_microcode_update(unsigned long data) { 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); } static int __init microcode_init(void) { void *data; static struct tasklet __initdata tasklet; unsigned int cpu; if ( !microcode_ops ) return 0; if ( !ucode_mod.mod_end && !ucode_blob.size ) return 0; 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 ) goto out; softirq_tasklet_init(&tasklet, _do_microcode_update, (unsigned long)data); for_each_online_cpu ( cpu ) { tasklet_schedule_on_cpu(&tasklet, cpu); do { process_pending_softirqs(); } while ( !cpumask_test_cpu(cpu, &init_mask) ); } out: if ( ucode_blob.size ) xfree(data); else ucode_mod_map(NULL); return 0; } __initcall(microcode_init); 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 ) { if ( ucode_mod.mod_end || ucode_blob.size ) { void *data; size_t len; int rc = 0; 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 ) rc = microcode_update_cpu(data, len); else rc = -ENOMEM; if ( !ucode_blob.size ) ucode_mod_map(NULL); if ( rc ) { if ( ucode_blob.size ) { xfree(ucode_blob.data); ucode_blob.size = 0; ucode_blob.data = NULL; } else ucode_mod.mod_end = 0; } } register_cpu_notifier(µcode_percpu_nfb); } return 0; } presmp_initcall(microcode_presmp_init);