diff options
author | Jan Beulich <jbeulich@suse.com> | 2011-12-01 17:57:54 +0100 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2011-12-01 17:57:54 +0100 |
commit | 436fb462abf5673af105e0f2f0640a598286d0fe (patch) | |
tree | 6f1880e47cae89dcb546fa97d315a3de703dfe40 /xen/arch/x86/microcode.c | |
parent | 0bda96825f7f8fcb32a609b3d507e8f0a0adf8a2 (diff) | |
download | xen-436fb462abf5673af105e0f2f0640a598286d0fe.tar.gz xen-436fb462abf5673af105e0f2f0640a598286d0fe.tar.bz2 xen-436fb462abf5673af105e0f2f0640a598286d0fe.zip |
x86/microcode: enable boot time (pre-Dom0) loading
Largely as a result of the continuing resistance of Linux maintainers
to accept a microcode loading patch for pv-ops Xen kernels, this
follows the suggested route and provides a means to load microcode
updates without the assistance of Dom0, thus also addressing eventual
problems in the hardware much earlier.
This leverages the fact that via the multiboot protocol another blob
of data can be easily added in the form of just an extra module. Since
microcode data cannot reliably be recognized by looking at the
provided data, this requires (in the non-EFI case) the use of a
command line parameter ("ucode=<number>") to identify which of the
modules is to be parsed for an eventual microcode update (in the EFI
case the module is being identified in the config file, and hence the
command line argument, if given, will be ignored).
This required to adjust the XSM module determination logic accordingly.
The format of the data to be provided is the raw binary blob already
used for AMD CPUs, and the output of the intel-microcode2ucode utility
for the Intel case (either the per-(family,model,stepping) file or -
to make things easier for distro-s integration-wise - simply the
concatenation of all of them).
In order to not convert the spin_lock() in microcode_update_cpu() (and
then obviously also all other uses on microcode_mutex) to
spin_lock_irqsave() (which would be undesirable for the hypercall
context in which the function also runs), the boot time handling gets
done using a tasklet (instead of using on_selected_cpus()).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
Diffstat (limited to 'xen/arch/x86/microcode.c')
-rw-r--r-- | xen/arch/x86/microcode.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c index b71622f889..d9e6a4c58d 100644 --- a/xen/arch/x86/microcode.c +++ b/xen/arch/x86/microcode.c @@ -29,13 +29,49 @@ #include <xen/notifier.h> #include <xen/sched.h> #include <xen/smp.h> +#include <xen/softirq.h> #include <xen/spinlock.h> +#include <xen/tasklet.h> #include <xen/guest_access.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/setup.h> #include <asm/microcode.h> +static module_t __initdata ucode_mod; +static void *(*__initdata ucode_mod_map)(const module_t *); +static unsigned int __initdata ucode_mod_idx; +static bool_t __initdata ucode_mod_forced; +static cpumask_t __initdata init_mask; + +void __init microcode_set_module(unsigned int idx) +{ + ucode_mod_idx = idx; + ucode_mod_forced = 1; +} + +static void __init parse_ucode(char *s) +{ + if ( !ucode_mod_forced ) + ucode_mod_idx = simple_strtoul(s, NULL, 0); +} +custom_param("ucode", parse_ucode); + +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 || ucode_mod_idx >= mbi->mods_count || + !__test_and_clear_bit(ucode_mod_idx, module_map) ) + return; + ucode_mod = mod[ucode_mod_idx]; + ucode_mod_map = map; +} + const struct microcode_ops *microcode_ops; static DEFINE_SPINLOCK(microcode_mutex); @@ -183,6 +219,41 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf, unsigned long len) return continue_hypercall_on_cpu(info->cpu, do_microcode_update, info); } +static void __init _do_microcode_update(unsigned long data) +{ + microcode_update_cpu((void *)data, ucode_mod.mod_end); + 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 || !ucode_mod.mod_end ) + return 0; + + data = ucode_mod_map(&ucode_mod); + if ( !data ) + return -ENOMEM; + + 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) ); + } + + ucode_mod_map(NULL); + + return 0; +} +__initcall(microcode_init); + static int microcode_percpu_callback( struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -205,7 +276,20 @@ static struct notifier_block microcode_percpu_nfb = { static int __init microcode_presmp_init(void) { if ( microcode_ops ) + { + if ( ucode_mod.mod_end ) + { + void *data = ucode_mod_map(&ucode_mod); + + if ( data ) + microcode_update_cpu(data, ucode_mod.mod_end); + + ucode_mod_map(NULL); + } + register_cpu_notifier(µcode_percpu_nfb); + } + return 0; } presmp_initcall(microcode_presmp_init); |