Merge branch 'ras-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 RAS updates from Ingo Molnar: "The main changes in this development cycle were: - more AMD northbridge support work, mostly in preparation for Fam17h CPUs (Yazen Ghannam, Borislav Petkov) - cleanups/refactorings and fixes (Borislav Petkov, Tony Luck, Yinghai Lu)" * 'ras-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/mce: Include the PPIN in MCE records when available x86/mce/AMD: Add system physical address translation for AMD Fam17h x86/amd_nb: Add SMN and Indirect Data Fabric access for AMD Fam17h x86/amd_nb: Add Fam17h Data Fabric as "Northbridge" x86/amd_nb: Make all exports EXPORT_SYMBOL_GPL x86/amd_nb: Make amd_northbridges internal to amd_nb.c x86/mce/AMD: Reset Threshold Limit after logging error x86/mce/AMD: Fix HWID_MCATYPE calculation by grouping arguments x86/MCE: Correct TSC timestamping of error records x86/RAS: Hide SMCA bank names x86/RAS: Rename smca_bank_names to smca_names x86/RAS: Simplify SMCA HWID descriptor struct x86/RAS: Simplify SMCA bank descriptor struct x86/MCE: Dump MCE to dmesg if no consumers x86/RAS: Add TSC timestamp to the injected MCE x86/MCE: Do not look at panic_on_oops in the severity grading
This commit is contained in:
commit
df5f0f0a02
|
@ -1046,7 +1046,7 @@ config X86_MCE_INTEL
|
|||
config X86_MCE_AMD
|
||||
def_bool y
|
||||
prompt "AMD MCE features"
|
||||
depends on X86_MCE && X86_LOCAL_APIC
|
||||
depends on X86_MCE && X86_LOCAL_APIC && AMD_NB
|
||||
---help---
|
||||
Additional support for AMD specific MCE features such as
|
||||
the DRAM Error Threshold.
|
||||
|
|
|
@ -21,6 +21,10 @@ extern int amd_numa_init(void);
|
|||
extern int amd_get_subcaches(int);
|
||||
extern int amd_set_subcaches(int, unsigned long);
|
||||
|
||||
extern int amd_smn_read(u16 node, u32 address, u32 *value);
|
||||
extern int amd_smn_write(u16 node, u32 address, u32 value);
|
||||
extern int amd_df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo);
|
||||
|
||||
struct amd_l3_cache {
|
||||
unsigned indices;
|
||||
u8 subcaches[4];
|
||||
|
@ -55,6 +59,7 @@ struct threshold_bank {
|
|||
};
|
||||
|
||||
struct amd_northbridge {
|
||||
struct pci_dev *root;
|
||||
struct pci_dev *misc;
|
||||
struct pci_dev *link;
|
||||
struct amd_l3_cache l3_cache;
|
||||
|
@ -66,7 +71,6 @@ struct amd_northbridge_info {
|
|||
u64 flags;
|
||||
struct amd_northbridge *nb;
|
||||
};
|
||||
extern struct amd_northbridge_info amd_northbridges;
|
||||
|
||||
#define AMD_NB_GART BIT(0)
|
||||
#define AMD_NB_L3_INDEX_DISABLE BIT(1)
|
||||
|
@ -74,20 +78,9 @@ extern struct amd_northbridge_info amd_northbridges;
|
|||
|
||||
#ifdef CONFIG_AMD_NB
|
||||
|
||||
static inline u16 amd_nb_num(void)
|
||||
{
|
||||
return amd_northbridges.num;
|
||||
}
|
||||
|
||||
static inline bool amd_nb_has_feature(unsigned feature)
|
||||
{
|
||||
return ((amd_northbridges.flags & feature) == feature);
|
||||
}
|
||||
|
||||
static inline struct amd_northbridge *node_to_amd_nb(int node)
|
||||
{
|
||||
return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL;
|
||||
}
|
||||
u16 amd_nb_num(void);
|
||||
bool amd_nb_has_feature(unsigned int feature);
|
||||
struct amd_northbridge *node_to_amd_nb(int node);
|
||||
|
||||
static inline u16 amd_pci_dev_to_node_id(struct pci_dev *pdev)
|
||||
{
|
||||
|
|
|
@ -193,6 +193,7 @@
|
|||
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
|
||||
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
|
||||
|
||||
#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
|
||||
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
|
||||
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
|
||||
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
|
||||
|
|
|
@ -252,8 +252,10 @@ static inline void cmci_recheck(void) {}
|
|||
|
||||
#ifdef CONFIG_X86_MCE_AMD
|
||||
void mce_amd_feature_init(struct cpuinfo_x86 *c);
|
||||
int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr);
|
||||
#else
|
||||
static inline void mce_amd_feature_init(struct cpuinfo_x86 *c) { }
|
||||
static inline int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) { return -EINVAL; };
|
||||
#endif
|
||||
|
||||
int mce_available(struct cpuinfo_x86 *c);
|
||||
|
@ -356,28 +358,23 @@ enum smca_bank_types {
|
|||
N_SMCA_BANK_TYPES
|
||||
};
|
||||
|
||||
struct smca_bank_name {
|
||||
const char *name; /* Short name for sysfs */
|
||||
const char *long_name; /* Long name for pretty-printing */
|
||||
};
|
||||
#define HWID_MCATYPE(hwid, mcatype) (((hwid) << 16) | (mcatype))
|
||||
|
||||
extern struct smca_bank_name smca_bank_names[N_SMCA_BANK_TYPES];
|
||||
|
||||
#define HWID_MCATYPE(hwid, mcatype) ((hwid << 16) | mcatype)
|
||||
|
||||
struct smca_hwid_mcatype {
|
||||
struct smca_hwid {
|
||||
unsigned int bank_type; /* Use with smca_bank_types for easy indexing. */
|
||||
u32 hwid_mcatype; /* (hwid,mcatype) tuple */
|
||||
u32 xec_bitmap; /* Bitmap of valid ExtErrorCodes; current max is 21. */
|
||||
};
|
||||
|
||||
struct smca_bank_info {
|
||||
struct smca_hwid_mcatype *type;
|
||||
u32 type_instance;
|
||||
struct smca_bank {
|
||||
struct smca_hwid *hwid;
|
||||
/* Instance ID */
|
||||
u32 id;
|
||||
};
|
||||
|
||||
extern struct smca_bank_info smca_banks[MAX_NR_BANKS];
|
||||
extern struct smca_bank smca_banks[MAX_NR_BANKS];
|
||||
|
||||
extern const char *smca_get_long_name(enum smca_bank_types t);
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_X86_MCE_H */
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
#define EFER_FFXSR (1<<_EFER_FFXSR)
|
||||
|
||||
/* Intel MSRs. Some also available on other CPUs */
|
||||
|
||||
#define MSR_PPIN_CTL 0x0000004e
|
||||
#define MSR_PPIN 0x0000004f
|
||||
|
||||
#define MSR_IA32_PERFCTR0 0x000000c1
|
||||
#define MSR_IA32_PERFCTR1 0x000000c2
|
||||
#define MSR_FSB_FREQ 0x000000cd
|
||||
|
|
|
@ -28,6 +28,7 @@ struct mce {
|
|||
__u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */
|
||||
__u64 synd; /* MCA_SYND MSR: only valid on SMCA systems */
|
||||
__u64 ipid; /* MCA_IPID MSR: only valid on SMCA systems */
|
||||
__u64 ppin; /* Protected Processor Inventory Number */
|
||||
};
|
||||
|
||||
#define MCE_GET_RECORD_LEN _IOR('M', 1, int)
|
||||
|
|
|
@ -13,8 +13,20 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <asm/amd_nb.h>
|
||||
|
||||
#define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450
|
||||
#define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463
|
||||
#define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464
|
||||
|
||||
/* Protect the PCI config register pairs used for SMN and DF indirect access. */
|
||||
static DEFINE_MUTEX(smn_mutex);
|
||||
|
||||
static u32 *flush_words;
|
||||
|
||||
static const struct pci_device_id amd_root_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct pci_device_id amd_nb_misc_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
|
||||
|
@ -24,9 +36,10 @@ const struct pci_device_id amd_nb_misc_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
|
||||
{}
|
||||
};
|
||||
EXPORT_SYMBOL(amd_nb_misc_ids);
|
||||
EXPORT_SYMBOL_GPL(amd_nb_misc_ids);
|
||||
|
||||
static const struct pci_device_id amd_nb_link_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
|
||||
|
@ -34,6 +47,7 @@ static const struct pci_device_id amd_nb_link_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -44,8 +58,25 @@ const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = {
|
|||
{ }
|
||||
};
|
||||
|
||||
struct amd_northbridge_info amd_northbridges;
|
||||
EXPORT_SYMBOL(amd_northbridges);
|
||||
static struct amd_northbridge_info amd_northbridges;
|
||||
|
||||
u16 amd_nb_num(void)
|
||||
{
|
||||
return amd_northbridges.num;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_nb_num);
|
||||
|
||||
bool amd_nb_has_feature(unsigned int feature)
|
||||
{
|
||||
return ((amd_northbridges.flags & feature) == feature);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_nb_has_feature);
|
||||
|
||||
struct amd_northbridge *node_to_amd_nb(int node)
|
||||
{
|
||||
return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(node_to_amd_nb);
|
||||
|
||||
static struct pci_dev *next_northbridge(struct pci_dev *dev,
|
||||
const struct pci_device_id *ids)
|
||||
|
@ -58,13 +89,106 @@ static struct pci_dev *next_northbridge(struct pci_dev *dev,
|
|||
return dev;
|
||||
}
|
||||
|
||||
static int __amd_smn_rw(u16 node, u32 address, u32 *value, bool write)
|
||||
{
|
||||
struct pci_dev *root;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (node >= amd_northbridges.num)
|
||||
goto out;
|
||||
|
||||
root = node_to_amd_nb(node)->root;
|
||||
if (!root)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&smn_mutex);
|
||||
|
||||
err = pci_write_config_dword(root, 0x60, address);
|
||||
if (err) {
|
||||
pr_warn("Error programming SMN address 0x%x.\n", address);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = (write ? pci_write_config_dword(root, 0x64, *value)
|
||||
: pci_read_config_dword(root, 0x64, value));
|
||||
if (err)
|
||||
pr_warn("Error %s SMN address 0x%x.\n",
|
||||
(write ? "writing to" : "reading from"), address);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&smn_mutex);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int amd_smn_read(u16 node, u32 address, u32 *value)
|
||||
{
|
||||
return __amd_smn_rw(node, address, value, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_smn_read);
|
||||
|
||||
int amd_smn_write(u16 node, u32 address, u32 value)
|
||||
{
|
||||
return __amd_smn_rw(node, address, &value, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_smn_write);
|
||||
|
||||
/*
|
||||
* Data Fabric Indirect Access uses FICAA/FICAD.
|
||||
*
|
||||
* Fabric Indirect Configuration Access Address (FICAA): Constructed based
|
||||
* on the device's Instance Id and the PCI function and register offset of
|
||||
* the desired register.
|
||||
*
|
||||
* Fabric Indirect Configuration Access Data (FICAD): There are FICAD LO
|
||||
* and FICAD HI registers but so far we only need the LO register.
|
||||
*/
|
||||
int amd_df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
|
||||
{
|
||||
struct pci_dev *F4;
|
||||
u32 ficaa;
|
||||
int err = -ENODEV;
|
||||
|
||||
if (node >= amd_northbridges.num)
|
||||
goto out;
|
||||
|
||||
F4 = node_to_amd_nb(node)->link;
|
||||
if (!F4)
|
||||
goto out;
|
||||
|
||||
ficaa = 1;
|
||||
ficaa |= reg & 0x3FC;
|
||||
ficaa |= (func & 0x7) << 11;
|
||||
ficaa |= instance_id << 16;
|
||||
|
||||
mutex_lock(&smn_mutex);
|
||||
|
||||
err = pci_write_config_dword(F4, 0x5C, ficaa);
|
||||
if (err) {
|
||||
pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = pci_read_config_dword(F4, 0x98, lo);
|
||||
if (err)
|
||||
pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&smn_mutex);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amd_df_indirect_read);
|
||||
|
||||
int amd_cache_northbridges(void)
|
||||
{
|
||||
u16 i = 0;
|
||||
struct amd_northbridge *nb;
|
||||
struct pci_dev *misc, *link;
|
||||
struct pci_dev *root, *misc, *link;
|
||||
|
||||
if (amd_nb_num())
|
||||
if (amd_northbridges.num)
|
||||
return 0;
|
||||
|
||||
misc = NULL;
|
||||
|
@ -74,15 +198,17 @@ int amd_cache_northbridges(void)
|
|||
if (!i)
|
||||
return -ENODEV;
|
||||
|
||||
nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
|
||||
nb = kcalloc(i, sizeof(struct amd_northbridge), GFP_KERNEL);
|
||||
if (!nb)
|
||||
return -ENOMEM;
|
||||
|
||||
amd_northbridges.nb = nb;
|
||||
amd_northbridges.num = i;
|
||||
|
||||
link = misc = NULL;
|
||||
for (i = 0; i != amd_nb_num(); i++) {
|
||||
link = misc = root = NULL;
|
||||
for (i = 0; i != amd_northbridges.num; i++) {
|
||||
node_to_amd_nb(i)->root = root =
|
||||
next_northbridge(root, amd_root_ids);
|
||||
node_to_amd_nb(i)->misc = misc =
|
||||
next_northbridge(misc, amd_nb_misc_ids);
|
||||
node_to_amd_nb(i)->link = link =
|
||||
|
@ -139,13 +265,13 @@ struct resource *amd_get_mmconfig_range(struct resource *res)
|
|||
{
|
||||
u32 address;
|
||||
u64 base, msr;
|
||||
unsigned segn_busn_bits;
|
||||
unsigned int segn_busn_bits;
|
||||
|
||||
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
|
||||
return NULL;
|
||||
|
||||
/* assume all cpus from fam10h have mmconfig */
|
||||
if (boot_cpu_data.x86 < 0x10)
|
||||
if (boot_cpu_data.x86 < 0x10)
|
||||
return NULL;
|
||||
|
||||
address = MSR_FAM10H_MMIO_CONF_BASE;
|
||||
|
@ -226,14 +352,14 @@ static void amd_cache_gart(void)
|
|||
if (!amd_nb_has_feature(AMD_NB_GART))
|
||||
return;
|
||||
|
||||
flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
|
||||
flush_words = kmalloc_array(amd_northbridges.num, sizeof(u32), GFP_KERNEL);
|
||||
if (!flush_words) {
|
||||
amd_northbridges.flags &= ~AMD_NB_GART;
|
||||
pr_notice("Cannot initialize GART flush words, GART support disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i != amd_nb_num(); i++)
|
||||
for (i = 0; i != amd_northbridges.num; i++)
|
||||
pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c, &flush_words[i]);
|
||||
}
|
||||
|
||||
|
@ -246,18 +372,20 @@ void amd_flush_garts(void)
|
|||
if (!amd_nb_has_feature(AMD_NB_GART))
|
||||
return;
|
||||
|
||||
/* Avoid races between AGP and IOMMU. In theory it's not needed
|
||||
but I'm not sure if the hardware won't lose flush requests
|
||||
when another is pending. This whole thing is so expensive anyways
|
||||
that it doesn't matter to serialize more. -AK */
|
||||
/*
|
||||
* Avoid races between AGP and IOMMU. In theory it's not needed
|
||||
* but I'm not sure if the hardware won't lose flush requests
|
||||
* when another is pending. This whole thing is so expensive anyways
|
||||
* that it doesn't matter to serialize more. -AK
|
||||
*/
|
||||
spin_lock_irqsave(&gart_lock, flags);
|
||||
flushed = 0;
|
||||
for (i = 0; i < amd_nb_num(); i++) {
|
||||
for (i = 0; i < amd_northbridges.num; i++) {
|
||||
pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
|
||||
flush_words[i] | 1);
|
||||
flushed++;
|
||||
}
|
||||
for (i = 0; i < amd_nb_num(); i++) {
|
||||
for (i = 0; i < amd_northbridges.num; i++) {
|
||||
u32 w;
|
||||
/* Make sure the hardware actually executed the flush*/
|
||||
for (;;) {
|
||||
|
|
|
@ -311,7 +311,7 @@ static int mce_severity_intel(struct mce *m, int tolerant, char **msg, bool is_e
|
|||
*msg = s->msg;
|
||||
s->covered = 1;
|
||||
if (s->sev >= MCE_UC_SEVERITY && ctx == IN_KERNEL) {
|
||||
if (panic_on_oops || tolerant < 1)
|
||||
if (tolerant < 1)
|
||||
return MCE_PANIC_SEVERITY;
|
||||
}
|
||||
return s->sev;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
@ -135,6 +136,9 @@ void mce_setup(struct mce *m)
|
|||
m->socketid = cpu_data(m->extcpu).phys_proc_id;
|
||||
m->apicid = cpu_data(m->extcpu).initial_apicid;
|
||||
rdmsrl(MSR_IA32_MCG_CAP, m->mcgcap);
|
||||
|
||||
if (this_cpu_has(X86_FEATURE_INTEL_PPIN))
|
||||
rdmsrl(MSR_PPIN, m->ppin);
|
||||
}
|
||||
|
||||
DEFINE_PER_CPU(struct mce, injectm);
|
||||
|
@ -207,8 +211,12 @@ EXPORT_SYMBOL_GPL(mce_inject_log);
|
|||
|
||||
static struct notifier_block mce_srao_nb;
|
||||
|
||||
static atomic_t num_notifiers;
|
||||
|
||||
void mce_register_decode_chain(struct notifier_block *nb)
|
||||
{
|
||||
atomic_inc(&num_notifiers);
|
||||
|
||||
/* Ensure SRAO notifier has the highest priority in the decode chain. */
|
||||
if (nb != &mce_srao_nb && nb->priority == INT_MAX)
|
||||
nb->priority -= 1;
|
||||
|
@ -219,6 +227,8 @@ EXPORT_SYMBOL_GPL(mce_register_decode_chain);
|
|||
|
||||
void mce_unregister_decode_chain(struct notifier_block *nb)
|
||||
{
|
||||
atomic_dec(&num_notifiers);
|
||||
|
||||
atomic_notifier_chain_unregister(&x86_mce_decoder_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mce_unregister_decode_chain);
|
||||
|
@ -270,17 +280,17 @@ struct mca_msr_regs msr_ops = {
|
|||
.misc = misc_reg
|
||||
};
|
||||
|
||||
static void print_mce(struct mce *m)
|
||||
static void __print_mce(struct mce *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_emerg(HW_ERR "CPU %d: Machine Check Exception: %Lx Bank %d: %016Lx\n",
|
||||
m->extcpu, m->mcgstatus, m->bank, m->status);
|
||||
pr_emerg(HW_ERR "CPU %d: Machine Check%s: %Lx Bank %d: %016Lx\n",
|
||||
m->extcpu,
|
||||
(m->mcgstatus & MCG_STATUS_MCIP ? " Exception" : ""),
|
||||
m->mcgstatus, m->bank, m->status);
|
||||
|
||||
if (m->ip) {
|
||||
pr_emerg(HW_ERR "RIP%s %02x:<%016Lx> ",
|
||||
!(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
|
||||
m->cs, m->ip);
|
||||
m->cs, m->ip);
|
||||
|
||||
if (m->cs == __KERNEL_CS)
|
||||
print_symbol("{%s}", m->ip);
|
||||
|
@ -308,6 +318,13 @@ static void print_mce(struct mce *m)
|
|||
pr_emerg(HW_ERR "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x microcode %x\n",
|
||||
m->cpuvendor, m->cpuid, m->time, m->socketid, m->apicid,
|
||||
cpu_data(m->extcpu).microcode);
|
||||
}
|
||||
|
||||
static void print_mce(struct mce *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
__print_mce(m);
|
||||
|
||||
/*
|
||||
* Print out human-readable details about the MCE error,
|
||||
|
@ -569,6 +586,32 @@ static struct notifier_block mce_srao_nb = {
|
|||
.priority = INT_MAX,
|
||||
};
|
||||
|
||||
static int mce_default_notifier(struct notifier_block *nb, unsigned long val,
|
||||
void *data)
|
||||
{
|
||||
struct mce *m = (struct mce *)data;
|
||||
|
||||
if (!m)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
* Run the default notifier if we have only the SRAO
|
||||
* notifier and us registered.
|
||||
*/
|
||||
if (atomic_read(&num_notifiers) > 2)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
__print_mce(m);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block mce_default_nb = {
|
||||
.notifier_call = mce_default_notifier,
|
||||
/* lowest prio, we want it to run last. */
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Read ADDR and MISC registers.
|
||||
*/
|
||||
|
@ -667,6 +710,15 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
|
|||
|
||||
mce_gather_info(&m, NULL);
|
||||
|
||||
/*
|
||||
* m.tsc was set in mce_setup(). Clear it if not requested.
|
||||
*
|
||||
* FIXME: Propagate @flags to mce_gather_info/mce_setup() to avoid
|
||||
* that dance.
|
||||
*/
|
||||
if (!(flags & MCP_TIMESTAMP))
|
||||
m.tsc = 0;
|
||||
|
||||
for (i = 0; i < mca_cfg.banks; i++) {
|
||||
if (!mce_banks[i].ctl || !test_bit(i, *b))
|
||||
continue;
|
||||
|
@ -674,14 +726,12 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
|
|||
m.misc = 0;
|
||||
m.addr = 0;
|
||||
m.bank = i;
|
||||
m.tsc = 0;
|
||||
|
||||
barrier();
|
||||
m.status = mce_rdmsrl(msr_ops.status(i));
|
||||
if (!(m.status & MCI_STATUS_VAL))
|
||||
continue;
|
||||
|
||||
|
||||
/*
|
||||
* Uncorrected or signalled events are handled by the exception
|
||||
* handler when it is enabled, so don't process those here.
|
||||
|
@ -696,9 +746,6 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
|
|||
|
||||
mce_read_aux(&m, i);
|
||||
|
||||
if (!(flags & MCP_TIMESTAMP))
|
||||
m.tsc = 0;
|
||||
|
||||
severity = mce_severity(&m, mca_cfg.tolerant, NULL, false);
|
||||
|
||||
if (severity == MCE_DEFERRED_SEVERITY && memory_error(&m))
|
||||
|
@ -1355,7 +1402,7 @@ static void mce_timer_fn(unsigned long data)
|
|||
iv = __this_cpu_read(mce_next_interval);
|
||||
|
||||
if (mce_available(this_cpu_ptr(&cpu_info))) {
|
||||
machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_poll_banks));
|
||||
machine_check_poll(0, this_cpu_ptr(&mce_poll_banks));
|
||||
|
||||
if (mce_intel_cmci_poll()) {
|
||||
iv = mce_adjust_timer(iv);
|
||||
|
@ -2138,6 +2185,7 @@ int __init mcheck_init(void)
|
|||
{
|
||||
mcheck_intel_therm_init();
|
||||
mce_register_decode_chain(&mce_srao_nb);
|
||||
mce_register_decode_chain(&mce_default_nb);
|
||||
mcheck_vendor_init_severity();
|
||||
|
||||
INIT_WORK(&mce_work, mce_process_work);
|
||||
|
|
|
@ -69,7 +69,12 @@ static const char * const smca_umc_block_names[] = {
|
|||
"misc_umc"
|
||||
};
|
||||
|
||||
struct smca_bank_name smca_bank_names[] = {
|
||||
struct smca_bank_name {
|
||||
const char *name; /* Short name for sysfs */
|
||||
const char *long_name; /* Long name for pretty-printing */
|
||||
};
|
||||
|
||||
static struct smca_bank_name smca_names[] = {
|
||||
[SMCA_LS] = { "load_store", "Load Store Unit" },
|
||||
[SMCA_IF] = { "insn_fetch", "Instruction Fetch Unit" },
|
||||
[SMCA_L2_CACHE] = { "l2_cache", "L2 Cache" },
|
||||
|
@ -84,9 +89,25 @@ struct smca_bank_name smca_bank_names[] = {
|
|||
[SMCA_PSP] = { "psp", "Platform Security Processor" },
|
||||
[SMCA_SMU] = { "smu", "System Management Unit" },
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(smca_bank_names);
|
||||
|
||||
static struct smca_hwid_mcatype smca_hwid_mcatypes[] = {
|
||||
const char *smca_get_name(enum smca_bank_types t)
|
||||
{
|
||||
if (t >= N_SMCA_BANK_TYPES)
|
||||
return NULL;
|
||||
|
||||
return smca_names[t].name;
|
||||
}
|
||||
|
||||
const char *smca_get_long_name(enum smca_bank_types t)
|
||||
{
|
||||
if (t >= N_SMCA_BANK_TYPES)
|
||||
return NULL;
|
||||
|
||||
return smca_names[t].long_name;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(smca_get_long_name);
|
||||
|
||||
static struct smca_hwid smca_hwid_mcatypes[] = {
|
||||
/* { bank_type, hwid_mcatype, xec_bitmap } */
|
||||
|
||||
/* ZN Core (HWID=0xB0) MCA types */
|
||||
|
@ -116,7 +137,7 @@ static struct smca_hwid_mcatype smca_hwid_mcatypes[] = {
|
|||
{ SMCA_SMU, HWID_MCATYPE(0x01, 0x0), 0x1 },
|
||||
};
|
||||
|
||||
struct smca_bank_info smca_banks[MAX_NR_BANKS];
|
||||
struct smca_bank smca_banks[MAX_NR_BANKS];
|
||||
EXPORT_SYMBOL_GPL(smca_banks);
|
||||
|
||||
/*
|
||||
|
@ -142,35 +163,34 @@ static void default_deferred_error_interrupt(void)
|
|||
}
|
||||
void (*deferred_error_int_vector)(void) = default_deferred_error_interrupt;
|
||||
|
||||
/*
|
||||
* CPU Initialization
|
||||
*/
|
||||
|
||||
static void get_smca_bank_info(unsigned int bank)
|
||||
{
|
||||
unsigned int i, hwid_mcatype, cpu = smp_processor_id();
|
||||
struct smca_hwid_mcatype *type;
|
||||
u32 high, instanceId;
|
||||
u16 hwid, mcatype;
|
||||
struct smca_hwid *s_hwid;
|
||||
u32 high, instance_id;
|
||||
|
||||
/* Collect bank_info using CPU 0 for now. */
|
||||
if (cpu)
|
||||
return;
|
||||
|
||||
if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_IPID(bank), &instanceId, &high)) {
|
||||
if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_IPID(bank), &instance_id, &high)) {
|
||||
pr_warn("Failed to read MCA_IPID for bank %d\n", bank);
|
||||
return;
|
||||
}
|
||||
|
||||
hwid = high & MCI_IPID_HWID;
|
||||
mcatype = (high & MCI_IPID_MCATYPE) >> 16;
|
||||
hwid_mcatype = HWID_MCATYPE(hwid, mcatype);
|
||||
hwid_mcatype = HWID_MCATYPE(high & MCI_IPID_HWID,
|
||||
(high & MCI_IPID_MCATYPE) >> 16);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(smca_hwid_mcatypes); i++) {
|
||||
type = &smca_hwid_mcatypes[i];
|
||||
if (hwid_mcatype == type->hwid_mcatype) {
|
||||
smca_banks[bank].type = type;
|
||||
smca_banks[bank].type_instance = instanceId;
|
||||
s_hwid = &smca_hwid_mcatypes[i];
|
||||
if (hwid_mcatype == s_hwid->hwid_mcatype) {
|
||||
|
||||
WARN(smca_banks[bank].hwid,
|
||||
"Bank %s already initialized!\n",
|
||||
smca_get_name(s_hwid->bank_type));
|
||||
|
||||
smca_banks[bank].hwid = s_hwid;
|
||||
smca_banks[bank].id = instance_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -533,6 +553,206 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
|
|||
deferred_error_interrupt_enable(c);
|
||||
}
|
||||
|
||||
int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr)
|
||||
{
|
||||
u64 dram_base_addr, dram_limit_addr, dram_hole_base;
|
||||
/* We start from the normalized address */
|
||||
u64 ret_addr = norm_addr;
|
||||
|
||||
u32 tmp;
|
||||
|
||||
u8 die_id_shift, die_id_mask, socket_id_shift, socket_id_mask;
|
||||
u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets;
|
||||
u8 intlv_addr_sel, intlv_addr_bit;
|
||||
u8 num_intlv_bits, hashed_bit;
|
||||
u8 lgcy_mmio_hole_en, base = 0;
|
||||
u8 cs_mask, cs_id = 0;
|
||||
bool hash_enabled = false;
|
||||
|
||||
/* Read D18F0x1B4 (DramOffset), check if base 1 is used. */
|
||||
if (amd_df_indirect_read(nid, 0, 0x1B4, umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
/* Remove HiAddrOffset from normalized address, if enabled: */
|
||||
if (tmp & BIT(0)) {
|
||||
u64 hi_addr_offset = (tmp & GENMASK_ULL(31, 20)) << 8;
|
||||
|
||||
if (norm_addr >= hi_addr_offset) {
|
||||
ret_addr -= hi_addr_offset;
|
||||
base = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read D18F0x110 (DramBaseAddress). */
|
||||
if (amd_df_indirect_read(nid, 0, 0x110 + (8 * base), umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
/* Check if address range is valid. */
|
||||
if (!(tmp & BIT(0))) {
|
||||
pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n",
|
||||
__func__, tmp);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
lgcy_mmio_hole_en = tmp & BIT(1);
|
||||
intlv_num_chan = (tmp >> 4) & 0xF;
|
||||
intlv_addr_sel = (tmp >> 8) & 0x7;
|
||||
dram_base_addr = (tmp & GENMASK_ULL(31, 12)) << 16;
|
||||
|
||||
/* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */
|
||||
if (intlv_addr_sel > 3) {
|
||||
pr_err("%s: Invalid interleave address select %d.\n",
|
||||
__func__, intlv_addr_sel);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Read D18F0x114 (DramLimitAddress). */
|
||||
if (amd_df_indirect_read(nid, 0, 0x114 + (8 * base), umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
intlv_num_sockets = (tmp >> 8) & 0x1;
|
||||
intlv_num_dies = (tmp >> 10) & 0x3;
|
||||
dram_limit_addr = ((tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0);
|
||||
|
||||
intlv_addr_bit = intlv_addr_sel + 8;
|
||||
|
||||
/* Re-use intlv_num_chan by setting it equal to log2(#channels) */
|
||||
switch (intlv_num_chan) {
|
||||
case 0: intlv_num_chan = 0; break;
|
||||
case 1: intlv_num_chan = 1; break;
|
||||
case 3: intlv_num_chan = 2; break;
|
||||
case 5: intlv_num_chan = 3; break;
|
||||
case 7: intlv_num_chan = 4; break;
|
||||
|
||||
case 8: intlv_num_chan = 1;
|
||||
hash_enabled = true;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Invalid number of interleaved channels %d.\n",
|
||||
__func__, intlv_num_chan);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
num_intlv_bits = intlv_num_chan;
|
||||
|
||||
if (intlv_num_dies > 2) {
|
||||
pr_err("%s: Invalid number of interleaved nodes/dies %d.\n",
|
||||
__func__, intlv_num_dies);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
num_intlv_bits += intlv_num_dies;
|
||||
|
||||
/* Add a bit if sockets are interleaved. */
|
||||
num_intlv_bits += intlv_num_sockets;
|
||||
|
||||
/* Assert num_intlv_bits <= 4 */
|
||||
if (num_intlv_bits > 4) {
|
||||
pr_err("%s: Invalid interleave bits %d.\n",
|
||||
__func__, num_intlv_bits);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (num_intlv_bits > 0) {
|
||||
u64 temp_addr_x, temp_addr_i, temp_addr_y;
|
||||
u8 die_id_bit, sock_id_bit, cs_fabric_id;
|
||||
|
||||
/*
|
||||
* Read FabricBlockInstanceInformation3_CS[BlockFabricID].
|
||||
* This is the fabric id for this coherent slave. Use
|
||||
* umc/channel# as instance id of the coherent slave
|
||||
* for FICAA.
|
||||
*/
|
||||
if (amd_df_indirect_read(nid, 0, 0x50, umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
cs_fabric_id = (tmp >> 8) & 0xFF;
|
||||
die_id_bit = 0;
|
||||
|
||||
/* If interleaved over more than 1 channel: */
|
||||
if (intlv_num_chan) {
|
||||
die_id_bit = intlv_num_chan;
|
||||
cs_mask = (1 << die_id_bit) - 1;
|
||||
cs_id = cs_fabric_id & cs_mask;
|
||||
}
|
||||
|
||||
sock_id_bit = die_id_bit;
|
||||
|
||||
/* Read D18F1x208 (SystemFabricIdMask). */
|
||||
if (intlv_num_dies || intlv_num_sockets)
|
||||
if (amd_df_indirect_read(nid, 1, 0x208, umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
/* If interleaved over more than 1 die. */
|
||||
if (intlv_num_dies) {
|
||||
sock_id_bit = die_id_bit + intlv_num_dies;
|
||||
die_id_shift = (tmp >> 24) & 0xF;
|
||||
die_id_mask = (tmp >> 8) & 0xFF;
|
||||
|
||||
cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << die_id_bit;
|
||||
}
|
||||
|
||||
/* If interleaved over more than 1 socket. */
|
||||
if (intlv_num_sockets) {
|
||||
socket_id_shift = (tmp >> 28) & 0xF;
|
||||
socket_id_mask = (tmp >> 16) & 0xFF;
|
||||
|
||||
cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit;
|
||||
}
|
||||
|
||||
/*
|
||||
* The pre-interleaved address consists of XXXXXXIIIYYYYY
|
||||
* where III is the ID for this CS, and XXXXXXYYYYY are the
|
||||
* address bits from the post-interleaved address.
|
||||
* "num_intlv_bits" has been calculated to tell us how many "I"
|
||||
* bits there are. "intlv_addr_bit" tells us how many "Y" bits
|
||||
* there are (where "I" starts).
|
||||
*/
|
||||
temp_addr_y = ret_addr & GENMASK_ULL(intlv_addr_bit-1, 0);
|
||||
temp_addr_i = (cs_id << intlv_addr_bit);
|
||||
temp_addr_x = (ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits;
|
||||
ret_addr = temp_addr_x | temp_addr_i | temp_addr_y;
|
||||
}
|
||||
|
||||
/* Add dram base address */
|
||||
ret_addr += dram_base_addr;
|
||||
|
||||
/* If legacy MMIO hole enabled */
|
||||
if (lgcy_mmio_hole_en) {
|
||||
if (amd_df_indirect_read(nid, 0, 0x104, umc, &tmp))
|
||||
goto out_err;
|
||||
|
||||
dram_hole_base = tmp & GENMASK(31, 24);
|
||||
if (ret_addr >= dram_hole_base)
|
||||
ret_addr += (BIT_ULL(32) - dram_hole_base);
|
||||
}
|
||||
|
||||
if (hash_enabled) {
|
||||
/* Save some parentheses and grab ls-bit at the end. */
|
||||
hashed_bit = (ret_addr >> 12) ^
|
||||
(ret_addr >> 18) ^
|
||||
(ret_addr >> 21) ^
|
||||
(ret_addr >> 30) ^
|
||||
cs_id;
|
||||
|
||||
hashed_bit &= BIT(0);
|
||||
|
||||
if (hashed_bit != ((ret_addr >> intlv_addr_bit) & BIT(0)))
|
||||
ret_addr ^= BIT(intlv_addr_bit);
|
||||
}
|
||||
|
||||
/* Is calculated system address is above DRAM limit address? */
|
||||
if (ret_addr > dram_limit_addr)
|
||||
goto out_err;
|
||||
|
||||
*sys_addr = ret_addr;
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(umc_normaddr_to_sysaddr);
|
||||
|
||||
static void
|
||||
__log_error(unsigned int bank, bool deferred_err, bool threshold_err, u64 misc)
|
||||
{
|
||||
|
@ -645,6 +865,7 @@ static void amd_threshold_interrupt(void)
|
|||
{
|
||||
u32 low = 0, high = 0, address = 0;
|
||||
unsigned int bank, block, cpu = smp_processor_id();
|
||||
struct thresh_restart tr;
|
||||
|
||||
/* assume first bank caused it */
|
||||
for (bank = 0; bank < mca_cfg.banks; ++bank) {
|
||||
|
@ -681,6 +902,11 @@ static void amd_threshold_interrupt(void)
|
|||
|
||||
log:
|
||||
__log_error(bank, false, true, ((u64)high << 32) | low);
|
||||
|
||||
/* Reset threshold block after logging error. */
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
tr.b = &per_cpu(threshold_banks, cpu)[bank]->blocks[block];
|
||||
threshold_restart_bank(&tr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -826,10 +1052,10 @@ static const char *get_name(unsigned int bank, struct threshold_block *b)
|
|||
return th_names[bank];
|
||||
}
|
||||
|
||||
if (!smca_banks[bank].type)
|
||||
if (!smca_banks[bank].hwid)
|
||||
return NULL;
|
||||
|
||||
bank_type = smca_banks[bank].type->bank_type;
|
||||
bank_type = smca_banks[bank].hwid->bank_type;
|
||||
|
||||
if (b && bank_type == SMCA_UMC) {
|
||||
if (b->block < ARRAY_SIZE(smca_umc_block_names))
|
||||
|
@ -838,8 +1064,8 @@ static const char *get_name(unsigned int bank, struct threshold_block *b)
|
|||
}
|
||||
|
||||
snprintf(buf_mcatype, MAX_MCATYPE_NAME_LEN,
|
||||
"%s_%x", smca_bank_names[bank_type].name,
|
||||
smca_banks[bank].type_instance);
|
||||
"%s_%x", smca_get_name(bank_type),
|
||||
smca_banks[bank].id);
|
||||
return buf_mcatype;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/mce.h>
|
||||
|
@ -130,7 +132,7 @@ bool mce_intel_cmci_poll(void)
|
|||
* Reset the counter if we've logged an error in the last poll
|
||||
* during the storm.
|
||||
*/
|
||||
if (machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_banks_owned)))
|
||||
if (machine_check_poll(0, this_cpu_ptr(&mce_banks_owned)))
|
||||
this_cpu_write(cmci_backoff_cnt, INITIAL_CHECK_INTERVAL);
|
||||
else
|
||||
this_cpu_dec(cmci_backoff_cnt);
|
||||
|
@ -342,7 +344,7 @@ void cmci_recheck(void)
|
|||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_banks_owned));
|
||||
machine_check_poll(0, this_cpu_ptr(&mce_banks_owned));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
|
@ -464,11 +466,46 @@ static void intel_clear_lmce(void)
|
|||
wrmsrl(MSR_IA32_MCG_EXT_CTL, val);
|
||||
}
|
||||
|
||||
static void intel_ppin_init(struct cpuinfo_x86 *c)
|
||||
{
|
||||
unsigned long long val;
|
||||
|
||||
/*
|
||||
* Even if testing the presence of the MSR would be enough, we don't
|
||||
* want to risk the situation where other models reuse this MSR for
|
||||
* other purposes.
|
||||
*/
|
||||
switch (c->x86_model) {
|
||||
case INTEL_FAM6_IVYBRIDGE_X:
|
||||
case INTEL_FAM6_HASWELL_X:
|
||||
case INTEL_FAM6_BROADWELL_XEON_D:
|
||||
case INTEL_FAM6_BROADWELL_X:
|
||||
case INTEL_FAM6_SKYLAKE_X:
|
||||
if (rdmsrl_safe(MSR_PPIN_CTL, &val))
|
||||
return;
|
||||
|
||||
if ((val & 3UL) == 1UL) {
|
||||
/* PPIN available but disabled: */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If PPIN is disabled, but not locked, try to enable: */
|
||||
if (!(val & 3UL)) {
|
||||
wrmsrl_safe(MSR_PPIN_CTL, val | 2UL);
|
||||
rdmsrl_safe(MSR_PPIN_CTL, &val);
|
||||
}
|
||||
|
||||
if ((val & 3UL) == 2UL)
|
||||
set_cpu_cap(c, X86_FEATURE_INTEL_PPIN);
|
||||
}
|
||||
}
|
||||
|
||||
void mce_intel_feature_init(struct cpuinfo_x86 *c)
|
||||
{
|
||||
intel_init_thermal(c);
|
||||
intel_init_cmci();
|
||||
intel_init_lmce();
|
||||
intel_ppin_init(c);
|
||||
}
|
||||
|
||||
void mce_intel_feature_clear(struct cpuinfo_x86 *c)
|
||||
|
|
|
@ -275,6 +275,8 @@ static void do_inject(void)
|
|||
unsigned int cpu = i_mce.extcpu;
|
||||
u8 b = i_mce.bank;
|
||||
|
||||
rdtscll(i_mce.tsc);
|
||||
|
||||
if (i_mce.misc)
|
||||
i_mce.status |= MCI_STATUS_MISCV;
|
||||
|
||||
|
|
|
@ -851,7 +851,7 @@ static void decode_mc6_mce(struct mce *m)
|
|||
/* Decode errors according to Scalable MCA specification */
|
||||
static void decode_smca_errors(struct mce *m)
|
||||
{
|
||||
struct smca_hwid_mcatype *type;
|
||||
struct smca_hwid *hwid;
|
||||
unsigned int bank_type;
|
||||
const char *ip_name;
|
||||
u8 xec = XEC(m->status, xec_mask);
|
||||
|
@ -862,18 +862,18 @@ static void decode_smca_errors(struct mce *m)
|
|||
if (boot_cpu_data.x86 >= 0x17 && m->bank == 4)
|
||||
pr_emerg(HW_ERR "Bank 4 is reserved on Fam17h.\n");
|
||||
|
||||
type = smca_banks[m->bank].type;
|
||||
if (!type)
|
||||
hwid = smca_banks[m->bank].hwid;
|
||||
if (!hwid)
|
||||
return;
|
||||
|
||||
bank_type = type->bank_type;
|
||||
ip_name = smca_bank_names[bank_type].long_name;
|
||||
bank_type = hwid->bank_type;
|
||||
ip_name = smca_get_long_name(bank_type);
|
||||
|
||||
pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec);
|
||||
|
||||
/* Only print the decode of valid error codes */
|
||||
if (xec < smca_mce_descs[bank_type].num_descs &&
|
||||
(type->xec_bitmap & BIT_ULL(xec))) {
|
||||
(hwid->xec_bitmap & BIT_ULL(xec))) {
|
||||
pr_emerg(HW_ERR "%s Error: ", ip_name);
|
||||
pr_cont("%s.\n", smca_mce_descs[bank_type].descs[xec]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue