Skip to contentSkip to conversation

Updates to mce_amd_inj module

PATCH In review Aravind Gopalakrishnan 4 participants 17 messages Sep 25, 2015 – Oct 12, 2015

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v2 cover Updates to mce_amd_inj module

This patchset is mostly a resend of earlier patches which got acceptance into maintainer's tree but did not make it into upstream. Original versions of patches 2 and 3 of this patchset-
a. http://marc.info/?l=linux-edac&m=143327679901253&w=2
b. http://marc.info/?l=linux-edac&m=143392472509250&w=2

Changes from original versions-
Patch 2: Earlier version did not include the update to README file.
That was because README attribute was introduced in separate patch (http://marc.info/?l=linux-edac&m=143327679301249&w=2)
But subsequently, only parts of it made it upstream.

Patch 3: NBCFG register offset definition exists already in
drivers/edac/amd64_edac.h which was included in earlier version. But any dependency with drivers/edac/ was removed when the file moved to arch/x86/ras. So, I have redefined it here. I can move it to a different place if required.

With changes to the location of the module, I am adapting the patches on top of latest tip.

Patch 1: Abort write file operation on invalid input
Patch 2: Extend flags attribute to accept values of 'df', 'th'.
These values will be used to trigger deferred error/threshold error apic interrupts respectively.
Patch 3: Modify injection mechanism for bank 4 errors. Since they are
logged or reported only on NBC, we make sure that we inject on the correct core here.

Changes from V1 (per Boris' comments)
- Instead of ignoring unwanted characters, return early on wrong input from user as this way, we can save a copy from user input for bad values.
- With above change, modified patch title and commit message a bit.
- Use 2 letter strings to indicate error injection for Deferred/Threshold error interrupts too.

Aravind Gopalakrishnan (3):
RAS, mce_amd_inj: Return early on invalid input RAS, mce_amd_inj: Add capability to trigger apic interrupts RAS, mce_amd_inj: Inject errors on NBC for bank 4 errors

 arch/x86/ras/mce_amd_inj.c | 109 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 102 insertions(+), 7 deletions(-)
Signature · 1 line
2.4.0
#1 lore raw

PATCH v2 1/3 RAS, mce_amd_inj: Return early on invalid input

Invalid input such as these are currently reported on dmesg- $> echo sweet > flags

[  122.079139] flags_write: Invalid flags value: et

Even if the 'flags' attribute has been updated correctly- $> cat flags
sw

But the input as a whole is wrong and we should not be writing anything to the file. Therefore, modifying the behavior in this patch to return EINVAL on bad input strings

---
 arch/x86/ras/mce_amd_inj.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/x86/ras/mce_amd_inj.c +3 -6
index 17e35b5..4fd8bb9 100644
@@ -129,12 +129,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 {
 	char buf[MAX_FLAG_OPT_SIZE], *__buf;
 	int err;
-	size_t ret;
 
 	if (cnt > MAX_FLAG_OPT_SIZE)
-		cnt = MAX_FLAG_OPT_SIZE;
-
-	ret = cnt;
+		return -EINVAL;
 
 	if (copy_from_user(&buf, ubuf, cnt))
 		return -EFAULT;
@@ -150,9 +147,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 		return err;
 	}
 
-	*ppos += ret;
+	*ppos += cnt;
 
-	return ret;
+	return cnt;
 }
 
 static const struct file_operations flags_fops = {
Signature · 1 line
2.4.0
#2 lore raw

PATCH v2 2/3 RAS, mce_amd_inj: Add capability to trigger apic interrupts

In reply to Aravind Gopalakrishnan

With this extension to the flags attribute, deferred error interrupts and threshold interrupts can be triggered to test the apic interrupt handler functionality for these type of errors

Update README section about the same too.

---
 arch/x86/ras/mce_amd_inj.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)
arch/x86/ras/mce_amd_inj.c +43 -1
index 4fd8bb9..794ca1f 100644
@@ -34,12 +34,16 @@ static u8 n_banks;
 enum injection_type {
 	SW_INJ = 0,	/* SW injection, simply decode the error */
 	HW_INJ,		/* Trigger a #MC */
+	DFR_INT_INJ,    /* Trigger Deferred error interrupt */
+	THR_INT_INJ,    /* Trigger threshold interrupt */
 	N_INJ_TYPES,
 };
 
 static const char * const flags_options[] = {
 	[SW_INJ] = "sw",
 	[HW_INJ] = "hw",
+	[DFR_INT_INJ] = "df",
+	[THR_INT_INJ] = "th",
 	NULL
 };
 
@@ -182,6 +186,16 @@ static void trigger_mce(void *info)
 	asm volatile("int $18");
 }
 
+static void trigger_dfr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
+}
+
+static void trigger_thr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
+}
+
 static void do_inject(void)
 {
 	u64 mcg_status = 0;
@@ -202,6 +216,20 @@ static void do_inject(void)
 	if (!(i_mce.status & MCI_STATUS_PCC))
 		mcg_status |= MCG_STATUS_RIPV;
 
+	if (inj_type == DFR_INT_INJ) {
+		/*
+		 * Ensure necessary status bits for deferred errors:
+		 * a. MCx_STATUS[Deferred] is set -
+		 *    This is to ensure the error will be handled by the
+		 *    interrupt handler
+		 * b. unset MCx_STATUS[UC]
+		 *    As deferred errors are _not_ UC
+		 */
+
+		i_mce.status |= MCI_STATUS_DEFERRED;
+		i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
+	}
+
 	get_online_cpus();
 	if (!cpu_online(cpu))
 		goto err;
@@ -222,7 +250,16 @@ static void do_inject(void)
 
 	toggle_hw_mce_inject(cpu, false);
 
-	smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	switch (inj_type) {
+	case DFR_INT_INJ:
+		smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
+		break;
+	case THR_INT_INJ:
+		smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
+		break;
+	default:
+		smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	}
 
 err:
 	put_online_cpus();
@@ -287,6 +324,11 @@ static const char readme_msg[] =
 "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
 "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
 "\t    before injecting.\n"
+"\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
+"\t    error APIC interrupt handler to handle the error if the feature is \n"
+"\t    is present in hardware. \n"
+"\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
+"\t    APIC interrupt handler to handle the error. \n"
 "\n";
 
 static ssize_t
Signature · 1 line
2.4.0
#3 lore raw

PATCH v2 3/3 RAS, mce_amd_inj: Inject errors on NBC for bank 4 errors

In reply to Aravind Gopalakrishnan

Bank 4 MCEs are logged and reported only on the node base core (NBC) in a socket. Refer to the D18F3x44[NbMcaToMstCpuEn] field in Fam10h and later BKDGs. The node base core (NBC) is the lowest numbered core in the node.

This patch ensures that we inject the error on the NBC for bank 4 errors. Otherwise, triggering #MC or APIC interrupts on a core which is not the NBC would not have any effect on the system, i.e we would not see any relevant output on kernel logs for the error we just injected.

---
 arch/x86/ras/mce_amd_inj.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
arch/x86/ras/mce_amd_inj.c +56 -0
index 794ca1f..42ab672 100644
@@ -17,7 +17,9 @@
 #include <linux/cpu.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+#include <linux/pci.h>
 #include <asm/mce.h>
+#include <asm/amd_nb.h>
 
 #include "../kernel/cpu/mcheck/mce-internal.h"
 
@@ -30,6 +32,7 @@ static struct dentry *dfs_inj;
 static u8 n_banks;
 
 #define MAX_FLAG_OPT_SIZE	3
+#define NBCFG			0x44
 
 enum injection_type {
 	SW_INJ = 0,	/* SW injection, simply decode the error */
@@ -196,6 +199,45 @@ static void trigger_thr_int(void *info)
 	asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
 }
 
+static u32 get_nbc_for_node(int node_id)
+{
+	struct cpuinfo_x86 *c = &boot_cpu_data;
+	u32 cores_per_node;
+
+	cores_per_node = c->x86_max_cores / amd_get_nodes_per_socket();
+
+	return cores_per_node * node_id;
+}
+
+static void toggle_nb_mca_mst_cpu(u16 nid)
+{
+	struct pci_dev *F3 = node_to_amd_nb(nid)->misc;
+	u32 val;
+	int err;
+
+	if (!F3)
+		return;
+
+	err = pci_read_config_dword(F3, NBCFG, &val);
+	if (err) {
+		pr_err("%s: Error reading F%dx%03x.\n",
+		       __func__, PCI_FUNC(F3->devfn), NBCFG);
+		return;
+	}
+
+	if (val & BIT(27))
+		return;
+
+	pr_err("%s: Set D18F3x44[NbMcaToMstCpuEn] which BIOS hasn't done.\n",
+	       __func__);
+
+	val |= BIT(27);
+	err = pci_write_config_dword(F3, NBCFG, val);
+	if (err)
+		pr_err("%s: Error writing F%dx%03x.\n",
+		       __func__, PCI_FUNC(F3->devfn), NBCFG);
+}
+
 static void do_inject(void)
 {
 	u64 mcg_status = 0;
@@ -230,6 +272,20 @@ static void do_inject(void)
 		i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
 	}
 
+	/*
+	 * For multi node CPUs, logging and reporting of bank 4 errors happens
+	 * only on the node base core. Refer to D18F3x44[NbMcaToMstCpuEn] for
+	 * Fam10h and later BKDGs.
+	 */
+	if (static_cpu_has(X86_FEATURE_AMD_DCM) && b == 4) {
+		/*
+		 * BIOS sets D18F3x44[NbMcaToMstCpuEn] by default. But make
+		 * sure of it here just in case.
+		 */
+		toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu));
+		cpu = get_nbc_for_node(amd_get_nb_id(cpu));
+	}
+
 	get_online_cpus();
 	if (!cpu_online(cpu))
 		goto err;
Signature · 1 line
2.4.0
#4 lore raw
In reply to Aravind Gopalakrishnan
Quoted 7 lines · Aravind Gopalakrishnan

Aravind Gopalakrishnan (3):
RAS, mce_amd_inj: Return early on invalid input RAS, mce_amd_inj: Add capability to trigger apic interrupts RAS, mce_amd_inj: Inject errors on NBC for bank 4 errors

 arch/x86/ras/mce_amd_inj.c | 109 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 102 insertions(+), 7 deletions(-)

All applied,
thanks.

Signature · 5 lines
Regards/Gruss,
    Boris.

SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
-- 
maintainer
#5 lore raw

cover tip-queue 2015-10-12

From: Borislav Petkov <[email protected]>

Hi Ingo,

here are some more patches for tip.

I sent the first one two weeks ago but it doesn't look like it has found its way into tip so resending here again. It corrects what we should be actually doing in the MCA code when offlining a CPU on Intel.

The mce_amd_inj ones are adding the fuinctionality to inject deferred and thresholding errors on AMD.

Finally, the microcode fixes are the ones which we had queued for 4.3 but a bug on 32-bit with CONFIG_PARAVIRT stopped them from going anywhere. They're fixed now.

Please apply,
thanks.

Aravind Gopalakrishnan (3):
x86/ras/mce_amd_inj: Return early on invalid input x86/ras/mce_amd_inj: Trigger deferred and thresholding errors
interrupts
x86/ras/mce_amd_inj: Inject bank 4 errors on the NBC

Ashok Raj (1):
x86/mce: Don't clear shared banks on Intel when offlining CPUs

Borislav Petkov (2):
x86/microcode/amd: Extract current patch level read to a function x86/microcode/amd: Do not overwrite final patch levels

maintainer
#6 lore raw

PATCH 1/6 x86/mce: Don't clear shared banks on Intel when offlining CPUs

From: Ashok Raj <[email protected]>

It is not safe to clear global MCi_CTL banks during CPU offline or suspend/resume operations. These MSRs are either thread-scoped (meaning private to a thread), or core-scoped (private to threads in that core only), or with a socket scope: visible and controllable from all threads in the socket.

When we offline a single CPU, clearing those MCi_CTL bits will stop signaling for all the shared, i.e., socket-wide resources, such as LLC, iMC, etc.

In addition, it might be possible to compromise the integrity of an Intel Secure Guard eXtentions (SGX) system if the attacker has control of the host system and is able to inject errors which would be otherwise ignored when MCi_CTL bits are cleared.

Hence on SGX enabled systems, if MCi_CTL is cleared, SGX gets disabled.

---
 arch/x86/kernel/cpu/mcheck/mce.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)
arch/x86/kernel/cpu/mcheck/mce.c +20 -10
index 9d014b82a124..17b5ec6edcb6 100644
@@ -2042,7 +2042,7 @@ int __init mcheck_init(void)
  * Disable machine checks on suspend and shutdown. We can't really handle
  * them later.
  */
-static int mce_disable_error_reporting(void)
+static void mce_disable_error_reporting(void)
 {
 	int i;
 
@@ -2052,17 +2052,32 @@ static int mce_disable_error_reporting(void)
 		if (b->init)
 			wrmsrl(MSR_IA32_MCx_CTL(i), 0);
 	}
-	return 0;
+	return;
+}
+
+static void vendor_disable_error_reporting(void)
+{
+	/*
+	 * Don't clear on Intel CPUs. Some of these MSRs are socket-wide.
+	 * Disabling them for just a single offlined CPU is bad, since it will
+	 * inhibit reporting for all shared resources on the socket like the
+	 * last level cache (LLC), the integrated memory controller (iMC), etc.
+	 */
+	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+		return;
+
+	mce_disable_error_reporting();
 }
 
 static int mce_syscore_suspend(void)
 {
-	return mce_disable_error_reporting();
+	vendor_disable_error_reporting();
+	return 0;
 }
 
 static void mce_syscore_shutdown(void)
 {
-	mce_disable_error_reporting();
+	vendor_disable_error_reporting();
 }
 
 /*
@@ -2342,19 +2357,14 @@ static void mce_device_remove(unsigned int cpu)
 static void mce_disable_cpu(void *h)
 {
 	unsigned long action = *(unsigned long *)h;
-	int i;
 
 	if (!mce_available(raw_cpu_ptr(&cpu_info)))
 		return;
 
 	if (!(action & CPU_TASKS_FROZEN))
 		cmci_clear();
-	for (i = 0; i < mca_cfg.banks; i++) {
-		struct mce_bank *b = &mce_banks[i];
 
-		if (b->init)
-			wrmsrl(MSR_IA32_MCx_CTL(i), 0);
-	}
+	vendor_disable_error_reporting();
 }
 
 static void mce_reenable_cpu(void *h)
Signature · 1 line
2.3.5
maintainer
#7 lore raw

PATCH 2/6 x86/ras/mce_amd_inj: Return early on invalid input

In reply to Borislav Petkov

From: Aravind Gopalakrishnan <[email protected]>

Invalid inputs such as these are currently reported in dmesg as failing:

$> echo sweet > flags

  [  122.079139] flags_write: Invalid flags value: et

even though the 'flags' attribute has been updated correctly:

$> cat flags
sw

This is because userspace keeps writing the remaining buffer until it encounters an error.

However, the input as a whole is wrong and we should not be writing anything to the file. Therefore, correct flags_write() to return -EINVAL immediately on bad input strings.

---
 arch/x86/ras/mce_amd_inj.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/x86/ras/mce_amd_inj.c +3 -6
index 17e35b5bf779..4fd8bb9b90b9 100644
@@ -129,12 +129,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 {
 	char buf[MAX_FLAG_OPT_SIZE], *__buf;
 	int err;
-	size_t ret;
 
 	if (cnt > MAX_FLAG_OPT_SIZE)
-		cnt = MAX_FLAG_OPT_SIZE;
-
-	ret = cnt;
+		return -EINVAL;
 
 	if (copy_from_user(&buf, ubuf, cnt))
 		return -EFAULT;
@@ -150,9 +147,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 		return err;
 	}
 
-	*ppos += ret;
+	*ppos += cnt;
 
-	return ret;
+	return cnt;
 }
 
 static const struct file_operations flags_fops = {
Signature · 1 line
2.3.5
#8 lore raw

[tip:ras/core] x86/ras/mce_amd_inj: Return early on invalid input

x86/ras/mce_amd_inj: Return early on invalid input
Commit-ID:  85c9306d44f757d2fb3b0e3e399080a025315c7f
Gitweb:     http://git.kernel.org/tip/85c9306d44f757d2fb3b0e3e399080a025315c7f
Author:     Aravind Gopalakrishnan <[email protected]>
AuthorDate: Mon, 12 Oct 2015 11:22:38 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Mon, 12 Oct 2015 16:15:47 +0200

x86/ras/mce_amd_inj: Return early on invalid input

Invalid inputs such as these are currently reported in dmesg as failing:

$> echo sweet > flags

  [  122.079139] flags_write: Invalid flags value: et

even though the 'flags' attribute has been updated correctly:

$> cat flags
sw

This is because userspace keeps writing the remaining buffer until it encounters an error.

However, the input as a whole is wrong and we should not be writing anything to the file. Therefore, correct flags_write() to return -EINVAL immediately on bad input strings.

---
 arch/x86/ras/mce_amd_inj.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
arch/x86/ras/mce_amd_inj.c +3 -6
index 17e35b5..4fd8bb9 100644
@@ -129,12 +129,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 {
 	char buf[MAX_FLAG_OPT_SIZE], *__buf;
 	int err;
-	size_t ret;
 
 	if (cnt > MAX_FLAG_OPT_SIZE)
-		cnt = MAX_FLAG_OPT_SIZE;
-
-	ret = cnt;
+		return -EINVAL;
 
 	if (copy_from_user(&buf, ubuf, cnt))
 		return -EFAULT;
@@ -150,9 +147,9 @@ static ssize_t flags_write(struct file *filp, const char __user *ubuf,
 		return err;
 	}
 
-	*ppos += ret;
+	*ppos += cnt;
 
-	return ret;
+	return cnt;
 }
 
 static const struct file_operations flags_fops = {
maintainer
#9 lore raw

PATCH 3/6 x86/ras/mce_amd_inj: Trigger deferred and thresholding errors interrupts

In reply to Borislav Petkov

From: Aravind Gopalakrishnan <[email protected]>

Add the capability to trigger deferred error interrupts and threshold interrupts in order to test the APIC interrupt handler functionality for these type of errors.

Update README section about the same too.

Reported by: kbuild test robot <[email protected]>

Cc "H. Peter Anvin" <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc x86-ml <[email protected]>
[ Cleanup comments. ]
[ Include asm/irq_vectors.h directly so that misc randbuilds don't fail. ]
---
 arch/x86/ras/mce_amd_inj.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)
arch/x86/ras/mce_amd_inj.c +41 -1
index 4fd8bb9b90b9..4d3bafb540c2 100644
@@ -17,7 +17,9 @@
 #include <linux/cpu.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+
 #include <asm/mce.h>
+#include <asm/irq_vectors.h>
 
 #include "../kernel/cpu/mcheck/mce-internal.h"
 
@@ -34,12 +36,16 @@ static u8 n_banks;
 enum injection_type {
 	SW_INJ = 0,	/* SW injection, simply decode the error */
 	HW_INJ,		/* Trigger a #MC */
+	DFR_INT_INJ,    /* Trigger Deferred error interrupt */
+	THR_INT_INJ,    /* Trigger threshold interrupt */
 	N_INJ_TYPES,
 };
 
 static const char * const flags_options[] = {
 	[SW_INJ] = "sw",
 	[HW_INJ] = "hw",
+	[DFR_INT_INJ] = "df",
+	[THR_INT_INJ] = "th",
 	NULL
 };
 
@@ -182,6 +188,16 @@ static void trigger_mce(void *info)
 	asm volatile("int $18");
 }
 
+static void trigger_dfr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
+}
+
+static void trigger_thr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
+}
+
 static void do_inject(void)
 {
 	u64 mcg_status = 0;
@@ -202,6 +218,16 @@ static void do_inject(void)
 	if (!(i_mce.status & MCI_STATUS_PCC))
 		mcg_status |= MCG_STATUS_RIPV;
 
+	/*
+	 * Ensure necessary status bits for deferred errors:
+	 * - MCx_STATUS[Deferred]: make sure it is a deferred error
+	 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
+	 */
+	if (inj_type == DFR_INT_INJ) {
+		i_mce.status |= MCI_STATUS_DEFERRED;
+		i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
+	}
+
 	get_online_cpus();
 	if (!cpu_online(cpu))
 		goto err;
@@ -222,7 +248,16 @@ static void do_inject(void)
 
 	toggle_hw_mce_inject(cpu, false);
 
-	smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	switch (inj_type) {
+	case DFR_INT_INJ:
+		smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
+		break;
+	case THR_INT_INJ:
+		smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
+		break;
+	default:
+		smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	}
 
 err:
 	put_online_cpus();
@@ -287,6 +322,11 @@ static const char readme_msg[] =
 "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
 "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
 "\t    before injecting.\n"
+"\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
+"\t    error APIC interrupt handler to handle the error if the feature is \n"
+"\t    is present in hardware. \n"
+"\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
+"\t    APIC interrupt handler to handle the error. \n"
 "\n";
 
 static ssize_t
Signature · 1 line
2.3.5
#10 lore raw

[tip:ras/core] x86/ras/mce_amd_inj: Trigger deferred and thresholding errors interrupts

x86/ras/mce_amd_inj: Trigger deferred and thresholding errors interrupts
Commit-ID:  a1300e50529795cd605da6a015d4944a18921db0
Gitweb:     http://git.kernel.org/tip/a1300e50529795cd605da6a015d4944a18921db0
Author:     Aravind Gopalakrishnan <[email protected]>
AuthorDate: Mon, 12 Oct 2015 11:22:39 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Mon, 12 Oct 2015 16:15:47 +0200

x86/ras/mce_amd_inj: Trigger deferred and thresholding errors interrupts

Add the capability to trigger deferred error interrupts and threshold interrupts in order to test the APIC interrupt handler functionality for these type of errors.

Update README section about the same too.

Reported by: kbuild test robot <[email protected]>

[ Cleanup comments. ]
[ Include asm/irq_vectors.h directly so that misc randbuilds don't fail. ]
Cc Borislav Petkov <[email protected]>
Cc H. Peter Anvin <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Tony Luck <[email protected]>
---
 arch/x86/ras/mce_amd_inj.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)
arch/x86/ras/mce_amd_inj.c +41 -1
index 4fd8bb9..4d3bafb 100644
@@ -17,7 +17,9 @@
 #include <linux/cpu.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+
 #include <asm/mce.h>
+#include <asm/irq_vectors.h>
 
 #include "../kernel/cpu/mcheck/mce-internal.h"
 
@@ -34,12 +36,16 @@ static u8 n_banks;
 enum injection_type {
 	SW_INJ = 0,	/* SW injection, simply decode the error */
 	HW_INJ,		/* Trigger a #MC */
+	DFR_INT_INJ,    /* Trigger Deferred error interrupt */
+	THR_INT_INJ,    /* Trigger threshold interrupt */
 	N_INJ_TYPES,
 };
 
 static const char * const flags_options[] = {
 	[SW_INJ] = "sw",
 	[HW_INJ] = "hw",
+	[DFR_INT_INJ] = "df",
+	[THR_INT_INJ] = "th",
 	NULL
 };
 
@@ -182,6 +188,16 @@ static void trigger_mce(void *info)
 	asm volatile("int $18");
 }
 
+static void trigger_dfr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (DEFERRED_ERROR_VECTOR));
+}
+
+static void trigger_thr_int(void *info)
+{
+	asm volatile("int %0" :: "i" (THRESHOLD_APIC_VECTOR));
+}
+
 static void do_inject(void)
 {
 	u64 mcg_status = 0;
@@ -202,6 +218,16 @@ static void do_inject(void)
 	if (!(i_mce.status & MCI_STATUS_PCC))
 		mcg_status |= MCG_STATUS_RIPV;
 
+	/*
+	 * Ensure necessary status bits for deferred errors:
+	 * - MCx_STATUS[Deferred]: make sure it is a deferred error
+	 * - MCx_STATUS[UC] cleared: deferred errors are _not_ UC
+	 */
+	if (inj_type == DFR_INT_INJ) {
+		i_mce.status |= MCI_STATUS_DEFERRED;
+		i_mce.status |= (i_mce.status & ~MCI_STATUS_UC);
+	}
+
 	get_online_cpus();
 	if (!cpu_online(cpu))
 		goto err;
@@ -222,7 +248,16 @@ static void do_inject(void)
 
 	toggle_hw_mce_inject(cpu, false);
 
-	smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	switch (inj_type) {
+	case DFR_INT_INJ:
+		smp_call_function_single(cpu, trigger_dfr_int, NULL, 0);
+		break;
+	case THR_INT_INJ:
+		smp_call_function_single(cpu, trigger_thr_int, NULL, 0);
+		break;
+	default:
+		smp_call_function_single(cpu, trigger_mce, NULL, 0);
+	}
 
 err:
 	put_online_cpus();
@@ -287,6 +322,11 @@ static const char readme_msg[] =
 "\t    handle the error. Be warned: might cause system panic if MCi_STATUS[PCC] \n"
 "\t    is set. Therefore, consider setting (debugfs_mountpoint)/mce/fake_panic \n"
 "\t    before injecting.\n"
+"\t  - \"df\": Trigger APIC interrupt for Deferred error. Causes deferred \n"
+"\t    error APIC interrupt handler to handle the error if the feature is \n"
+"\t    is present in hardware. \n"
+"\t  - \"th\": Trigger APIC interrupt for Threshold errors. Causes threshold \n"
+"\t    APIC interrupt handler to handle the error. \n"
 "\n";
 
 static ssize_t
maintainer
#11 lore raw

PATCH 4/6 x86/ras/mce_amd_inj: Inject bank 4 errors on the NBC

In reply to Borislav Petkov

From: Aravind Gopalakrishnan <[email protected]>

Bank 4 MCEs are logged and reported only on the node base core (NBC) in a socket. Refer to the D18F3x44[NbMcaToMstCpuEn] field in Fam10h and later BKDGs. The node base core (NBC) is the lowest numbered core in the node.

This patch ensures that we inject the error on the NBC for bank 4 errors. Otherwise, triggering #MC or APIC interrupts on a core which is not the NBC would not have any effect on the system, i.e. we would not see any relevant output on kernel logs for the error we just injected.

Cc "H. Peter Anvin" <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc x86-ml <[email protected]>
[ Cleanup comments. ]
[ Add a missing dependency on AMD_NB caught by Randy Dunlap. ]
---
 arch/x86/ras/Kconfig       |  4 +---
 arch/x86/ras/mce_amd_inj.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 3 deletions(-)
arch/x86/ras/Kconfig +1 -3
index 10fea5fc821e..df280da34825 100644
@@ -1,11 +1,9 @@
 config AMD_MCE_INJ
 	tristate "Simple MCE injection interface for AMD processors"
-	depends on RAS && EDAC_DECODE_MCE && DEBUG_FS
+	depends on RAS && EDAC_DECODE_MCE && DEBUG_FS && AMD_NB
 	default n
 	help
 	  This is a simple debugfs interface to inject MCEs and test different
 	  aspects of the MCE handling code.
 
 	  WARNING: Do not even assume this interface is staying stable!
-
-
arch/x86/ras/mce_amd_inj.c +52 -0

78 lines not on this page · raw message

Signature · 1 line
2.3.5
#12 lore raw

[tip:ras/core] x86/ras/mce_amd_inj: Inject bank 4 errors on the NBC

x86/ras/mce_amd_inj: Inject bank 4 errors on the NBC
Commit-ID:  fa20a2ed6fff717839ec03b6574ea0affcb58841
Gitweb:     http://git.kernel.org/tip/fa20a2ed6fff717839ec03b6574ea0affcb58841
Author:     Aravind Gopalakrishnan <[email protected]>
AuthorDate: Mon, 12 Oct 2015 11:22:40 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Mon, 12 Oct 2015 16:15:48 +0200

x86/ras/mce_amd_inj: Inject bank 4 errors on the NBC

Bank 4 MCEs are logged and reported only on the node base core (NBC) in a socket. Refer to the D18F3x44[NbMcaToMstCpuEn] field in Fam10h and later BKDGs. The node base core (NBC) is the lowest numbered core in the node.

This patch ensures that we inject the error on the NBC for bank 4 errors. Otherwise, triggering #MC or APIC interrupts on a core which is not the NBC would not have any effect on the system, i.e. we would not see any relevant output on kernel logs for the error we just injected.

[ Cleanup comments. ]
[ Add a missing dependency on AMD_NB caught by Randy Dunlap. ]
Cc Borislav Petkov <[email protected]>
Cc H. Peter Anvin <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Tony Luck <[email protected]>
---
 arch/x86/ras/Kconfig       |  4 +---
 arch/x86/ras/mce_amd_inj.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 3 deletions(-)
arch/x86/ras/Kconfig +1 -3
index 10fea5f..df280da 100644
@@ -1,11 +1,9 @@
 config AMD_MCE_INJ
 	tristate "Simple MCE injection interface for AMD processors"
-	depends on RAS && EDAC_DECODE_MCE && DEBUG_FS
+	depends on RAS && EDAC_DECODE_MCE && DEBUG_FS && AMD_NB
 	default n
 	help
 	  This is a simple debugfs interface to inject MCEs and test different
 	  aspects of the MCE handling code.
 
 	  WARNING: Do not even assume this interface is staying stable!
-
-
arch/x86/ras/mce_amd_inj.c +52 -0

78 lines not on this page · raw message

maintainer
#13 lore raw

PATCH 5/6 x86/microcode/amd: Extract current patch level read to a function

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

Pave the way for checking the current patch level of the microcode in a core. We want to be able to do stuff depending on the patch level - in this case decide whether to update or not. But that will be added in a later patch.

Drop unused local var uci assignment, while at it.

Integrate a fix for 32-bit and CONFIG_PARAVIRT from Takashi Iwai <[email protected]>:

Use native_rdmsr() in check_current_patch_level() because with CONFIG_PARAVIRT enabled and on 32-bit, where we run before paging has been enabled, we cannot deref pv_info yet. Or we could, but we'd need to access its physical address. This way of fixing it is simpler. See
https://bugzilla.suse.com/show_bug.cgi?id=943179 for the background.

---
 arch/x86/include/asm/microcode_amd.h      |  1 +
 arch/x86/kernel/cpu/microcode/amd.c       | 24 ++++++++++++++++++++++--
 arch/x86/kernel/cpu/microcode/amd_early.c | 17 +++++++----------
 3 files changed, 30 insertions(+), 12 deletions(-)
arch/x86/include/asm/microcode_amd.h +1 -0
index ac6d328977a6..9b214e10d499 100644
@@ -76,4 +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);
 #endif /* _ASM_X86_MICROCODE_AMD_H */
arch/x86/kernel/cpu/microcode/amd.c +22 -2
index 12829c3ced3c..2d630138bf3e 100644
@@ -177,6 +177,25 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
 	return patch_size;
 }
 
+/*
+ * Check the current patch level on this CPU.
+ *
+ * @rev: Use it to return the patch level. It is set to 0 in the case of
+ * error.
+ *
+ * Returns:
+ *  - true: if update should stop
+ *  - false: otherwise
+ */
+bool check_current_patch_level(u32 *rev)
+{
+	u32 dummy;
+
+	native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy);
+
+	return false;
+}
+
 int __apply_microcode_amd(struct microcode_amd *mc_amd)
 {
 	u32 rev, dummy;
@@ -197,7 +216,7 @@ int apply_microcode_amd(int cpu)
 	struct microcode_amd *mc_amd;
 	struct ucode_cpu_info *uci;
 	struct ucode_patch *p;
-	u32 rev, dummy;
+	u32 rev;
 
 	BUG_ON(raw_smp_processor_id() != cpu);
 
@@ -210,7 +229,8 @@ int apply_microcode_amd(int cpu)
 	mc_amd  = p->data;
 	uci->mc = p->data;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+	if (check_current_patch_level(&rev))
+		return -1;
 
 	/* need to apply patch? */
 	if (rev >= mc_amd->hdr.patch_id) {
arch/x86/kernel/cpu/microcode/amd_early.c +7 -10
index e8a215a9a345..abb90097582f 100644
@@ -196,9 +196,8 @@ static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
 		return;
 	}
 
-	/* find ucode and update if needed */
-
-	native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
+	if (check_current_patch_level(&rev))
+		return;
 
 	while (left > 0) {
 		struct microcode_amd *mc;
@@ -319,7 +318,6 @@ static void __init get_bsp_sig(void)
 void load_ucode_amd_ap(void)
 {
 	unsigned int cpu = smp_processor_id();
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 	struct equiv_cpu_entry *eq;
 	struct microcode_amd *mc;
 	u32 rev, eax;
@@ -332,10 +330,8 @@ void load_ucode_amd_ap(void)
 	if (!container)
 		return;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
-
-	uci->cpu_sig.rev = rev;
-	uci->cpu_sig.sig = eax;
+	if (check_current_patch_level(&rev))
+		return;
 
 	eax = cpuid_eax(0x00000001);
 	eq  = (struct equiv_cpu_entry *)(container + CONTAINER_HDR_SZ);
@@ -424,9 +420,10 @@ int __init save_microcode_in_initrd_amd(void)
 void reload_ucode_amd(void)
 {
 	struct microcode_amd *mc;
-	u32 rev, eax;
+	u32 rev;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
+	if (check_current_patch_level(&rev))
+		return;
 
 	mc = (struct microcode_amd *)amd_ucode_patch;
 
Signature · 1 line
2.3.5
#14 lore raw

[tip:ras/core] x86/microcode/amd: Extract current patch level read to a function

x86/microcode/amd: Extract current patch level read to a function
Commit-ID:  2eff73c0a11f19ff082a566e3429fbaaca7b8e7b
Gitweb:     http://git.kernel.org/tip/2eff73c0a11f19ff082a566e3429fbaaca7b8e7b
Author:     Borislav Petkov <[email protected]>
AuthorDate: Mon, 12 Oct 2015 11:22:41 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Mon, 12 Oct 2015 16:15:48 +0200

x86/microcode/amd: Extract current patch level read to a function

Pave the way for checking the current patch level of the microcode in a core. We want to be able to do stuff depending on the patch level - in this case decide whether to update or not. But that will be added in a later patch.

Drop unused local var uci assignment, while at it.

Integrate a fix for 32-bit and CONFIG_PARAVIRT from Takashi Iwai:

Use native_rdmsr() in check_current_patch_level() because with CONFIG_PARAVIRT enabled and on 32-bit, where we run before paging has been enabled, we cannot deref pv_info yet. Or we could, but we'd need to access its physical address. This way of fixing it is simpler. See:

https://bugzilla.suse.com/show_bug.cgi?id=943179 for the background.

Cc Borislav Petkov <[email protected]>
Cc H. Peter Anvin <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Takashi Iwai <[email protected]>:
Cc Thomas Gleixner <[email protected]>
Cc Tony Luck <[email protected]>
---
 arch/x86/include/asm/microcode_amd.h      |  1 +
 arch/x86/kernel/cpu/microcode/amd.c       | 24 ++++++++++++++++++++++--
 arch/x86/kernel/cpu/microcode/amd_early.c | 17 +++++++----------
 3 files changed, 30 insertions(+), 12 deletions(-)
arch/x86/include/asm/microcode_amd.h +1 -0
index ac6d328..9b214e1 100644
@@ -76,4 +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);
 #endif /* _ASM_X86_MICROCODE_AMD_H */
arch/x86/kernel/cpu/microcode/amd.c +22 -2
index 12829c3..2d63013 100644
@@ -177,6 +177,25 @@ static unsigned int verify_patch_size(u8 family, u32 patch_size,
 	return patch_size;
 }
 
+/*
+ * Check the current patch level on this CPU.
+ *
+ * @rev: Use it to return the patch level. It is set to 0 in the case of
+ * error.
+ *
+ * Returns:
+ *  - true: if update should stop
+ *  - false: otherwise
+ */
+bool check_current_patch_level(u32 *rev)
+{
+	u32 dummy;
+
+	native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy);
+
+	return false;
+}
+
 int __apply_microcode_amd(struct microcode_amd *mc_amd)
 {
 	u32 rev, dummy;
@@ -197,7 +216,7 @@ int apply_microcode_amd(int cpu)
 	struct microcode_amd *mc_amd;
 	struct ucode_cpu_info *uci;
 	struct ucode_patch *p;
-	u32 rev, dummy;
+	u32 rev;
 
 	BUG_ON(raw_smp_processor_id() != cpu);
 
@@ -210,7 +229,8 @@ int apply_microcode_amd(int cpu)
 	mc_amd  = p->data;
 	uci->mc = p->data;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
+	if (check_current_patch_level(&rev))
+		return -1;
 
 	/* need to apply patch? */
 	if (rev >= mc_amd->hdr.patch_id) {
arch/x86/kernel/cpu/microcode/amd_early.c +7 -10
index e8a215a..abb9009 100644
@@ -196,9 +196,8 @@ static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
 		return;
 	}
 
-	/* find ucode and update if needed */
-
-	native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
+	if (check_current_patch_level(&rev))
+		return;
 
 	while (left > 0) {
 		struct microcode_amd *mc;
@@ -319,7 +318,6 @@ static void __init get_bsp_sig(void)
 void load_ucode_amd_ap(void)
 {
 	unsigned int cpu = smp_processor_id();
-	struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
 	struct equiv_cpu_entry *eq;
 	struct microcode_amd *mc;
 	u32 rev, eax;
@@ -332,10 +330,8 @@ void load_ucode_amd_ap(void)
 	if (!container)
 		return;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
-
-	uci->cpu_sig.rev = rev;
-	uci->cpu_sig.sig = eax;
+	if (check_current_patch_level(&rev))
+		return;
 
 	eax = cpuid_eax(0x00000001);
 	eq  = (struct equiv_cpu_entry *)(container + CONTAINER_HDR_SZ);
@@ -424,9 +420,10 @@ int __init save_microcode_in_initrd_amd(void)
 void reload_ucode_amd(void)
 {
 	struct microcode_amd *mc;
-	u32 rev, eax;
+	u32 rev;
 
-	rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
+	if (check_current_patch_level(&rev))
+		return;
 
 	mc = (struct microcode_amd *)amd_ucode_patch;
 
maintainer
#15 lore raw

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

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

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.

---
 arch/x86/include/asm/microcode_amd.h      |  2 +-
 arch/x86/kernel/cpu/microcode/amd.c       | 38 +++++++++++++++++++++++++++----
 arch/x86/kernel/cpu/microcode/amd_early.c | 13 ++++++++---
 3 files changed, 44 insertions(+), 9 deletions(-)
arch/x86/include/asm/microcode_amd.h +1 -1
index 9b214e10d499..d3e86cfd08fe 100644
@@ -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 */
arch/x86/kernel/cpu/microcode/amd.c +33 -5
index 2d630138bf3e..da922d1e2f71 100644
@@ -178,6 +178,16 @@ static unsigned int verify_patch_size(u8 family, u32 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.
  *
  * @rev: Use it to return the patch level. It is set to 0 in the case of
@@ -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);
+
+	if (IS_ENABLED(CONFIG_X86_32) && early)
+		levels = (u32 *)__pa_nodebug(&final_levels);
+	else
+		levels = final_levels;
+
+	for (i = 0; levels[i]; i++) {
+		if (lvl == levels[i]) {
+			lvl = 0;
+			ret = true;
+			break;
+		}
+	}
 
-	native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy);
+	if (rev)
+		*rev = lvl;
 
-	return false;
+	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? */
arch/x86/kernel/cpu/microcode/amd_early.c +10 -3
index abb90097582f..a54a47b9d8ea 100644
@@ -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;
Signature · 1 line
2.3.5
#16 lore raw

[tip:ras/core] x86/microcode/amd: Do not overwrite final patch levels

x86/microcode/amd: Do not overwrite final patch levels
Commit-ID:  0399f73299f1b7e04de329050f7111b362b7eeb5
Gitweb:     http://git.kernel.org/tip/0399f73299f1b7e04de329050f7111b362b7eeb5
Author:     Borislav Petkov <[email protected]>
AuthorDate: Mon, 12 Oct 2015 11:22:42 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Mon, 12 Oct 2015 16:15:48 +0200

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.

---
 arch/x86/include/asm/microcode_amd.h      |  2 +-
 arch/x86/kernel/cpu/microcode/amd.c       | 38 +++++++++++++++++++++++++++----
 arch/x86/kernel/cpu/microcode/amd_early.c | 13 ++++++++---
 3 files changed, 44 insertions(+), 9 deletions(-)
arch/x86/include/asm/microcode_amd.h +1 -1
index 9b214e1..d3e86cf 100644
@@ -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 */
arch/x86/kernel/cpu/microcode/amd.c +33 -5
index 2d63013..da922d1 100644
@@ -178,6 +178,16 @@ static unsigned int verify_patch_size(u8 family, u32 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.
  *
  * @rev: Use it to return the patch level. It is set to 0 in the case of
@@ -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);
+
+	if (IS_ENABLED(CONFIG_X86_32) && early)
+		levels = (u32 *)__pa_nodebug(&final_levels);
+	else
+		levels = final_levels;
+
+	for (i = 0; levels[i]; i++) {
+		if (lvl == levels[i]) {
+			lvl = 0;
+			ret = true;
+			break;
+		}
+	}
 
-	native_rdmsr(MSR_AMD64_PATCH_LEVEL, *rev, dummy);
+	if (rev)
+		*rev = lvl;
 
-	return false;
+	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? */
arch/x86/kernel/cpu/microcode/amd_early.c +10 -3
index abb9009..a54a47b 100644
@@ -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;