target-i386: Enable control registers for MPX

Enable and disable at CPL changes, MSR changes, and XRSTOR changes.

Backports commit f4f1110e4b34797ddfa87bb28f9518b9256778be from qemu
This commit is contained in:
Richard Henderson 2018-02-20 13:00:05 -05:00 committed by Lioncash
parent 7a7a72f49b
commit cacb60b57b
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
10 changed files with 228 additions and 10 deletions

View file

@ -65,6 +65,7 @@
<ClCompile Include="..\..\..\qemu\target-i386\int_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\mem_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\misc_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\mpx_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\seg_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\smm_helper.c" />
<ClCompile Include="..\..\..\qemu\target-i386\svm_helper.c" />

View file

@ -140,6 +140,9 @@
<ClCompile Include="..\..\..\qemu\target-i386\misc_helper.c">
<Filter>target-i386</Filter>
</ClCompile>
<ClCompile Include="..\..\..\qemu\target-i386\mpx_helper.c">
<Filter>target-i386</Filter>
</ClCompile>
<ClCompile Include="..\..\..\qemu\target-i386\seg_helper.c">
<Filter>target-i386</Filter>
</ClCompile>

View file

@ -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

View file

@ -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:

View file

@ -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);

View file

@ -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:

View file

@ -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)

View file

@ -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 +

View file

@ -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]);

View file

@ -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 */