From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1M0ryA-0005Uz-L8 for qemu-devel@nongnu.org; Mon, 04 May 2009 02:48:06 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1M0ry6-0005Tz-T3 for qemu-devel@nongnu.org; Mon, 04 May 2009 02:48:06 -0400 Received: from [199.232.76.173] (port=34035 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1M0ry6-0005Tu-Hx for qemu-devel@nongnu.org; Mon, 04 May 2009 02:48:02 -0400 Received: from mga09.intel.com ([134.134.136.24]:56159) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1M0ry5-0002u3-NH for qemu-devel@nongnu.org; Mon, 04 May 2009 02:48:02 -0400 From: Huang Ying Content-Type: multipart/signed; micalg="pgp-sha1"; protocol="application/pgp-signature"; boundary="=-+wkzlFLdWA83BLjPjJEW" Date: Mon, 04 May 2009 14:47:57 +0800 Message-Id: <1241419677.8815.42.camel@yhuang-dev.sh.intel.com> Mime-Version: 1.0 Subject: [Qemu-devel] [RFC -v3 1/2] QEMU-KVM: MCE: Add MCE simulation to qemu/tcg List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Avi Kivity , Anthony Liguori , Andi Kleen Cc: qemu-devel@nongnu.org, "kvm@vger.kernel.org" --=-+wkzlFLdWA83BLjPjJEW Content-Type: text/plain Content-Transfer-Encoding: quoted-printable - 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 --- 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(+) --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -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 =20 +#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 @@ =20 #define MSR_MTRRdefType 0x2ff =20 +#define MSR_MC0_CTL 0x400 +#define MSR_MC0_STATUS 0x401 +#define MSR_MC0_ADDR 0x402 +#define MSR_MC0_MISC 0x403 + #define MSR_EFER 0xc0000080 =20 #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; =20 CPUX86State *cpu_x86_init(const char *cpu_model); --- a/target-i386/op_helper.c +++ b/target-i386/op_helper.c @@ -3104,7 +3104,23 @@ void helper_wrmsr(void) case MSR_MTRRdefType: env->mtrr_deftype =3D val; break; + case MSR_MCG_STATUS: + env->mcg_status =3D val; + break; + case MSR_MCG_CTL: + if ((env->mcg_cap & MCG_CTL_P) + && (val =3D=3D 0 || val =3D=3D ~(uint64_t)0)) + env->mcg_ctl =3D val; + break; default: + if ((uint32_t)ECX >=3D MSR_MC0_CTL + && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) { + uint32_t offset =3D (uint32_t)ECX - MSR_MC0_CTL; + if ((offset & 0x3) !=3D 0 + || (val =3D=3D 0 || val =3D=3D ~(uint64_t)0)) + env->mce_banks[offset] =3D val; + break; + } /* XXX: exception ? */ break; } @@ -3223,7 +3239,25 @@ void helper_rdmsr(void) /* XXX: exception ? */ val =3D 0; break; + case MSR_MCG_CAP: + val =3D env->mcg_cap; + break; + case MSR_MCG_CTL: + if (env->mcg_cap & MCG_CTL_P) + val =3D env->mcg_ctl; + else + val =3D 0; + break; + case MSR_MCG_STATUS: + val =3D env->mcg_status; + break; default: + if ((uint32_t)ECX >=3D MSR_MC0_CTL + && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) { + uint32_t offset =3D (uint32_t)ECX - MSR_MC0_CTL; + val =3D env->mce_banks[offset]; + break; + } /* XXX: exception ? */ val =3D 0; break; --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1432,6 +1432,75 @@ static void breakpoint_handler(CPUState=20 } #endif /* !CONFIG_USER_ONLY */ =20 +/* 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 =3D cenv->mcg_cap; + unsigned bank_num =3D mcg_cap & 0xff; + uint64_t *banks =3D cenv->mce_banks; + + if (bank >=3D 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 !=3D ~(uint64_t)0) + return; + banks +=3D 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] !=3D ~(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 |=3D MCI_STATUS_OVER; + banks[2] =3D addr; + banks[3] =3D misc; + cenv->mcg_status =3D mcg_status; + banks[1] =3D 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 |=3D MCI_STATUS_OVER; + banks[2] =3D addr; + banks[3] =3D misc; + banks[1] =3D status; + } else + banks[1] |=3D MCI_STATUS_OVER; +} + +static void mce_init(CPUX86State *cenv) +{ + unsigned int bank, bank_num; + + if (((cenv->cpuid_version >> 8)&0xf) >=3D 6 + && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) =3D=3D (CPUID_MCE|= CPUID_MCA)) { + cenv->mcg_cap =3D MCE_CAP_DEF | MCE_BANKS_DEF; + cenv->mcg_ctl =3D ~(uint64_t)0; + bank_num =3D cenv->mcg_cap & 0xff; + cenv->mce_banks =3D qemu_mallocz(bank_num * sizeof(uint64_t) * 4); + for (bank =3D 0; bank < bank_num; bank++) + cenv->mce_banks[bank*4] =3D ~(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); --- a/monitor.c +++ b/monitor.c @@ -1676,6 +1676,28 @@ static void do_acl(Monitor *mon, } } =20 +#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 =3D ((uint64_t)status_hi << 32) | status_lo; + uint64_t mcg_status =3D ((uint64_t)mcg_status_hi << 32) | mcg_status_l= o; + uint64_t addr =3D ((uint64_t)addr_hi << 32) | addr_lo; + uint64_t misc =3D ((uint64_t)misc_hi << 32) | misc_lo; + + for (cenv =3D first_cpu; cenv !=3D NULL; cenv =3D cenv->next_cpu) + if (cenv->cpu_index =3D=3D 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[] =3D { { "help|?", "s?", help_cmd, @@ -1793,6 +1815,9 @@ static const mon_cmd_t mon_cmds[] =3D { "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, }, }; =20 @@ -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); =20 #ifdef DEBUG monitor_printf(mon, "command=3D'%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 =3D 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 =3D 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 =3D 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_ar= gs); goto fail; --- a/cpu-all.h +++ b/cpu-all.h @@ -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. */ =20 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); =20 +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, --- a/cpu-exec.c +++ b/cpu-exec.c @@ -391,6 +391,10 @@ int cpu_exec(CPUState *env1) env->hflags2 |=3D HF2_NMI_MASK; do_interrupt(EXCP02_NMI, 0, 0, 0, 1); next_tb =3D 0; + } else if (interrupt_request & CPU_INTERRUPT_MCE) { + env->interrupt_request &=3D ~CPU_INTERRUPT_MCE= ; + do_interrupt(EXCP12_MCHK, 0, 0, 0, 0); + next_tb =3D 0; } else if ((interrupt_request & CPU_INTERRUPT_HARD= ) && (((env->hflags2 & HF2_VINTR_MASK) &&=20 (env->hflags2 & HF2_HIF_MASK)) || --=-+wkzlFLdWA83BLjPjJEW Content-Type: application/pgp-signature; name="signature.asc" Content-Description: This is a digitally signed message part -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEABECAAYFAkn+j5oACgkQKhFGF+eHlpiAngCaA4aUeD7pv1u4Eyz9vA1lCUd0 ML4AmwU4tj5zPuOXyL4sDqpnowMffGVE =wtXU -----END PGP SIGNATURE----- --=-+wkzlFLdWA83BLjPjJEW--