Enable and disable at CPL changes, MSR changes, and XRSTOR changes. Signed-off-by: Richard Henderson <r...@twiddle.net> --- target-i386/Makefile.objs | 2 +- target-i386/cpu.c | 18 +++++------ target-i386/cpu.h | 21 ++++++++++++- target-i386/fpu_helper.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- target-i386/helper.c | 2 ++ target-i386/kvm.c | 5 +++ target-i386/misc_helper.c | 9 ++++++ target-i386/mpx_helper.c | 51 +++++++++++++++++++++++++++++++ target-i386/smm_helper.c | 4 +++ target-i386/translate.c | 5 +++ 10 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 target-i386/mpx_helper.c
diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs index 7a1df2c..2cb07e1 100644 --- a/target-i386/Makefile.objs +++ b/target-i386/Makefile.objs @@ -1,6 +1,6 @@ obj-y += translate.o helper.o cpu.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-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-i386/cpu.c b/target-i386/cpu.c index b2fc59f..c19ff8a 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -335,7 +335,8 @@ static const char *cpuid_xsave_feature_name[] = { #define TCG_SVM_FEATURES 0 #define TCG_KVM_FEATURES 0 #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_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ + 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, @@ -433,12 +434,7 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { }; #undef REGISTER -typedef struct ExtSaveArea { - uint32_t feature, bits; - uint32_t offset, size; -} ExtSaveArea; - -static const ExtSaveArea ext_save_areas[] = { +const ExtSaveArea x86_ext_save_areas[] = { [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, .offset = 0x240, .size = 0x100 }, [3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, @@ -2427,8 +2423,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (count == 0) { *ecx = 0x240; - for (i = 2; i < ARRAY_SIZE(ext_save_areas); i++) { - const ExtSaveArea *esa = &ext_save_areas[i]; + 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) { @@ -2443,8 +2439,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = *ecx; } else if (count == 1) { *eax = env->features[FEAT_XSAVE]; - } else if (count < ARRAY_SIZE(ext_save_areas)) { - const ExtSaveArea *esa = &ext_save_areas[count]; + } 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; diff --git a/target-i386/cpu.h b/target-i386/cpu.h index a8153c0..8f0fc35 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -155,6 +155,9 @@ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ #define HF_SMAP_SHIFT 23 /* CR4.SMAP */ #define HF_OSXSAVE_SHIFT 24 /* CR4.OSXSAVE */ +#define HF_MPX_EN_SHIFT 25 /* MPX Enabled (CR4+XCR0+BNDCFGx) */ +#define HF_MPX_PR_SHIFT 26 /* BNDCFGx.BNDPRESERVE */ +#define HF_MPX_IU_SHIFT 27 /* BND registers in-use */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -179,6 +182,9 @@ #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) #define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) #define HF_OSXSAVE_MASK (1 << HF_OSXSAVE_SHIFT) +#define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT) +#define HF_MPX_PR_MASK (1 << HF_MPX_PR_SHIFT) +#define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT) /* hflags2 */ @@ -741,6 +747,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 XMM_B(n) _b[63 - (n)] #define XMM_W(n) _w[31 - (n)] @@ -1096,7 +1106,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); @@ -1336,6 +1353,8 @@ void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, 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_hf(CPUX86State *env); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c index d0b960c..c3cb218 100644 --- a/target-i386/fpu_helper.c +++ b/target-i386/fpu_helper.c @@ -1154,6 +1154,22 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr) } } +static void do_xsave_bndregs(CPUX86State *env, target_ulong addr) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + cpu_stq_data(env, addr, env->bnd_regs[i].lb); + cpu_stq_data(env, addr + 8, env->bnd_regs[i].ub); + } +} + +static void do_xsave_bndcsr(CPUX86State *env, target_ulong addr) +{ + cpu_stq_data(env, addr, env->bndcs_regs.cfgu); + cpu_stq_data(env, addr + 8, env->bndcs_regs.sts); +} + void helper_fxsave(CPUX86State *env, target_ulong ptr) { /* The operand must be 16 byte aligned */ @@ -1176,9 +1192,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, @@ -1205,6 +1228,12 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, if (opt & XSTATE_SSE) { do_xsave_sse(env, ptr); } + if (opt & XSTATE_BNDREGS) { + do_xsave_bndregs(env, ptr + x86_ext_save_areas[XSTATE_BNDREGS].offset); + } + if (opt & XSTATE_BNDCSR) { + do_xsave_bndcsr(env, ptr + x86_ext_save_areas[XSTATE_BNDCSR].offset); + } /* Update the XSTATE_BV field. */ old_bv = cpu_ldq_data(env, ptr + 512); @@ -1270,6 +1299,24 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr) } } +static void do_xrstor_bndregs(CPUX86State *env, target_ulong addr) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + env->bnd_regs[i].lb = cpu_ldq_data(env, addr); + env->bnd_regs[i].ub = cpu_ldq_data(env, addr + 8); + } +} + +static void do_xrstor_bndcsr(CPUX86State *env, target_ulong addr) +{ + /* FIXME: Extend highest implemented bit of linear address. */ + env->bndcs_regs.cfgu = cpu_ldq_data(env, addr); + env->bndcs_regs.sts = cpu_ldq_data(env, addr + 8); + cpu_sync_bndcs_hf(env); +} + void helper_fxrstor(CPUX86State *env, target_ulong ptr) { /* The operand must be 16 byte aligned */ @@ -1342,6 +1389,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) { + do_xrstor_bndregs(env, + ptr + x86_ext_save_areas[XSTATE_BNDREGS].offset); + 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) { + do_xrstor_bndcsr(env, + ptr + x86_ext_save_areas[XSTATE_BNDCSR].offset); + } else { + memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); + } + cpu_sync_bndcs_hf(env); + } } uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) @@ -1375,7 +1441,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_hf(env); return; do_gpf: diff --git a/target-i386/helper.c b/target-i386/helper.c index 5e8e63d..c7edaa4 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -489,6 +489,8 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) env->cr[4] = new_cr4; env->hflags = hflags; + + cpu_sync_bndcs_hf(env); } #if defined(CONFIG_USER_ONLY) diff --git a/target-i386/kvm.c b/target-i386/kvm.c index f057982..27ae029 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -2186,6 +2186,11 @@ int kvm_arch_get_registers(CPUState *cs) if (ret < 0) { return ret; } + + /* ??? HFLAGS may be out of sync if any of the above error out. + But there seems little point in recomputing this multiple times. */ + cpu_sync_bndcs_hf(&cpu->env); + return 0; } diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c index 52c5d65..1e1cc4a 100644 --- a/target-i386/misc_helper.c +++ b/target-i386/misc_helper.c @@ -394,6 +394,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_hf(env); + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + @@ -539,6 +545,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/target-i386/mpx_helper.c b/target-i386/mpx_helper.c new file mode 100644 index 0000000..decb2ea --- /dev/null +++ b/target-i386/mpx_helper.c @@ -0,0 +1,51 @@ +/* + * x86 MPX helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + + +void cpu_sync_bndcs_hf(CPUX86State *env) +{ + uint32_t hflags = env->hflags; + uint32_t bndcsr; + + if ((hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + if ((hflags & HF_OSXSAVE_MASK) + && (env->xcr0 & XSTATE_BNDCSR) + && (bndcsr & BNDCFG_ENABLE)) { + hflags |= HF_MPX_EN_MASK; + } else { + hflags &= ~HF_MPX_EN_MASK; + } + + if (bndcsr & BNDCFG_BNDPRESERVE) { + hflags |= HF_MPX_PR_MASK; + } else { + hflags &= ~HF_MPX_PR_MASK; + } + + env->hflags = hflags; +} diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index 02e24b9..77fc1b8 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -97,6 +97,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/target-i386/translate.c b/target-i386/translate.c index a98347f..eb30401 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -7714,6 +7714,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); gen_helper_xrstor(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->pc - s->cs_base); + gen_eob(s); } break; case 6: -- 2.4.3