|
@@ -28,7 +28,7 @@
|
|
|
#include <asm/msr.h>
|
|
|
#include <asm/trace/irq_vectors.h>
|
|
|
|
|
|
-#define NR_BLOCKS 9
|
|
|
+#define NR_BLOCKS 5
|
|
|
#define THRESHOLD_MAX 0xFFF
|
|
|
#define INT_TYPE_APIC 0x00020000
|
|
|
#define MASK_VALID_HI 0x80000000
|
|
@@ -49,6 +49,19 @@
|
|
|
#define DEF_LVT_OFF 0x2
|
|
|
#define DEF_INT_TYPE_APIC 0x2
|
|
|
|
|
|
+/* Scalable MCA: */
|
|
|
+
|
|
|
+/* Threshold LVT offset is at MSR0xC0000410[15:12] */
|
|
|
+#define SMCA_THR_LVT_OFF 0xF000
|
|
|
+
|
|
|
+/*
|
|
|
+ * OS is required to set the MCAX bit to acknowledge that it is now using the
|
|
|
+ * new MSR ranges and new registers under each bank. It also means that the OS
|
|
|
+ * will configure deferred errors in the new MCx_CONFIG register. If the bit is
|
|
|
+ * not set, uncorrectable errors will cause a system panic.
|
|
|
+ */
|
|
|
+#define SMCA_MCAX_EN_OFF 0x1
|
|
|
+
|
|
|
static const char * const th_names[] = {
|
|
|
"load_store",
|
|
|
"insn_fetch",
|
|
@@ -84,6 +97,13 @@ struct thresh_restart {
|
|
|
|
|
|
static inline bool is_shared_bank(int bank)
|
|
|
{
|
|
|
+ /*
|
|
|
+ * Scalable MCA provides for only one core to have access to the MSRs of
|
|
|
+ * a shared bank.
|
|
|
+ */
|
|
|
+ if (mce_flags.smca)
|
|
|
+ return false;
|
|
|
+
|
|
|
/* Bank 4 is for northbridge reporting and is thus shared */
|
|
|
return (bank == 4);
|
|
|
}
|
|
@@ -135,6 +155,14 @@ static int lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
|
|
|
}
|
|
|
|
|
|
if (apic != msr) {
|
|
|
+ /*
|
|
|
+ * On SMCA CPUs, LVT offset is programmed at a different MSR, and
|
|
|
+ * the BIOS provides the value. The original field where LVT offset
|
|
|
+ * was set is reserved. Return early here:
|
|
|
+ */
|
|
|
+ if (mce_flags.smca)
|
|
|
+ return 0;
|
|
|
+
|
|
|
pr_err(FW_BUG "cpu %d, invalid threshold interrupt offset %d "
|
|
|
"for bank %d, block %d (MSR%08X=0x%x%08x)\n",
|
|
|
b->cpu, apic, b->bank, b->block, b->address, hi, lo);
|
|
@@ -247,14 +275,65 @@ static void deferred_error_interrupt_enable(struct cpuinfo_x86 *c)
|
|
|
wrmsr(MSR_CU_DEF_ERR, low, high);
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
|
|
|
+ int offset, u32 misc_high)
|
|
|
+{
|
|
|
+ unsigned int cpu = smp_processor_id();
|
|
|
+ struct threshold_block b;
|
|
|
+ int new;
|
|
|
+
|
|
|
+ if (!block)
|
|
|
+ per_cpu(bank_map, cpu) |= (1 << bank);
|
|
|
+
|
|
|
+ memset(&b, 0, sizeof(b));
|
|
|
+ b.cpu = cpu;
|
|
|
+ b.bank = bank;
|
|
|
+ b.block = block;
|
|
|
+ b.address = addr;
|
|
|
+ b.interrupt_capable = lvt_interrupt_supported(bank, misc_high);
|
|
|
+
|
|
|
+ if (!b.interrupt_capable)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ b.interrupt_enable = 1;
|
|
|
+
|
|
|
+ if (mce_flags.smca) {
|
|
|
+ u32 smca_low, smca_high;
|
|
|
+ u32 smca_addr = MSR_AMD64_SMCA_MCx_CONFIG(bank);
|
|
|
+
|
|
|
+ if (!rdmsr_safe(smca_addr, &smca_low, &smca_high)) {
|
|
|
+ smca_high |= SMCA_MCAX_EN_OFF;
|
|
|
+ wrmsr(smca_addr, smca_low, smca_high);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Gather LVT offset for thresholding: */
|
|
|
+ if (rdmsr_safe(MSR_CU_DEF_ERR, &smca_low, &smca_high))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ new = (smca_low & SMCA_THR_LVT_OFF) >> 12;
|
|
|
+ } else {
|
|
|
+ new = (misc_high & MASK_LVTOFF_HI) >> 20;
|
|
|
+ }
|
|
|
+
|
|
|
+ offset = setup_APIC_mce_threshold(offset, new);
|
|
|
+
|
|
|
+ if ((offset == new) && (mce_threshold_vector != amd_threshold_interrupt))
|
|
|
+ mce_threshold_vector = amd_threshold_interrupt;
|
|
|
+
|
|
|
+done:
|
|
|
+ mce_threshold_block_init(&b, offset);
|
|
|
+
|
|
|
+out:
|
|
|
+ return offset;
|
|
|
+}
|
|
|
+
|
|
|
/* cpu init entry point, called from mce.c with preempt off */
|
|
|
void mce_amd_feature_init(struct cpuinfo_x86 *c)
|
|
|
{
|
|
|
- struct threshold_block b;
|
|
|
- unsigned int cpu = smp_processor_id();
|
|
|
u32 low = 0, high = 0, address = 0;
|
|
|
unsigned int bank, block;
|
|
|
- int offset = -1, new;
|
|
|
+ int offset = -1;
|
|
|
|
|
|
for (bank = 0; bank < mca_cfg.banks; ++bank) {
|
|
|
for (block = 0; block < NR_BLOCKS; ++block) {
|
|
@@ -279,29 +358,7 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
|
|
|
(high & MASK_LOCKED_HI))
|
|
|
continue;
|
|
|
|
|
|
- if (!block)
|
|
|
- per_cpu(bank_map, cpu) |= (1 << bank);
|
|
|
-
|
|
|
- memset(&b, 0, sizeof(b));
|
|
|
- b.cpu = cpu;
|
|
|
- b.bank = bank;
|
|
|
- b.block = block;
|
|
|
- b.address = address;
|
|
|
- b.interrupt_capable = lvt_interrupt_supported(bank, high);
|
|
|
-
|
|
|
- if (!b.interrupt_capable)
|
|
|
- goto init;
|
|
|
-
|
|
|
- b.interrupt_enable = 1;
|
|
|
- new = (high & MASK_LVTOFF_HI) >> 20;
|
|
|
- offset = setup_APIC_mce_threshold(offset, new);
|
|
|
-
|
|
|
- if ((offset == new) &&
|
|
|
- (mce_threshold_vector != amd_threshold_interrupt))
|
|
|
- mce_threshold_vector = amd_threshold_interrupt;
|
|
|
-
|
|
|
-init:
|
|
|
- mce_threshold_block_init(&b, offset);
|
|
|
+ offset = prepare_threshold_block(bank, block, address, offset, high);
|
|
|
}
|
|
|
}
|
|
|
|