Skip to content

Commit

Permalink
target-i386: kvm: Add basic Intel LMCE support
Browse files Browse the repository at this point in the history
This patch adds the support to inject SRAR and SRAO as LMCE, i.e. they
are injected to only one VCPU rather than broadcast to all VCPUs. As KVM
reports LMCE support on Intel platforms, this features is only available
on Intel platforms.

LMCE is disabled by default and can be enabled/disabled by cpu option
'lmce=on/off'.

Signed-off-by: Ashok Raj <ashok.raj@intel.com>
[Haozhong: Enable LMCE only on Intel platforms
           Disable LMCE by default and add a cpu option 'lmce'
           Handle the error if LMCE is enabled w/o host support
           Remove MCG_LMCE_P from MCE_CAP_DEF
           Add migration support for LMCE
           Minor code style changes]
Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
  • Loading branch information
ashok-raj authored and ehabkost committed Jul 7, 2016
1 parent c35bd19 commit 87f8b62
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 4 deletions.
4 changes: 3 additions & 1 deletion target-i386/cpu.c
Expand Up @@ -2839,7 +2839,8 @@ static void mce_init(X86CPU *cpu)
if (((cenv->cpuid_version >> 8) & 0xf) >= 6
&& (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) ==
(CPUID_MCE | CPUID_MCA)) {
cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF;
cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF |
(cpu->enable_lmce ? MCG_LMCE_P : 0);
cenv->mcg_ctl = ~(uint64_t)0;
for (bank = 0; bank < MCE_BANKS_DEF; bank++) {
cenv->mce_banks[bank * 4] = ~(uint64_t)0;
Expand Down Expand Up @@ -3286,6 +3287,7 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id),
DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
DEFINE_PROP_END_OF_LIST()
};

Expand Down
12 changes: 12 additions & 0 deletions target-i386/cpu.h
Expand Up @@ -292,6 +292,7 @@

#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */
#define MCG_LMCE_P (1ULL<<27) /* Local Machine Check Supported */

#define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P)
#define MCE_BANKS_DEF 10
Expand All @@ -301,6 +302,9 @@
#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */
#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */
#define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */
#define MCG_STATUS_LMCE (1ULL<<3) /* Local MCE signaled */

#define MCG_EXT_CTL_LMCE_EN (1ULL<<0) /* Local MCE enabled */

#define MCI_STATUS_VAL (1ULL<<63) /* valid error */
#define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */
Expand Down Expand Up @@ -343,6 +347,7 @@
#define MSR_MCG_CAP 0x179
#define MSR_MCG_STATUS 0x17a
#define MSR_MCG_CTL 0x17b
#define MSR_MCG_EXT_CTL 0x4d0

#define MSR_P6_EVNTSEL0 0x186

Expand Down Expand Up @@ -1114,6 +1119,7 @@ typedef struct CPUX86State {

uint64_t mcg_cap;
uint64_t mcg_ctl;
uint64_t mcg_ext_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4];

uint64_t tsc_aux;
Expand Down Expand Up @@ -1181,6 +1187,12 @@ struct X86CPU {
*/
bool enable_pmu;

/* LMCE support can be enabled/disabled via cpu option 'lmce=on/off'. It is
* disabled by default to avoid breaking migration between QEMU with
* different LMCE configurations.
*/
bool enable_lmce;

/* Compatibility bits for old machine types: */
bool enable_cpuid_0xb;

Expand Down
36 changes: 33 additions & 3 deletions target-i386/kvm.c
Expand Up @@ -106,6 +106,8 @@ static int has_xsave;
static int has_xcrs;
static int has_pit_state2;

static bool has_msr_mcg_ext_ctl;

static struct kvm_cpuid2 *cpuid_cache;

int kvm_has_pit_state2(void)
Expand Down Expand Up @@ -382,10 +384,12 @@ static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,

static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code)
{
CPUState *cs = CPU(cpu);
CPUX86State *env = &cpu->env;
uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S;
uint64_t mcg_status = MCG_STATUS_MCIP;
int flags = 0;

if (code == BUS_MCEERR_AR) {
status |= MCI_STATUS_AR | 0x134;
Expand All @@ -394,10 +398,19 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code)
status |= 0xc0;
mcg_status |= MCG_STATUS_RIPV;
}

flags = cpu_x86_support_mca_broadcast(env) ? MCE_INJECT_BROADCAST : 0;
/* We need to read back the value of MSR_EXT_MCG_CTL that was set by the
* guest kernel back into env->mcg_ext_ctl.
*/
cpu_synchronize_state(cs);
if (env->mcg_ext_ctl & MCG_EXT_CTL_LMCE_EN) {
mcg_status |= MCG_STATUS_LMCE;
flags = 0;
}

cpu_x86_inject_mce(NULL, cpu, 9, status, mcg_status, paddr,
(MCM_ADDR_PHYS << 6) | 0xc,
cpu_x86_support_mca_broadcast(env) ?
MCE_INJECT_BROADCAST : 0);
(MCM_ADDR_PHYS << 6) | 0xc, flags);
}

static void hardware_memory_error(void)
Expand Down Expand Up @@ -883,6 +896,10 @@ int kvm_arch_init_vcpu(CPUState *cs)

unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK);
if (unsupported_caps) {
if (unsupported_caps & MCG_LMCE_P) {
error_report("kvm: LMCE not supported");
return -ENOTSUP;
}
error_report("warning: Unsupported MCG_CAP bits: 0x%" PRIx64,
unsupported_caps);
}
Expand All @@ -903,6 +920,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
!!(c->ecx & CPUID_EXT_SMX);
}

if (env->mcg_cap & MCG_LMCE_P) {
has_msr_mcg_ext_ctl = has_msr_feature_control = true;
}

c = cpuid_find_entry(&cpuid_data.cpuid, 0x80000007, 0);
if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) {
/* for migration */
Expand Down Expand Up @@ -1723,6 +1744,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level)

kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status);
kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl);
if (has_msr_mcg_ext_ctl) {
kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, env->mcg_ext_ctl);
}
for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, env->mce_banks[i]);
}
Expand Down Expand Up @@ -2026,6 +2050,9 @@ static int kvm_get_msrs(X86CPU *cpu)
if (env->mcg_cap) {
kvm_msr_entry_add(cpu, MSR_MCG_STATUS, 0);
kvm_msr_entry_add(cpu, MSR_MCG_CTL, 0);
if (has_msr_mcg_ext_ctl) {
kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, 0);
}
for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, 0);
}
Expand Down Expand Up @@ -2154,6 +2181,9 @@ static int kvm_get_msrs(X86CPU *cpu)
case MSR_MCG_CTL:
env->mcg_ctl = msrs[i].data;
break;
case MSR_MCG_EXT_CTL:
env->mcg_ext_ctl = msrs[i].data;
break;
case MSR_IA32_MISC_ENABLE:
env->msr_ia32_misc_enable = msrs[i].data;
break;
Expand Down
19 changes: 19 additions & 0 deletions target-i386/machine.c
Expand Up @@ -896,6 +896,24 @@ static const VMStateDescription vmstate_tsc_khz = {
}
};

static bool mcg_ext_ctl_needed(void *opaque)
{
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
return cpu->enable_lmce && env->mcg_ext_ctl;
}

static const VMStateDescription vmstate_mcg_ext_ctl = {
.name = "cpu/mcg_ext_ctl",
.version_id = 1,
.minimum_version_id = 1,
.needed = mcg_ext_ctl_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.mcg_ext_ctl, X86CPU),
VMSTATE_END_OF_LIST()
}
};

VMStateDescription vmstate_x86_cpu = {
.name = "cpu",
.version_id = 12,
Expand Down Expand Up @@ -1022,6 +1040,7 @@ VMStateDescription vmstate_x86_cpu = {
#ifdef TARGET_X86_64
&vmstate_pkru,
#endif
&vmstate_mcg_ext_ctl,
NULL
}
};

0 comments on commit 87f8b62

Please sign in to comment.