diff --git a/qemu/target-i386/cpu.c b/qemu/target-i386/cpu.c index 7c0a54bb..777d93a0 100644 --- a/qemu/target-i386/cpu.c +++ b/qemu/target-i386/cpu.c @@ -333,6 +333,9 @@ static const char *cpuid_6_feature_name[] = { #define TCG_APM_FEATURES 0 #define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT +#define TCG_XSAVE_FEATURES (CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1) + /* missing: + CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ typedef struct FeatureWordInfo { const char **feat_names; @@ -422,7 +425,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { true,1, R_EAX, 0, - 0, + TCG_XSAVE_FEATURES, }, // FEAT_ARAT { @@ -484,7 +487,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_eax = 0xd, .cpuid_needs_ecx = true, .cpuid_ecx = 1, .cpuid_reg = R_EAX, - .tcg_features = 0, + .tcg_features = TCG_XSAVE_FEATURES, }, [FEAT_6_EAX] = { .feat_names = cpuid_6_feature_name, diff --git a/qemu/target-i386/fpu_helper.c b/qemu/target-i386/fpu_helper.c index d7f34776..65784b31 100644 --- a/qemu/target-i386/fpu_helper.c +++ b/qemu/target-i386/fpu_helper.c @@ -1226,9 +1226,9 @@ static uint64_t get_xinuse(CPUX86State *env) return -1; } -void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) { - uintptr_t ra = GETPC(); uint64_t old_bv, new_bv; /* The OS must have enabled XSAVE. */ @@ -1243,21 +1243,36 @@ void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) /* Never save anything not enabled by XCR0. */ rfbm &= env->xcr0; + opt &= rfbm; - if (rfbm & XSTATE_FP) { + if (opt & XSTATE_FP) { do_xsave_fpu(env, ptr, ra); } if (rfbm & XSTATE_SSE) { + /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ do_xsave_mxcsr(env, ptr, ra); + } + if (opt & XSTATE_SSE) { do_xsave_sse(env, ptr, ra); } /* Update the XSTATE_BV field. */ old_bv = cpu_ldq_data_ra(env, ptr + 512, ra); - new_bv = (old_bv & ~rfbm) | (get_xinuse(env) & rfbm); + new_bv = (old_bv & ~rfbm) | (inuse & rfbm); cpu_stq_data_ra(env, ptr + 512, new_bv, ra); } +void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); +} + +void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uint64_t inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); +} + static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) { int i, fpus, fptag; @@ -1398,8 +1413,10 @@ uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) case 0: return env->xcr0; case 1: - /* FIXME: #GP if !CPUID.(EAX=0DH,ECX=1):EAX.XG1[bit 2]. */ - return env->xcr0 & get_xinuse(env); + if (env->features[FEAT_XSAVE] & CPUID_XSAVE_XGETBV1) { + return env->xcr0 & get_xinuse(env); + } + break; } raise_exception_ra(env, EXCP0D_GPF, GETPC()); } diff --git a/qemu/target-i386/helper.h b/qemu/target-i386/helper.h index 72aa35d0..7f21a693 100644 --- a/qemu/target-i386/helper.h +++ b/qemu/target-i386/helper.h @@ -190,6 +190,7 @@ DEF_HELPER_3(frstor, void, env, tl, int) DEF_HELPER_FLAGS_2(fxsave, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(fxrstor, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_3(xsave, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_3(xsaveopt, TCG_CALL_NO_WG, void, env, tl, i64) DEF_HELPER_FLAGS_3(xrstor, TCG_CALL_NO_WG, void, env, tl, i64) DEF_HELPER_FLAGS_2(xgetbv, TCG_CALL_NO_WG, i64, env, i32) DEF_HELPER_FLAGS_3(xsetbv, TCG_CALL_NO_WG, void, env, i32, i64) diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index 68634d0b..e42a6a1c 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -105,6 +105,7 @@ typedef struct DisasContext { int cpuid_ext2_features; int cpuid_ext3_features; int cpuid_7_0_ebx_features; + int cpuid_xsave_features; struct uc_struct *uc; // Unicorn @@ -8297,7 +8298,7 @@ case 0x101: gen_helper_xrstor(tcg_ctx, cpu_env, cpu_A0, cpu_tmp1_i64); break; - CASE_MEM_OP(6): /* clwb */ + CASE_MEM_OP(6): /* xsaveopt / clwb */ if (prefixes & PREFIX_LOCK) { goto illegal_op; } @@ -8307,9 +8308,19 @@ case 0x101: goto illegal_op; } gen_nop_modrm(env, s, modrm); - break; + } else { + /* xsaveopt */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 + || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(tcg_ctx, cpu_tmp1_i64, *cpu_regs[R_EAX], + *cpu_regs[R_EDX]); + gen_helper_xsaveopt(tcg_ctx, cpu_env, cpu_A0, cpu_tmp1_i64); } - goto illegal_op; + break; CASE_MEM_OP(7): /* clflush / clflushopt */ if (prefixes & PREFIX_LOCK) { @@ -8576,6 +8587,7 @@ void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; + dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; #ifdef TARGET_X86_64 dc->lma = (flags >> HF_LMA_SHIFT) & 1; dc->code64 = (flags >> HF_CS64_SHIFT) & 1;