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:
parent
c8ae067f26
commit
6c5456474e
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue