@@ -202,6 +202,7 @@
#define CR4_DE_MASK (1 << 3)
#define CR4_PSE_MASK (1 << 4)
#define CR4_PAE_MASK (1 << 5)
+#define CR4_MCE_MASK (1 << 6)
#define CR4_PGE_MASK (1 << 7)
#define CR4_PCE_MASK (1 << 8)
#define CR4_OSFXSR_SHIFT 9
@@ -248,6 +249,17 @@
#define PG_ERROR_RSVD_MASK 0x08
#define PG_ERROR_I_D_MASK 0x10
+#define MCE_CAP_DEF 0x100
+#define MCE_BANKS_DEF 4
+
+#define MCG_CTL_P (1UL<<8)
+
+#define MCG_STATUS_MCIP (1UL<<2)
+
+#define MCI_STATUS_VAL (1UL<<63)
+#define MCI_STATUS_OVER (1UL<<62)
+#define MCI_STATUS_UC (1UL<<61)
+
#define MSR_IA32_TSC 0x10
#define MSR_IA32_APICBASE 0x1b
#define MSR_IA32_APICBASE_BSP (1<<8)
@@ -288,6 +300,11 @@
#define MSR_MTRRdefType 0x2ff
+#define MSR_MC0_CTL 0x400
+#define MSR_MC0_STATUS 0x401
+#define MSR_MC0_ADDR 0x402
+#define MSR_MC0_MISC 0x403
+
#define MSR_EFER 0xc0000080
#define MSR_EFER_SCE (1 << 0)
@@ -674,6 +691,11 @@ typedef struct CPUX86State {
user */
struct APICState *apic_state;
uint32_t mp_state;
+
+ uint64 mcg_cap;
+ uint64 mcg_status;
+ uint64 mcg_ctl;
+ uint64 *mce_banks;
} CPUX86State;
CPUX86State *cpu_x86_init(const char *cpu_model);
@@ -3104,7 +3104,23 @@ void helper_wrmsr(void)
case MSR_MTRRdefType:
env->mtrr_deftype = val;
break;
+ case MSR_MCG_STATUS:
+ env->mcg_status = val;
+ break;
+ case MSR_MCG_CTL:
+ if ((env->mcg_cap & MCG_CTL_P)
+ && (val == 0 || val == ~(uint64_t)0))
+ env->mcg_ctl = val;
+ break;
default:
+ if ((uint32_t)ECX >= MSR_MC0_CTL
+ && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
+ uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
+ if ((offset & 0x3) != 0
+ || (val == 0 || val == ~(uint64_t)0))
+ env->mce_banks[offset] = val;
+ break;
+ }
/* XXX: exception ? */
break;
}
@@ -3223,7 +3239,25 @@ void helper_rdmsr(void)
/* XXX: exception ? */
val = 0;
break;
+ case MSR_MCG_CAP:
+ val = env->mcg_cap;
+ break;
+ case MSR_MCG_CTL:
+ if (env->mcg_cap & MCG_CTL_P)
+ val = env->mcg_ctl;
+ else
+ val = 0;
+ break;
+ case MSR_MCG_STATUS:
+ val = env->mcg_status;
+ break;
default:
+ if ((uint32_t)ECX >= MSR_MC0_CTL
+ && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
+ uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
+ val = env->mce_banks[offset];
+ break;
+ }
/* XXX: exception ? */
val = 0;
break;
@@ -1432,6 +1432,75 @@ static void breakpoint_handler(CPUState
}
#endif /* !CONFIG_USER_ONLY */
+/* This should come from sysemu.h - if we could include it here... */
+void qemu_system_reset_request(void);
+
+void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc)
+{
+ uint64_t mcg_cap = cenv->mcg_cap;
+ unsigned bank_num = mcg_cap & 0xff;
+ uint64_t *banks = cenv->mce_banks;
+
+ if (bank >= bank_num || !(status & MCI_STATUS_VAL))
+ return;
+
+ /*
+ * if MSR_MCG_CTL is not all 1s, the uncorrected error
+ * reporting is disabled
+ */
+ if ((status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) &&
+ cenv->mcg_ctl != ~(uint64_t)0)
+ return;
+ banks += 4 * bank;
+ /*
+ * if MSR_MCi_CTL is not all 1s, the uncorrected error
+ * reporting is disabled for the bank
+ */
+ if ((status & MCI_STATUS_UC) && banks[0] != ~(uint64_t)0)
+ return;
+ if (status & MCI_STATUS_UC) {
+ if ((cenv->mcg_status & MCG_STATUS_MCIP) ||
+ !(cenv->cr[4] & CR4_MCE_MASK)) {
+ fprintf(stderr, "injects mce exception while previous "
+ "one is in progress!\n");
+ qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
+ qemu_system_reset_request();
+ return;
+ }
+ if (banks[1] & MCI_STATUS_VAL)
+ status |= MCI_STATUS_OVER;
+ banks[2] = addr;
+ banks[3] = misc;
+ cenv->mcg_status = mcg_status;
+ banks[1] = status;
+ cpu_interrupt(cenv, CPU_INTERRUPT_MCE);
+ } else if (!(banks[1] & MCI_STATUS_VAL)
+ || !(banks[1] & MCI_STATUS_UC)) {
+ if (banks[1] & MCI_STATUS_VAL)
+ status |= MCI_STATUS_OVER;
+ banks[2] = addr;
+ banks[3] = misc;
+ banks[1] = status;
+ } else
+ banks[1] |= MCI_STATUS_OVER;
+}
+
+static void mce_init(CPUX86State *cenv)
+{
+ unsigned int bank, bank_num;
+
+ if (((cenv->cpuid_version >> 8)&0xf) >= 6
+ && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)) {
+ cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF;
+ cenv->mcg_ctl = ~(uint64_t)0;
+ bank_num = cenv->mcg_cap & 0xff;
+ cenv->mce_banks = qemu_mallocz(bank_num * sizeof(uint64_t) * 4);
+ for (bank = 0; bank < bank_num; bank++)
+ cenv->mce_banks[bank*4] = ~(uint64_t)0;
+ }
+}
+
static void host_cpuid(uint32_t function, uint32_t count,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
@@ -1691,6 +1760,7 @@ CPUX86State *cpu_x86_init(const char *cp
cpu_x86_close(env);
return NULL;
}
+ mce_init(env);
cpu_reset(env);
#ifdef CONFIG_KQEMU
kqemu_init(env);
@@ -1676,6 +1676,28 @@ static void do_acl(Monitor *mon,
}
}
+#if defined(TARGET_I386)
+static void do_inject_mce(Monitor *mon,
+ int cpu_index, int bank,
+ unsigned status_hi, unsigned status_lo,
+ unsigned mcg_status_hi, unsigned mcg_status_lo,
+ unsigned addr_hi, unsigned addr_lo,
+ unsigned misc_hi, unsigned misc_lo)
+{
+ CPUState *cenv;
+ uint64_t status = ((uint64_t)status_hi << 32) | status_lo;
+ uint64_t mcg_status = ((uint64_t)mcg_status_hi << 32) | mcg_status_lo;
+ uint64_t addr = ((uint64_t)addr_hi << 32) | addr_lo;
+ uint64_t misc = ((uint64_t)misc_hi << 32) | misc_lo;
+
+ for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu)
+ if (cenv->cpu_index == cpu_index && cenv->mcg_cap) {
+ cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc);
+ break;
+ }
+}
+#endif
+
/* Please update qemu-doc.texi when adding or changing commands */
static const mon_cmd_t mon_cmds[] = {
{ "help|?", "s?", help_cmd,
@@ -1793,6 +1815,9 @@ static const mon_cmd_t mon_cmds[] = {
"acl deny vnc.username bob\n"
"acl reset vnc.username\n" },
{ "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
+#if defined(TARGET_I386)
+ { "mce", "iillll", do_inject_mce, "cpu bank status mcgstatus addr misc", "inject a MCE on the given CPU"},
+#endif
{ NULL, NULL, },
};
@@ -2538,6 +2563,15 @@ static void monitor_handle_command(Monit
void *arg3, void *arg4, void *arg5);
void (*handler_7)(Monitor *mon, void *arg0, void *arg1, void *arg2,
void *arg3, void *arg4, void *arg5, void *arg6);
+ void (*handler_8)(Monitor *mon, void *arg0, void *arg1, void *arg2,
+ void *arg3, void *arg4, void *arg5, void *arg6,
+ void *arg7);
+ void (*handler_9)(Monitor *mon, void *arg0, void *arg1, void *arg2,
+ void *arg3, void *arg4, void *arg5, void *arg6,
+ void *arg7, void *arg8);
+ void (*handler_10)(Monitor *mon, void *arg0, void *arg1, void *arg2,
+ void *arg3, void *arg4, void *arg5, void *arg6,
+ void *arg7, void *arg8, void *arg9);
#ifdef DEBUG
monitor_printf(mon, "command='%s'\n", cmdline);
@@ -2835,6 +2869,21 @@ static void monitor_handle_command(Monit
handler_7(mon, args[0], args[1], args[2], args[3], args[4], args[5],
args[6]);
break;
+ case 8:
+ handler_8 = cmd->handler;
+ handler_8(mon, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7]);
+ break;
+ case 9:
+ handler_9 = cmd->handler;
+ handler_9(mon, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7], args[8]);
+ break;
+ case 10:
+ handler_10 = cmd->handler;
+ handler_10(mon, args[0], args[1], args[2], args[3], args[4], args[5],
+ args[6], args[7], args[8], args[9]);
+ break;
default:
monitor_printf(mon, "unsupported number of arguments: %d\n", nb_args);
goto fail;
@@ -769,6 +769,7 @@ extern int use_icount;
#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */
#define CPU_INTERRUPT_VIRQ 0x100 /* virtual interrupt pending. */
#define CPU_INTERRUPT_NMI 0x200 /* NMI pending. */
+#define CPU_INTERRUPT_MCE 0x400 /* (x86 only) MCE pending. */
void cpu_interrupt(CPUState *s, int mask);
void cpu_reset_interrupt(CPUState *env, int mask);
@@ -911,6 +912,9 @@ void *qemu_get_ram_ptr(ram_addr_t addr);
/* This should not be used by devices. */
ram_addr_t qemu_ram_addr_from_host(void *ptr);
+void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc);
+
int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read,
CPUWriteMemoryFunc **mem_write,
@@ -391,6 +391,10 @@ int cpu_exec(CPUState *env1)
env->hflags2 |= HF2_NMI_MASK;
do_interrupt(EXCP02_NMI, 0, 0, 0, 1);
next_tb = 0;
+ } else if (interrupt_request & CPU_INTERRUPT_MCE) {
+ env->interrupt_request &= ~CPU_INTERRUPT_MCE;
+ do_interrupt(EXCP12_MCHK, 0, 0, 0, 0);
+ next_tb = 0;
} else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
(((env->hflags2 & HF2_VINTR_MASK) &&
(env->hflags2 & HF2_HIF_MASK)) ||
- MCE features are initialized when VCPU is intialized according to CPUID. - A monitor command "mce" is added to inject a MCE. - A new interrupt mask: CPU_INTERRUPT_MCE is added to inject the MCE. Signed-off-by: Huang Ying <ying.huang@intel.com> --- cpu-all.h | 4 ++ cpu-exec.c | 4 ++ monitor.c | 49 +++++++++++++++++++++++++++++++++ target-i386/cpu.h | 22 +++++++++++++++ target-i386/helper.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ target-i386/op_helper.c | 34 +++++++++++++++++++++++ 6 files changed, 183 insertions(+)