]> xenbits.xensource.com Git - people/royger/xen.git/commitdiff
x86/microcode: enable boot time (pre-Dom0) loading
authorJan Beulich <jbeulich@suse.com>
Thu, 1 Dec 2011 16:57:54 +0000 (17:57 +0100)
committerJan Beulich <jbeulich@suse.com>
Thu, 1 Dec 2011 16:57:54 +0000 (17:57 +0100)
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>
xen/arch/x86/efi/boot.c
xen/arch/x86/microcode.c
xen/arch/x86/setup.c
xen/include/asm-x86/processor.h
xen/include/asm-x86/setup.h
xen/include/xsm/xsm.h
xen/xsm/xsm_core.c
xen/xsm/xsm_policy.c

index 81bcc1be5dc8113664e63992629ecff5ab6d0960..184448c4d8d905dd9dac605d97efa5467393ad24 100644 (file)
@@ -49,6 +49,7 @@ static UINT32 __initdata mdesc_ver;
 static struct file __initdata cfg;
 static struct file __initdata kernel;
 static struct file __initdata ramdisk;
+static struct file __initdata ucode;
 static struct file __initdata xsm;
 
 static multiboot_info_t __initdata mbi = {
@@ -174,6 +175,8 @@ static void __init __attribute__((__noreturn__)) blexit(const CHAR16 *str)
         efi_bs->FreePages(kernel.addr, PFN_UP(kernel.size));
     if ( ramdisk.addr )
         efi_bs->FreePages(ramdisk.addr, PFN_UP(ramdisk.size));
+    if ( ucode.addr )
+        efi_bs->FreePages(ucode.addr, PFN_UP(ucode.size));
     if ( xsm.addr )
         efi_bs->FreePages(xsm.addr, PFN_UP(xsm.size));
 
@@ -806,6 +809,17 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
         efi_bs->FreePool(name.w);
     }
 
+    name.s = get_value(&cfg, section.s, "ucode");
+    if ( !name.s )
+        name.s = get_value(&cfg, "global", "ucode");
+    if ( name.s )
+    {
+        microcode_set_module(mbi.mods_count);
+        split_value(name.s);
+        read_file(dir_handle, s2w(&name), &ucode);
+        efi_bs->FreePool(name.w);
+    }
+
     name.s = get_value(&cfg, section.s, "xsm");
     if ( name.s )
     {
index b71622f889619c4b4ba8201c4d061523e4fa51bd..d9e6a4c58d695a6a7f0654f9079a342c07da8626 100644 (file)
 #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(&microcode_percpu_nfb);
+    }
+
     return 0;
 }
 presmp_initcall(microcode_presmp_init);
index bca36ed1aa70ad3ab496722483ca6d14bbc89775..19a38440c982b5cea30a8fb246c31deed03c3da8 100644 (file)
@@ -550,10 +550,10 @@ void __init __start_xen(unsigned long mbi_p)
 {
     char *memmap_type = NULL;
     char *cmdline, *kextra, *loader;
-    unsigned int initrdidx = 1;
+    unsigned int initrdidx;
     multiboot_info_t *mbi = __va(mbi_p);
     module_t *mod = (module_t *)__va(mbi->mods_addr);
-    unsigned long nr_pages, modules_headroom;
+    unsigned long nr_pages, modules_headroom, *module_map;
     int i, j, e820_warn = 0, bytes = 0;
     bool_t acpi_boot_table_init_done = 0;
     struct ns16550_defaults ns16550 = {
@@ -1229,7 +1229,13 @@ void __init __start_xen(unsigned long mbi_p)
 
     init_IRQ();
 
-    xsm_init(&initrdidx, mbi, bootstrap_map);
+    module_map = xmalloc_array(unsigned long, BITS_TO_LONGS(mbi->mods_count));
+    bitmap_fill(module_map, mbi->mods_count);
+    __clear_bit(0, module_map); /* Dom0 kernel is always first */
+
+    xsm_init(module_map, mbi, bootstrap_map);
+
+    microcode_grab_module(module_map, mbi, bootstrap_map);
 
     timer_init();
 
@@ -1356,6 +1362,12 @@ void __init __start_xen(unsigned long mbi_p)
     if ( xen_cpuidle )
         xen_processor_pmbits |= XEN_PROCESSOR_PM_CX;
 
+    initrdidx = find_first_bit(module_map, mbi->mods_count);
+    if ( bitmap_weight(module_map, mbi->mods_count) > 1 )
+        printk(XENLOG_WARNING
+               "Multiple initrd candidates, picking module #%u\n",
+               initrdidx);
+
     /*
      * We're going to setup domain0 using the module(s) that we stashed safely
      * above our heap. The second module, if present, is an initrd ramdisk.
index cf10ad58db677d7d7107cc2b6088d5ec5e482754..68684b92ae7fdd453cf3251a11df8cb2d35dfeaf 100644 (file)
@@ -599,6 +599,7 @@ int cpuid_hypervisor_leaves( uint32_t idx, uint32_t sub_idx,
 int rdmsr_hypervisor_regs(uint32_t idx, uint64_t *val);
 int wrmsr_hypervisor_regs(uint32_t idx, uint64_t val);
 
+void microcode_set_module(unsigned int);
 int microcode_update(XEN_GUEST_HANDLE(const_void), unsigned long len);
 int microcode_resume_cpu(int cpu);
 
index 874b1a323e4b9ed165c7e558249e19b0d0e3e070..b684f0134812f27382e441eab2d78193bf53e531 100644 (file)
@@ -44,4 +44,7 @@ void discard_initial_images(void);
 int xen_in_range(unsigned long mfn);
 void arch_get_xen_caps(xen_capabilities_info_t *info);
 
+void microcode_grab_module(
+    unsigned long *, const multiboot_info_t *, void *(*)(const module_t *));
+
 #endif
index 1f70e87c507362906b7a3486f9cf257d91e5c3e0..d294f2a51dccefbc22636031a2f56115dc48bdd4 100644 (file)
@@ -454,14 +454,15 @@ static inline long __do_xsm_op (XEN_GUEST_HANDLE(xsm_op_t) op)
 }
 
 #ifdef XSM_ENABLE
-extern int xsm_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
+extern int xsm_init(unsigned long *module_map, const multiboot_info_t *mbi,
                     void *(*bootstrap_map)(const module_t *));
-extern int xsm_policy_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
+extern int xsm_policy_init(unsigned long *module_map,
+                           const multiboot_info_t *mbi,
                            void *(*bootstrap_map)(const module_t *));
 extern int register_xsm(struct xsm_operations *ops);
 extern int unregister_xsm(struct xsm_operations *ops);
 #else
-static inline int xsm_init (unsigned int *initrdidx,
+static inline int xsm_init (unsigned long *module_map,
                             const multiboot_info_t *mbi,
                             void *(*bootstrap_map)(const module_t *))
 {
index 3396b3d7295ea60f98438ed0c5988994637ec7c8..96c8669ac2a4ce2355d746ca2d364b86b29e0923 100644 (file)
@@ -43,7 +43,7 @@ static void __init do_xsm_initcalls(void)
     }
 }
 
-int __init xsm_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
+int __init xsm_init(unsigned long *module_map, const multiboot_info_t *mbi,
                     void *(*bootstrap_map)(const module_t *))
 {
     int ret = 0;
@@ -52,7 +52,7 @@ int __init xsm_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
 
     if ( XSM_MAGIC )
     {
-        ret = xsm_policy_init(initrdidx, mbi, bootstrap_map);
+        ret = xsm_policy_init(module_map, mbi, bootstrap_map);
         if ( ret )
         {
             bootstrap_map(NULL);
index c5b334e7e903587970da56a847f72f8e17205578..a419cf4e8408c8934a778ef07eecbe8db802bd0b 100644 (file)
 
 #include <xsm/xsm.h>
 #include <xen/multiboot.h>
+#include <asm/bitops.h>
 
 char *__initdata policy_buffer = NULL;
 u32 __initdata policy_size = 0;
 
-int xsm_policy_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
+int xsm_policy_init(unsigned long *module_map, const multiboot_info_t *mbi,
                     void *(*bootstrap_map)(const module_t *))
 {
     int i;
@@ -35,10 +36,13 @@ int xsm_policy_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
 
     /*
      * Try all modules and see whichever could be the binary policy.
-     * Adjust the initrdidx if module[1] is the binary policy.
+     * Adjust module_map for the module that is the binary policy.
      */
     for ( i = mbi->mods_count-1; i >= 1; i-- )
     {
+        if ( !test_bit(i, module_map) )
+            continue;
+
         _policy_start = bootstrap_map(mod + i);
         _policy_len   = mod[i].mod_end;
 
@@ -50,8 +54,7 @@ int xsm_policy_init(unsigned int *initrdidx, const multiboot_info_t *mbi,
             printk("Policy len  0x%lx, start at %p.\n",
                    _policy_len,_policy_start);
 
-            if ( i == 1 )
-                *initrdidx = (mbi->mods_count > 2) ? 2 : 0;
+            __clear_bit(i, module_map);
             break;
 
         }