blob: 20d2acdc12daa1128d72471d53639aebf82f4854 [file] [log] [blame]
/* Copyright (C) 2009 Intel Corporation
Author: Andi Kleen
Common Intel CPU code.
mcelog is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; version
2.
mcelog is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should find a copy of v2 of the GNU General Public License somewhere
on your Linux system; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <stddef.h>
#include "mcelog.h"
#include "intel.h"
#include "bitfield.h"
#include "nehalem.h"
#include "memdb.h"
#include "page.h"
#include "sandy-bridge.h"
#include "ivy-bridge.h"
#include "haswell.h"
int memory_error_support;
void intel_cpu_init(enum cputype cpu)
{
if (cpu == CPU_NEHALEM || cpu == CPU_XEON75XX || cpu == CPU_INTEL ||
cpu == CPU_SANDY_BRIDGE || cpu == CPU_SANDY_BRIDGE_EP ||
cpu == CPU_IVY_BRIDGE || cpu == CPU_IVY_BRIDGE_EPEX ||
cpu == CPU_HASWELL || cpu == CPU_HASWELL_EPEX || cpu == CPU_BROADWELL ||
cpu == CPU_BROADWELL_DE || cpu == CPU_BROADWELL_EPEX ||
cpu == CPU_KNIGHTS_LANDING || cpu == CPU_KNIGHTS_MILL ||
cpu == CPU_SKYLAKE || cpu == CPU_SKYLAKE_XEON ||
cpu == CPU_KABYLAKE || cpu == CPU_DENVERTON)
memory_error_support = 1;
}
enum cputype select_intel_cputype(int family, int model)
{
if (family == 15) {
if (model == 6)
return CPU_TULSA;
return CPU_P4;
}
if (family == 6) {
if (model >= 0x1a && model != 28)
memory_error_support = 1;
if (model < 0xf)
return CPU_P6OLD;
else if (model == 0xf || model == 0x17) /* Merom/Penryn */
return CPU_CORE2;
else if (model == 0x1d)
return CPU_DUNNINGTON;
else if (model == 0x1a || model == 0x2c || model == 0x1e ||
model == 0x25)
return CPU_NEHALEM;
else if (model == 0x2e || model == 0x2f)
return CPU_XEON75XX;
else if (model == 0x2a)
return CPU_SANDY_BRIDGE;
else if (model == 0x2d)
return CPU_SANDY_BRIDGE_EP;
else if (model == 0x3a)
return CPU_IVY_BRIDGE;
else if (model == 0x3e)
return CPU_IVY_BRIDGE_EPEX;
else if (model == 0x3c || model == 0x45 || model == 0x46)
return CPU_HASWELL;
else if (model == 0x3f)
return CPU_HASWELL_EPEX;
else if (model == 0x3d)
return CPU_BROADWELL;
else if (model == 0x4f)
return CPU_BROADWELL_EPEX;
else if (model == 0x56)
return CPU_BROADWELL_DE;
else if (model == 0x57)
return CPU_KNIGHTS_LANDING;
else if (model == 0x85)
return CPU_KNIGHTS_MILL;
else if (model == 0x1c || model == 0x26 || model == 0x27 ||
model == 0x35 || model == 0x36 || model == 0x36 ||
model == 0x37 || model == 0x4a || model == 0x4c ||
model == 0x4d || model == 0x5a || model == 0x5d)
return CPU_ATOM;
else if (model == 0x4e || model == 0x5e)
return CPU_SKYLAKE;
else if (model == 0x55)
return CPU_SKYLAKE_XEON;
else if (model == 0x8E || model == 0x9E)
return CPU_KABYLAKE;
else if (model == 0x5f)
return CPU_DENVERTON;
if (model > 0x1a) {
Eprintf("Family 6 Model %u CPU: only decoding architectural errors\n",
model);
return CPU_INTEL;
}
}
if (family > 6) {
Eprintf("Family %u Model %u CPU: only decoding architectural errors\n",
family, model);
return CPU_INTEL;
}
Eprintf("Unknown Intel CPU type family %u model %u\n", family, model);
return family == 6 ? CPU_P6OLD : CPU_GENERIC;
}
int is_intel_cpu(int cpu)
{
switch (cpu) {
CASE_INTEL_CPUS:
return 1;
}
return 0;
}
static int intel_memory_error(struct mce *m, unsigned recordlen)
{
u32 mca = m->status & 0xffff;
if ((mca >> 7) == 1) {
unsigned corr_err_cnt = 0;
int channel[2] = { (mca & 0xf) == 0xf ? -1 : (int)(mca & 0xf), -1 };
int dimm[2] = { -1, -1 };
switch (cputype) {
case CPU_NEHALEM:
nehalem_memerr_misc(m, channel, dimm);
break;
case CPU_SANDY_BRIDGE_EP:
sandy_bridge_ep_memerr_misc(m, channel, dimm);
break;
case CPU_IVY_BRIDGE_EPEX:
ivy_bridge_ep_memerr_misc(m, channel, dimm);
break;
default:
break;
}
if (recordlen > offsetof(struct mce, mcgcap) && m->mcgcap & MCG_CMCI_P)
corr_err_cnt = EXTRACT(m->status, 38, 52);
memory_error(m, channel[0], dimm[0], corr_err_cnt, recordlen);
account_page_error(m, channel[0], dimm[0]);
/*
* When both DIMMs have a error account the error twice to the page.
*/
if (channel[1] != -1) {
memory_error(m, channel[1], dimm[1], corr_err_cnt, recordlen);
account_page_error(m, channel[1], dimm[1]);
}
return 1;
}
return 0;
}
/* No bugs known, but filter out memory errors if the user asked for it */
int mce_filter_intel(struct mce *m, unsigned recordlen)
{
if (intel_memory_error(m, recordlen) == 1)
return !filter_memory_errors;
return 1;
}