From cacb60b57bef2e3d9a936a64ff41668ba8420201 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 20 Feb 2018 13:00:05 -0500 Subject: [PATCH] target-i386: Enable control registers for MPX Enable and disable at CPL changes, MSR changes, and XRSTOR changes. Backports commit f4f1110e4b34797ddfa87bb28f9518b9256778be from qemu --- .../x86_64-softmmu/x86_64-softmmu.vcxproj | 1 + .../x86_64-softmmu.vcxproj.filters | 3 + qemu/target-i386/Makefile.objs | 2 +- qemu/target-i386/cpu.c | 102 +++++++++++++++++- qemu/target-i386/cpu.h | 31 +++++- qemu/target-i386/fpu_helper.c | 79 +++++++++++++- qemu/target-i386/helper.c | 2 + qemu/target-i386/misc_helper.c | 9 ++ qemu/target-i386/smm_helper.c | 4 + qemu/target-i386/translate.c | 5 + 10 files changed, 228 insertions(+), 10 deletions(-) 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 */