x86/microcode: Fix loading precedence

So it can happen that even with builtin microcode,
CONFIG_BLK_DEV_INITRD=y gets forgotten enabled.

Or, even with that disabled, an initrd image gets supplied by the boot
loader, by omission or is simply forgotten there. And since we do look
at boot_params.hdr.ramdisk_* to know whether we have received an initrd,
we might get puzzled.

So let's just make the loader look for builtin microcode first and if
found, ignore the ramdisk image.

If no builtin found, it falls back to scanning the supplied initrd, of
course.

For that, we move all the initrd scanning in a separate
__scan_microcode_initrd() function and fall back to it only if
load_builtin_intel_microcode() has failed.

Reported-and-tested-by: Gabriel Craciunescu <nix.or.die@gmail.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1465225850-7352-2-git-send-email-bp@alien8.de
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Borislav Petkov 2016-06-06 17:10:42 +02:00 committed by Ingo Molnar
parent c8ae067f26
commit 6c5456474e
3 changed files with 133 additions and 102 deletions

View File

@ -145,28 +145,4 @@ static inline bool
get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; } get_builtin_firmware(struct cpio_data *cd, const char *name) { return false; }
#endif #endif
static inline unsigned long get_initrd_start(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
return initrd_start;
#else
return 0;
#endif
}
static inline unsigned long get_initrd_start_addr(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
#ifdef CONFIG_X86_32
unsigned long *initrd_start_p = (unsigned long *)__pa_nodebug(&initrd_start);
return (unsigned long)__pa_nodebug(*initrd_start_p);
#else
return get_initrd_start();
#endif
#else /* CONFIG_BLK_DEV_INITRD */
return 0;
#endif
}
#endif /* _ASM_X86_MICROCODE_H */ #endif /* _ASM_X86_MICROCODE_H */

View File

@ -61,19 +61,20 @@ static u16 this_equiv_id;
static struct cpio_data ucode_cpio; static struct cpio_data ucode_cpio;
/*
* Microcode patch container file is prepended to the initrd in cpio format.
* See Documentation/x86/early-microcode.txt
*/
static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin";
static struct cpio_data __init find_ucode_in_initrd(void) static struct cpio_data __init find_ucode_in_initrd(void)
{ {
#ifdef CONFIG_BLK_DEV_INITRD
long offset = 0; long offset = 0;
char *path; char *path;
void *start; void *start;
size_t size; size_t size;
/*
* Microcode patch container file is prepended to the initrd in cpio
* format. See Documentation/x86/early-microcode.txt
*/
static __initdata char ucode_path[] = "kernel/x86/microcode/AuthenticAMD.bin";
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
struct boot_params *p; struct boot_params *p;
@ -89,9 +90,12 @@ static struct cpio_data __init find_ucode_in_initrd(void)
path = ucode_path; path = ucode_path;
start = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET); start = (void *)(boot_params.hdr.ramdisk_image + PAGE_OFFSET);
size = boot_params.hdr.ramdisk_size; size = boot_params.hdr.ramdisk_size;
#endif #endif /* !CONFIG_X86_32 */
return find_cpio_data(path, start, size, &offset); return find_cpio_data(path, start, size, &offset);
#else
return (struct cpio_data){ NULL, 0, "" };
#endif
} }
static size_t compute_container_size(u8 *data, u32 total_size) static size_t compute_container_size(u8 *data, u32 total_size)
@ -289,11 +293,11 @@ void __init load_ucode_amd_bsp(unsigned int family)
size = &ucode_cpio.size; size = &ucode_cpio.size;
#endif #endif
cp = find_ucode_in_initrd();
if (!cp.data) {
if (!load_builtin_amd_microcode(&cp, family)) if (!load_builtin_amd_microcode(&cp, family))
cp = find_ucode_in_initrd();
if (!(cp.data && cp.size))
return; return;
}
*data = cp.data; *data = cp.data;
*size = cp.size; *size = cp.size;

View File

@ -51,6 +51,12 @@ static struct mc_saved_data {
struct microcode_intel **mc_saved; struct microcode_intel **mc_saved;
} mc_saved_data; } mc_saved_data;
/* Microcode blobs within the initrd. 0 if builtin. */
static struct ucode_blobs {
unsigned long start;
bool valid;
} blobs;
static enum ucode_state static enum ucode_state
load_microcode_early(struct microcode_intel **saved, load_microcode_early(struct microcode_intel **saved,
unsigned int num_saved, struct ucode_cpu_info *uci) unsigned int num_saved, struct ucode_cpu_info *uci)
@ -532,37 +538,6 @@ static bool __init load_builtin_intel_microcode(struct cpio_data *cp)
#endif #endif
} }
static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin";
static __init enum ucode_state
scan_microcode(struct mc_saved_data *mcs, unsigned long *mc_ptrs,
unsigned long start, unsigned long size,
struct ucode_cpu_info *uci)
{
struct cpio_data cd;
long offset = 0;
#ifdef CONFIG_X86_32
char *p = (char *)__pa_nodebug(ucode_name);
#else
char *p = ucode_name;
#endif
cd.data = NULL;
cd.size = 0;
/* try built-in microcode if no initrd */
if (!size) {
if (!load_builtin_intel_microcode(&cd))
return UCODE_ERROR;
} else {
cd = find_cpio_data(p, (void *)start, size, &offset);
if (!cd.data)
return UCODE_ERROR;
}
return get_matching_model_microcode(start, cd.data, cd.size,
mcs, mc_ptrs, uci);
}
/* /*
* Print ucode update info. * Print ucode update info.
*/ */
@ -680,14 +655,22 @@ static int apply_microcode_early(struct ucode_cpu_info *uci, bool early)
*/ */
int __init save_microcode_in_initrd_intel(void) int __init save_microcode_in_initrd_intel(void)
{ {
unsigned int count = mc_saved_data.num_saved;
struct microcode_intel *mc_saved[MAX_UCODE_COUNT]; struct microcode_intel *mc_saved[MAX_UCODE_COUNT];
int ret = 0; unsigned int count = mc_saved_data.num_saved;
unsigned long offset = 0;
int ret;
if (!count) if (!count)
return ret; return 0;
copy_ptrs(mc_saved, mc_tmp_ptrs, get_initrd_start(), count); /*
* We have found a valid initrd but it might've been relocated in the
* meantime so get its updated address.
*/
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && blobs.valid)
offset = initrd_start;
copy_ptrs(mc_saved, mc_tmp_ptrs, offset, count);
ret = save_microcode(&mc_saved_data, mc_saved, count); ret = save_microcode(&mc_saved_data, mc_saved, count);
if (ret) if (ret)
@ -698,20 +681,92 @@ int __init save_microcode_in_initrd_intel(void)
return ret; return ret;
} }
static __init enum ucode_state
__scan_microcode_initrd(struct cpio_data *cd, struct ucode_blobs *blbp)
{
#ifdef CONFIG_BLK_DEV_INITRD
long offset = 0;
static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin";
char *p = IS_ENABLED(CONFIG_X86_32) ? (char *)__pa_nodebug(ucode_name)
: ucode_name;
# ifdef CONFIG_X86_32
unsigned long start = 0, size;
struct boot_params *params;
params = (struct boot_params *)__pa_nodebug(&boot_params);
size = params->hdr.ramdisk_size;
/*
* Set start only if we have an initrd image. We cannot use initrd_start
* because it is not set that early yet.
*/
start = (size ? params->hdr.ramdisk_image : 0);
# else /* CONFIG_X86_64 */
unsigned long start = 0, size;
size = (u64)boot_params.ext_ramdisk_size << 32;
size |= boot_params.hdr.ramdisk_size;
if (size) {
start = (u64)boot_params.ext_ramdisk_image << 32;
start |= boot_params.hdr.ramdisk_image;
start += PAGE_OFFSET;
}
# endif
*cd = find_cpio_data(p, (void *)start, size, &offset);
if (cd->data) {
blbp->start = start;
blbp->valid = true;
return UCODE_OK;
} else
#endif /* CONFIG_BLK_DEV_INITRD */
return UCODE_ERROR;
}
static __init enum ucode_state
scan_microcode(struct mc_saved_data *mcs, unsigned long *mc_ptrs,
struct ucode_cpu_info *uci, struct ucode_blobs *blbp)
{
struct cpio_data cd = { NULL, 0, "" };
enum ucode_state ret;
/* try built-in microcode first */
if (load_builtin_intel_microcode(&cd))
/*
* Invalidate blobs as we might've gotten an initrd too,
* supplied by the boot loader, by mistake or simply forgotten
* there. That's fine, we ignore it since we've found builtin
* microcode already.
*/
blbp->valid = false;
else {
ret = __scan_microcode_initrd(&cd, blbp);
if (ret != UCODE_OK)
return ret;
}
return get_matching_model_microcode(blbp->start, cd.data, cd.size,
mcs, mc_ptrs, uci);
}
static void __init static void __init
_load_ucode_intel_bsp(struct mc_saved_data *mcs, unsigned long *mc_ptrs, _load_ucode_intel_bsp(struct mc_saved_data *mcs, unsigned long *mc_ptrs,
unsigned long start, unsigned long size) struct ucode_blobs *blbp)
{ {
struct ucode_cpu_info uci; struct ucode_cpu_info uci;
enum ucode_state ret; enum ucode_state ret;
collect_cpu_info_early(&uci); collect_cpu_info_early(&uci);
ret = scan_microcode(mcs, mc_ptrs, start, size, &uci); ret = scan_microcode(mcs, mc_ptrs, &uci, blbp);
if (ret != UCODE_OK) if (ret != UCODE_OK)
return; return;
ret = load_microcode(mcs, mc_ptrs, start, &uci); ret = load_microcode(mcs, mc_ptrs, blbp->start, &uci);
if (ret != UCODE_OK) if (ret != UCODE_OK)
return; return;
@ -720,54 +775,50 @@ _load_ucode_intel_bsp(struct mc_saved_data *mcs, unsigned long *mc_ptrs,
void __init load_ucode_intel_bsp(void) void __init load_ucode_intel_bsp(void)
{ {
u64 start, size; struct ucode_blobs *blobs_p;
struct mc_saved_data *mcs;
unsigned long *ptrs;
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
struct boot_params *p; mcs = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
ptrs = (unsigned long *)__pa_nodebug(&mc_tmp_ptrs);
p = (struct boot_params *)__pa_nodebug(&boot_params); blobs_p = (struct ucode_blobs *)__pa_nodebug(&blobs);
size = p->hdr.ramdisk_size;
/*
* Set start only if we have an initrd image. We cannot use initrd_start
* because it is not set that early yet.
*/
start = (size ? p->hdr.ramdisk_image : 0);
_load_ucode_intel_bsp((struct mc_saved_data *)__pa_nodebug(&mc_saved_data),
(unsigned long *)__pa_nodebug(&mc_tmp_ptrs),
start, size);
#else #else
size = boot_params.hdr.ramdisk_size; mcs = &mc_saved_data;
start = (size ? boot_params.hdr.ramdisk_image + PAGE_OFFSET : 0); ptrs = mc_tmp_ptrs;
blobs_p = &blobs;
_load_ucode_intel_bsp(&mc_saved_data, mc_tmp_ptrs, start, size);
#endif #endif
_load_ucode_intel_bsp(mcs, ptrs, blobs_p);
} }
void load_ucode_intel_ap(void) void load_ucode_intel_ap(void)
{ {
unsigned long *mcs_tmp_p; struct ucode_blobs *blobs_p;
struct mc_saved_data *mcs_p; struct mc_saved_data *mcs;
struct ucode_cpu_info uci; struct ucode_cpu_info uci;
enum ucode_state ret; enum ucode_state ret;
#ifdef CONFIG_X86_32 unsigned long *ptrs;
mcs_tmp_p = (unsigned long *)__pa_nodebug(mc_tmp_ptrs); #ifdef CONFIG_X86_32
mcs_p = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data); mcs = (struct mc_saved_data *)__pa_nodebug(&mc_saved_data);
ptrs = (unsigned long *)__pa_nodebug(mc_tmp_ptrs);
blobs_p = (struct ucode_blobs *)__pa_nodebug(&blobs);
#else #else
mcs_tmp_p = mc_tmp_ptrs; mcs = &mc_saved_data;
mcs_p = &mc_saved_data; ptrs = mc_tmp_ptrs;
blobs_p = &blobs;
#endif #endif
/* /*
* If there is no valid ucode previously saved in memory, no need to * If there is no valid ucode previously saved in memory, no need to
* update ucode on this AP. * update ucode on this AP.
*/ */
if (!mcs_p->num_saved) if (!mcs->num_saved)
return; return;
collect_cpu_info_early(&uci); collect_cpu_info_early(&uci);
ret = load_microcode(mcs_p, mcs_tmp_p, get_initrd_start_addr(), &uci); ret = load_microcode(mcs, ptrs, blobs_p->start, &uci);
if (ret != UCODE_OK) if (ret != UCODE_OK)
return; return;