diff --git a/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj b/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj
index fd550ee9..2082c5ac 100644
--- a/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj
+++ b/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj
@@ -65,6 +65,7 @@
+
diff --git a/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj.filters b/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj.filters
index 56a50494..78d5c9ab 100644
--- a/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj.filters
+++ b/msvc/unicorn/x86_64-softmmu/x86_64-softmmu.vcxproj.filters
@@ -140,6 +140,9 @@
target-i386
+
+ target-i386
+
target-i386
diff --git a/qemu/target-i386/Makefile.objs b/qemu/target-i386/Makefile.objs
index e18857fd..eeb0bda0 100644
--- a/qemu/target-i386/Makefile.objs
+++ b/qemu/target-i386/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += translate.o helper.o cpu.o bpt_helper.o
obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o
-obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o
+obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o mpx_helper.o
obj-$(CONFIG_SOFTMMU) += arch_memory_mapping.o
obj-y += unicorn.o
diff --git a/qemu/target-i386/cpu.c b/qemu/target-i386/cpu.c
index 777d93a0..c8e474b5 100644
--- a/qemu/target-i386/cpu.c
+++ b/qemu/target-i386/cpu.c
@@ -324,7 +324,7 @@ static const char *cpuid_6_feature_name[] = {
#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \
CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \
CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \
- CPUID_7_0_EBX_CLWB)
+ CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX)
/* missing:
CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2,
CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM,
@@ -518,10 +518,60 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
};
#undef REGISTER
-typedef struct ExtSaveArea {
- uint32_t feature, bits;
- uint32_t offset, size;
-} ExtSaveArea;
+ const ExtSaveArea x86_ext_save_areas[] = {
+ // XSTATE_FP_BIT
+ {
+ 0, 0,
+ 0, 0
+ },
+ // XSTATE_SSE_BIT
+ {
+ 0, 0,
+ 0, 0,
+ },
+ // XSTATE_YMM_BIT
+ {
+ FEAT_1_ECX, CPUID_EXT_AVX,
+ 0x240,
+ 0x100,
+ },
+ // XSTATE_BNDREGS_BIT
+ {
+ FEAT_7_0_EBX, CPUID_7_0_EBX_MPX,
+ 0x3c0,
+ 0x40,
+ },
+ // XSTATE_BNDCSR_BIT
+ {
+ FEAT_7_0_EBX, CPUID_7_0_EBX_MPX,
+ 0x400,
+ 0x40,
+ },
+ // XSTATE_OPMASK_BIT
+ {
+ FEAT_7_0_EBX, CPUID_7_0_EBX_AVX512F,
+ 0x440,
+ 0x40,
+ },
+ // XSTATE_ZMM_Hi256_BIT
+ {
+ FEAT_7_0_EBX, CPUID_7_0_EBX_AVX512F,
+ 0x480,
+ 0x200,
+ },
+ // XSTATE_Hi16_ZMM_BIT
+ {
+ FEAT_7_0_EBX, CPUID_7_0_EBX_AVX512F,
+ 0x680,
+ 0x400,
+ },
+ // XSTATE_PKRU_BIT
+ {
+ FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU,
+ 0xA80,
+ 0x8,
+ },
+};
const char *get_register_name_32(unsigned int reg)
{
@@ -2333,11 +2383,53 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*edx = 0;
break;
case 0xD: {
+ uint64_t ena_mask;
+ int i;
+
/* Processor Extended State */
*eax = 0;
*ebx = 0;
*ecx = 0;
*edx = 0;
+ if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) {
+ break;
+ }
+
+ //if (kvm_enabled()) {
+ // Unicorn: commented out
+ //ena_mask = kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX);
+ //ena_mask <<= 32;
+ //ena_mask |= kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX);
+ //} else {
+ ena_mask = -1;
+ //}
+
+ if (count == 0) {
+ *ecx = 0x240;
+ for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) {
+ const ExtSaveArea *esa = &x86_ext_save_areas[i];
+ if ((env->features[esa->feature] & esa->bits) == esa->bits
+ && ((ena_mask >> i) & 1) != 0) {
+ if (i < 32) {
+ *eax |= 1u << i;
+ } else {
+ *edx |= 1u << (i - 32);
+ }
+ *ecx = MAX(*ecx, esa->offset + esa->size);
+ }
+ }
+ *eax |= ena_mask & (XSTATE_FP | XSTATE_SSE);
+ *ebx = *ecx;
+ } else if (count == 1) {
+ *eax = env->features[FEAT_XSAVE];
+ } else if (count < ARRAY_SIZE(x86_ext_save_areas)) {
+ const ExtSaveArea *esa = &x86_ext_save_areas[count];
+ if ((env->features[esa->feature] & esa->bits) == esa->bits
+ && ((ena_mask >> count) & 1) != 0) {
+ *eax = esa->size;
+ *ebx = esa->offset;
+ }
+ }
break;
}
case 0x80000000:
diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h
index d73d660e..e375c479 100644
--- a/qemu/target-i386/cpu.h
+++ b/qemu/target-i386/cpu.h
@@ -155,6 +155,8 @@
#define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */
#define HF_SMAP_SHIFT 23 /* CR4.SMAP */
#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */
+#define HF_MPX_EN_SHIFT 25 /* MPX Enabled (CR4+XCR0+BNDCFGx) */
+#define HF_MPX_IU_SHIFT 26 /* BND registers in-use */
#define HF_CPL_MASK (3 << HF_CPL_SHIFT)
#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
@@ -179,6 +181,8 @@
#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT)
#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT)
+#define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT)
+#define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT)
/* hflags2 */
@@ -187,12 +191,14 @@
#define HF2_NMI_SHIFT 2 /* CPU serving NMI */
#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
#define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */
+#define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */
#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT)
#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
#define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT)
+#define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT)
#define CR0_PE_SHIFT 0
#define CR0_MP_SHIFT 1
@@ -396,6 +402,16 @@
#define MSR_IA32_BNDCFGS 0x00000d90
#define MSR_IA32_XSS 0x00000da0
+#define XSTATE_FP_BIT 0
+#define XSTATE_SSE_BIT 1
+#define XSTATE_YMM_BIT 2
+#define XSTATE_BNDREGS_BIT 3
+#define XSTATE_BNDCSR_BIT 4
+#define XSTATE_OPMASK_BIT 5
+#define XSTATE_ZMM_Hi256_BIT 6
+#define XSTATE_Hi16_ZMM_BIT 7
+#define XSTATE_PKRU_BIT 9
+
#define XSTATE_FP (1ULL << 0)
#define XSTATE_SSE (1ULL << 1)
#define XSTATE_YMM (1ULL << 2)
@@ -750,6 +766,10 @@ typedef struct BNDCSReg {
uint64_t sts;
} BNDCSReg;
+#define BNDCFG_ENABLE 1ULL
+#define BNDCFG_BNDPRESERVE 2ULL
+#define BNDCFG_BDIR_MASK TARGET_PAGE_MASK
+
#ifdef HOST_WORDS_BIGENDIAN
#define ZMM_B(n) _b_ZMMReg[63 - (n)]
#define ZMM_W(n) _w_ZMMReg[31 - (n)]
@@ -1113,7 +1133,14 @@ void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32);
int cpu_x86_signal_handler(int host_signum, void *pinfo,
void *puc);
-/* cpuid.c */
+/* cpu.c */
+typedef struct ExtSaveArea {
+ uint32_t feature, bits;
+ uint32_t offset, size;
+} ExtSaveArea;
+
+extern const ExtSaveArea x86_ext_save_areas[];
+
void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx);
@@ -1323,6 +1350,8 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access);
void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features);
void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features);
+/* mpx_helper.c */
+void cpu_sync_bndcs_hflags(CPUX86State *env);
/* Return name of 32-bit register, from a R_* constant */
const char *get_register_name_32(unsigned int reg);
diff --git a/qemu/target-i386/fpu_helper.c b/qemu/target-i386/fpu_helper.c
index 65784b31..4427f128 100644
--- a/qemu/target-i386/fpu_helper.c
+++ b/qemu/target-i386/fpu_helper.c
@@ -1197,6 +1197,22 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra)
}
}
+static void do_xsave_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra)
+{
+ int i;
+
+ for (i = 0; i < 4; i++, addr += 16) {
+ cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra);
+ cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra);
+ }
+}
+
+static void do_xsave_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra)
+{
+ cpu_stq_data_ra(env, addr, env->bndcs_regs.cfgu, ra);
+ cpu_stq_data_ra(env, addr + 8, env->bndcs_regs.sts, ra);
+}
+
void helper_fxsave(CPUX86State *env, target_ulong ptr)
{
uintptr_t ra = GETPC();
@@ -1221,9 +1237,16 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr)
static uint64_t get_xinuse(CPUX86State *env)
{
- /* We don't track XINUSE. We could calculate it here, but it's
- probably less work to simply indicate all components in use. */
- return -1;
+ uint64_t inuse = -1;
+
+ /* For the most part, we don't track XINUSE. We could calculate it
+ here for all components, but it's probably less work to simply
+ indicate in use. That said, the state of BNDREGS is important
+ enough to track in HFLAGS, so we might as well use that here. */
+ if ((env->hflags & HF_MPX_IU_MASK) == 0) {
+ inuse &= ~XSTATE_BNDREGS;
+ }
+ return inuse;
}
static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm,
@@ -1255,6 +1278,14 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm,
if (opt & XSTATE_SSE) {
do_xsave_sse(env, ptr, ra);
}
+ if (opt & XSTATE_BNDREGS) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
+ do_xsave_bndregs(env, ptr + off, ra);
+ }
+ if (opt & XSTATE_BNDCSR) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
+ do_xsave_bndcsr(env, ptr + off, ra);
+ }
/* Update the XSTATE_BV field. */
old_bv = cpu_ldq_data_ra(env, ptr + 512, ra);
@@ -1320,6 +1351,23 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra)
}
}
+static void do_xrstor_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra)
+{
+ int i;
+
+ for (i = 0; i < 4; i++, addr += 16) {
+ env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra);
+ env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra);
+ }
+}
+
+static void do_xrstor_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra)
+{
+ /* FIXME: Extend highest implemented bit of linear address. */
+ env->bndcs_regs.cfgu = cpu_ldq_data_ra(env, addr, ra);
+ env->bndcs_regs.sts = cpu_ldq_data_ra(env, addr + 8, ra);
+}
+
void helper_fxrstor(CPUX86State *env, target_ulong ptr)
{
uintptr_t ra = GETPC();
@@ -1400,6 +1448,25 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
memset(env->xmm_regs, 0, sizeof(env->xmm_regs));
}
}
+ if (rfbm & XSTATE_BNDREGS) {
+ if (xstate_bv & XSTATE_BNDREGS) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset;
+ do_xrstor_bndregs(env, ptr + off, ra);
+ env->hflags |= HF_MPX_IU_MASK;
+ } else {
+ memset(env->bnd_regs, 0, sizeof(env->bnd_regs));
+ env->hflags &= ~HF_MPX_IU_MASK;
+ }
+ }
+ if (rfbm & XSTATE_BNDCSR) {
+ if (xstate_bv & XSTATE_BNDCSR) {
+ target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset;
+ do_xrstor_bndcsr(env, ptr + off, ra);
+ } else {
+ memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs));
+ }
+ cpu_sync_bndcs_hflags(env);
+ }
}
uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx)
@@ -1443,7 +1510,13 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask)
goto do_gpf;
}
+ /* Disallow enabling only half of MPX. */
+ if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) {
+ goto do_gpf;
+ }
+
env->xcr0 = mask;
+ cpu_sync_bndcs_hflags(env);
return;
do_gpf:
diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c
index 8ad69e35..11179c2b 100644
--- a/qemu/target-i386/helper.c
+++ b/qemu/target-i386/helper.c
@@ -491,6 +491,8 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
env->cr[4] = new_cr4;
env->hflags = hflags;
+
+ cpu_sync_bndcs_hflags(env);
}
#if defined(CONFIG_USER_ONLY)
diff --git a/qemu/target-i386/misc_helper.c b/qemu/target-i386/misc_helper.c
index 0e5f7090..eaf1c0f8 100644
--- a/qemu/target-i386/misc_helper.c
+++ b/qemu/target-i386/misc_helper.c
@@ -363,6 +363,12 @@ void helper_wrmsr(CPUX86State *env)
case MSR_IA32_MISC_ENABLE:
env->msr_ia32_misc_enable = val;
break;
+ case MSR_IA32_BNDCFGS:
+ /* FIXME: #GP if reserved bits are set. */
+ /* FIXME: Extend highest implemented bit of linear address. */
+ env->msr_bndcfgs = val;
+ cpu_sync_bndcs_hflags(env);
+ break;
default:
if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL
&& (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL +
@@ -508,6 +514,9 @@ void helper_rdmsr(CPUX86State *env)
case MSR_IA32_MISC_ENABLE:
val = env->msr_ia32_misc_enable;
break;
+ case MSR_IA32_BNDCFGS:
+ val = env->msr_bndcfgs;
+ break;
default:
if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL
&& (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL +
diff --git a/qemu/target-i386/smm_helper.c b/qemu/target-i386/smm_helper.c
index 6af316ce..8d9df241 100644
--- a/qemu/target-i386/smm_helper.c
+++ b/qemu/target-i386/smm_helper.c
@@ -88,6 +88,10 @@ void do_smm_enter(X86CPU *cpu)
x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit);
x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
+ /* ??? Vol 1, 16.5.6 Intel MPX and SMM says that IA32_BNDCFGS
+ is saved at offset 7ED0. Vol 3, 34.4.1.1, Table 32-2, has
+ 7EA0-7ED7 as "reserved". What's this, and what's really
+ supposed to happen? */
x86_stq_phys(cs, sm_state + 0x7ed0, env->efer);
x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]);
diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c
index e42a6a1c..4855929f 100644
--- a/qemu/target-i386/translate.c
+++ b/qemu/target-i386/translate.c
@@ -8296,6 +8296,11 @@ case 0x101:
tcg_gen_concat_tl_i64(tcg_ctx, cpu_tmp1_i64, *cpu_regs[R_EAX],
*cpu_regs[R_EDX]);
gen_helper_xrstor(tcg_ctx, cpu_env, cpu_A0, cpu_tmp1_i64);
+ /* XRSTOR is how MPX is enabled, which changes how
+ we translate. Thus we need to end the TB. */
+ gen_update_cc_op(s);
+ gen_jmp_im(s, s->pc - s->cs_base);
+ gen_eob(s);
break;
CASE_MEM_OP(6): /* xsaveopt / clwb */