Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0399f732 authored by Borislav Petkov's avatar Borislav Petkov Committed by Ingo Molnar
Browse files

x86/microcode/amd: Do not overwrite final patch levels

A certain number of patch levels of applied microcode should not
be overwritten by the microcode loader, otherwise bad things
will happen.

Check those and abort update if the current core has one of
those final patch levels applied by the BIOS. 32-bit needs
special handling, of course.

See https://bugzilla.suse.com/show_bug.cgi?id=913996

 for more
info.

Tested-by: default avatarPeter Kirchgeßner <pkirchgessner@t-online.de>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Cc: Borislav Petkov <bp@alien8.de>
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>
Cc: Tony Luck <tony.luck@intel.com>
Link: http://lkml.kernel.org/r/1444641762-9437-7-git-send-email-bp@alien8.de


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 2eff73c0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -76,5 +76,5 @@ static inline int __init save_microcode_in_initrd_amd(void) { return -EINVAL; }
void reload_ucode_amd(void) {}
#endif

extern bool check_current_patch_level(u32 *rev);
extern bool check_current_patch_level(u32 *rev, bool early);
#endif /* _ASM_X86_MICROCODE_AMD_H */
+33 −5
Original line number Diff line number Diff line
@@ -177,6 +177,16 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
	return patch_size;
}

/*
 * Those patch levels cannot be updated to newer ones and thus should be final.
 */
static u32 final_levels[] = {
	0x01000098,
	0x0100009f,
	0x010000af,
	0, /* T-101 terminator */
};

/*
 * Check the current patch level on this CPU.
 *
@@ -187,13 +197,31 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
 *  - true: if update should stop
 *  - false: otherwise
 */
bool check_current_patch_level(u32 *rev)
bool check_current_patch_level(u32 *rev, bool early)
{
	u32 dummy;
	u32 lvl, dummy, i;
	bool ret = false;
	u32 *levels;

	native_rdmsr(MSR_AMD64_PATCH_LEVEL, lvl, dummy);

	native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy);
	if (IS_ENABLED(CONFIG_X86_32) && early)
		levels = (u32 *)__pa_nodebug(&final_levels);
	else
		levels = final_levels;

	return false;
	for (i = 0; levels[i]; i++) {
		if (lvl == levels[i]) {
			lvl = 0;
			ret = true;
			break;
		}
	}

	if (rev)
		*rev = lvl;

	return ret;
}

int __apply_microcode_amd(struct microcode_amd *mc_amd)
@@ -229,7 +257,7 @@ int apply_microcode_amd(int cpu)
	mc_amd  = p->data;
	uci->mc = p->data;

	if (check_current_patch_level(&rev))
	if (check_current_patch_level(&rev, false))
		return -1;

	/* need to apply patch? */
+10 −3
Original line number Diff line number Diff line
@@ -196,7 +196,7 @@ static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
		return;
	}

	if (check_current_patch_level(&rev))
	if (check_current_patch_level(&rev, true))
		return;

	while (left > 0) {
@@ -330,7 +330,10 @@ void load_ucode_amd_ap(void)
	if (!container)
		return;

	if (check_current_patch_level(&rev))
	/*
	 * 64-bit runs with paging enabled, thus early==false.
	 */
	if (check_current_patch_level(&rev, false))
		return;

	eax = cpuid_eax(0x00000001);
@@ -422,7 +425,11 @@ void reload_ucode_amd(void)
	struct microcode_amd *mc;
	u32 rev;

	if (check_current_patch_level(&rev))
	/*
	 * early==false because this is a syscore ->resume path and by
	 * that time paging is long enabled.
	 */
	if (check_current_patch_level(&rev, false))
		return;

	mc = (struct microcode_amd *)amd_ucode_patch;