Skip to content

Commit

Permalink
Merge branch 'x86-microcode-for-linus' of git://git.kernel.org/pub/sc…
Browse files Browse the repository at this point in the history
…m/linux/kernel/git/tip/tip

Pull x86 microcode loading updates from Ingo Molnar:
 "Update documentation, improve robustness and fix a memory leak"

* 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/microcode/intel: Improve microcode patches saving flow
  x86/microcode: Document the three loading methods
  x86/microcode/AMD: Free unneeded patch before exit from update_cache()
  • Loading branch information
torvalds committed Sep 4, 2017
2 parents f29139b + aa78c1c commit 0098410
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 84 deletions.
70 changes: 0 additions & 70 deletions Documentation/x86/early-microcode.txt

This file was deleted.

137 changes: 137 additions & 0 deletions Documentation/x86/microcode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
The Linux Microcode Loader

Authors: Fenghua Yu <[email protected]>
Borislav Petkov <[email protected]>

The kernel has a x86 microcode loading facility which is supposed to
provide microcode loading methods in the OS. Potential use cases are
updating the microcode on platforms beyond the OEM End-Of-Life support,
and updating the microcode on long-running systems without rebooting.

The loader supports three loading methods:

1. Early load microcode
=======================

The kernel can update microcode very early during boot. Loading
microcode early can fix CPU issues before they are observed during
kernel boot time.

The microcode is stored in an initrd file. During boot, it is read from
it and loaded into the CPU cores.

The format of the combined initrd image is microcode in (uncompressed)
cpio format followed by the (possibly compressed) initrd image. The
loader parses the combined initrd image during boot.

The microcode files in cpio name space are:

on Intel: kernel/x86/microcode/GenuineIntel.bin
on AMD : kernel/x86/microcode/AuthenticAMD.bin

During BSP (BootStrapping Processor) boot (pre-SMP), the kernel
scans the microcode file in the initrd. If microcode matching the
CPU is found, it will be applied in the BSP and later on in all APs
(Application Processors).

The loader also saves the matching microcode for the CPU in memory.
Thus, the cached microcode patch is applied when CPUs resume from a
sleep state.

Here's a crude example how to prepare an initrd with microcode (this is
normally done automatically by the distribution, when recreating the
initrd, so you don't really have to do it yourself. It is documented
here for future reference only).

---
#!/bin/bash

if [ -z "$1" ]; then
echo "You need to supply an initrd file"
exit 1
fi

INITRD="$1"

DSTDIR=kernel/x86/microcode
TMPDIR=/tmp/initrd

rm -rf $TMPDIR

mkdir $TMPDIR
cd $TMPDIR
mkdir -p $DSTDIR

if [ -d /lib/firmware/amd-ucode ]; then
cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
fi

if [ -d /lib/firmware/intel-ucode ]; then
cat /lib/firmware/intel-ucode/* > $DSTDIR/GenuineIntel.bin
fi

find . | cpio -o -H newc >../ucode.cpio
cd ..
mv $INITRD $INITRD.orig
cat ucode.cpio $INITRD.orig > $INITRD

rm -rf $TMPDIR
---

The system needs to have the microcode packages installed into
/lib/firmware or you need to fixup the paths above if yours are
somewhere else and/or you've downloaded them directly from the processor
vendor's site.

2. Late loading
===============

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.

The /dev/cpu/microcode method is deprecated because it needs a special
userspace tool for that.

The easier method is simply installing the microcode packages your distro
supplies and running:

# echo 1 > /sys/devices/system/cpu/microcode/reload

as root.

The loading mechanism looks for microcode blobs in
/lib/firmware/{intel-ucode,amd-ucode}. The default distro installation
packages already put them there.

3. Builtin microcode
====================

The loader supports also loading of a builtin microcode supplied through
the regular firmware builtin method CONFIG_FIRMWARE_IN_KERNEL. Only
64-bit is currently supported.

Here's an example:

CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 amd-ucode/microcode_amd_fam15h.bin"
CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"

This basically means, you have the following tree structure locally:

/lib/firmware/
|-- amd-ucode
...
| |-- microcode_amd_fam15h.bin
...
|-- intel-ucode
...
| |-- 06-3a-09
...

so that the build system can find those files and integrate them into
the final kernel image. The early loader finds them and applies them.

Needless to say, this method is not the most flexible one because it
requires rebuilding the kernel each time updated microcode from the CPU
vendor is available.
5 changes: 4 additions & 1 deletion arch/x86/kernel/cpu/microcode/amd.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,12 @@ static void update_cache(struct ucode_patch *new_patch)

list_for_each_entry(p, &microcode_cache, plist) {
if (p->equiv_cpu == new_patch->equiv_cpu) {
if (p->patch_id >= new_patch->patch_id)
if (p->patch_id >= new_patch->patch_id) {
/* we already have the latest patch */
kfree(new_patch->data);
kfree(new_patch);
return;
}

list_replace(&p->plist, &new_patch->plist);
kfree(p->data);
Expand Down
27 changes: 14 additions & 13 deletions arch/x86/kernel/cpu/microcode/intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,18 @@ static bool microcode_matches(struct microcode_header_intel *mc_header,
return false;
}

static struct ucode_patch *__alloc_microcode_buf(void *data, unsigned int size)
static struct ucode_patch *memdup_patch(void *data, unsigned int size)
{
struct ucode_patch *p;

p = kzalloc(sizeof(struct ucode_patch), GFP_KERNEL);
if (!p)
return ERR_PTR(-ENOMEM);
return NULL;

p->data = kmemdup(data, size, GFP_KERNEL);
if (!p->data) {
kfree(p);
return ERR_PTR(-ENOMEM);
return NULL;
}

return p;
Expand All @@ -183,8 +183,8 @@ static void save_microcode_patch(void *data, unsigned int size)
if (mc_hdr->rev <= mc_saved_hdr->rev)
continue;

p = __alloc_microcode_buf(data, size);
if (IS_ERR(p))
p = memdup_patch(data, size);
if (!p)
pr_err("Error allocating buffer %p\n", data);
else
list_replace(&iter->plist, &p->plist);
Expand All @@ -196,24 +196,25 @@ static void save_microcode_patch(void *data, unsigned int size)
* newly found.
*/
if (!prev_found) {
p = __alloc_microcode_buf(data, size);
if (IS_ERR(p))
p = memdup_patch(data, size);
if (!p)
pr_err("Error allocating buffer for %p\n", data);
else
list_add_tail(&p->plist, &microcode_cache);
}

if (!p)
return;

/*
* Save for early loading. On 32-bit, that needs to be a physical
* address as the APs are running from physical addresses, before
* paging has been enabled.
*/
if (p) {
if (IS_ENABLED(CONFIG_X86_32))
intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p->data);
else
intel_ucode_patch = p->data;
}
if (IS_ENABLED(CONFIG_X86_32))
intel_ucode_patch = (struct microcode_intel *)__pa_nodebug(p->data);
else
intel_ucode_patch = p->data;
}

static int microcode_sanity_check(void *mc, int print_err)
Expand Down

0 comments on commit 0098410

Please sign in to comment.