aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch
diff options
context:
space:
mode:
authorKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2013-09-27 10:22:55 +0200
committerJan Beulich <jbeulich@suse.com>2013-09-27 10:22:55 +0200
commit155587481e392e4261038364e2761aab27f597ed (patch)
treeefc95aa3facbcb9eed170bbff0cb7cde3ad9fe82 /xen/arch
parent0dcfb88fb838ad6f8558f2952feb2f09dc34470f (diff)
downloadxen-155587481e392e4261038364e2761aab27f597ed.tar.gz
xen-155587481e392e4261038364e2761aab27f597ed.tar.bz2
xen-155587481e392e4261038364e2761aab27f597ed.zip
x86/microcode: Scan the initramfs payload for microcode blob
The Linux kernel is able to update the microcode during early bootup via inspection of the initramfs blob to see if there is an cpio image with certain microcode files. Linux is able to function with two (or more) cpio archives in the initrd b/c it unpacks all of the cpio archives. The format of the early initramfs is nicely documented in Linux's Documentation/x86/early-microcode.txt: Early load microcode ==================== By Fenghua Yu <fenghua.yu@intel.com> Kernel can update microcode in early phase of boot time. Loading microcode early can fix CPU issues before they are observed during kernel boot time. Microcode is stored in an initrd file. The microcode is read from the initrd file and loaded to CPUs during boot time. The format of the combined initrd image is microcode in cpio format followed by the initrd image (maybe compressed). Kernel parses the combined initrd image during boot time. The microcode file in cpio name space is: kernel/x86/microcode/GenuineIntel.bin During BSP boot (before SMP starts), if the kernel finds the microcode file in the initrd file, it parses the microcode and saves matching microcode in memory. If matching microcode is found, it will be uploaded in BSP and later on in all APs. The cached microcode patch is applied when CPUs resume from a sleep state. There are two legacy user space interfaces to load microcode, either through /dev/cpu/microcode or through /sys/devices/system/cpu/microcode/reload file in sysfs. In addition to these two legacy methods, the early loading method described here is the third method with which microcode can be uploaded to a system's CPUs. The following example script shows how to generate a new combined initrd file in /boot/initrd-3.5.0.ucode.img with original microcode microcode.bin and original initrd image /boot/initrd-3.5.0.img. mkdir initrd cd initrd mkdir kernel mkdir kernel/x86 mkdir kernel/x86/microcode cp ../microcode.bin kernel/x86/microcode/GenuineIntel.bin find .|cpio -oc >../ucode.cpio cd .. cat ucode.cpio /boot/initrd-3.5.0.img >/boot/initrd-3.5.0.ucode.img As such this code inspects the initrd to see if the microcode signatures are present and if so updates the hypervisor. The option to turn this scan on/off is gated by the 'ucode' parameter. The options are now: 'scan' Scan for the microcode in any multiboot payload. <index> Attempt to load microcode blob (not the cpio archive format) from the multiboot payload number. This option alters slightly the 'ucode' parameter by only allowing either parameter: ucode=[<index>|scan] Implementation wise the ucode_blob is defined as __initdata. That is OK from the viewpoint of suspend/resume as the the underlaying architecture microcode (microcode_intel or microcode_amd) end up saving the blob in 'struct ucode_cpu_info' which is a per-cpu data structure (see ucode_cpu_info). They end up saving it when doing the pre-SMP (for CPU0) and SMP (for the rest) microcode loading. Naturally if one does a hypercall to update the microcode and it is newer, then the old per-cpu data is replaced. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Acked-by: Keir Fraser <keir@xen.org>
Diffstat (limited to 'xen/arch')
-rw-r--r--xen/arch/x86/microcode.c158
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(&microcode_percpu_nfb);