[16/16] target-i386: Implement FSGSBASE
diff mbox

Message ID 1455039832-9133-17-git-send-email-rth@twiddle.net
State New
Headers show

Commit Message

Richard Henderson Feb. 9, 2016, 5:43 p.m. UTC
Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 target-i386/cpu.c        |  7 +++++--
 target-i386/helper.h     |  1 +
 target-i386/int_helper.c | 10 ++++++++++
 target-i386/translate.c  | 34 ++++++++++++++++++++++++++++++++++
 4 files changed, 50 insertions(+), 2 deletions(-)

Patch
diff mbox

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 063c8fb..0af43a3 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -358,9 +358,9 @@  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_MPX)
+          CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE)
           /* missing:
-          CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2,
+          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,
           CPUID_7_0_EBX_RDSEED */
 #define TCG_7_0_ECX_FEATURES 0
@@ -2730,6 +2730,9 @@  static void x86_cpu_reset(CPUState *s)
     if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
         cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK;
     }
+    if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_FSGSBASE) {
+        cr4 |= CR4_FSGSBASE_MASK;
+    }
 #endif
 
     env->xcr0 = xcr0;
diff --git a/target-i386/helper.h b/target-i386/helper.h
index 60a615f..e33451a 100644
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -15,6 +15,7 @@  DEF_HELPER_2(idivl_EAX, void, env, tl)
 DEF_HELPER_2(divq_EAX, void, env, tl)
 DEF_HELPER_2(idivq_EAX, void, env, tl)
 #endif
+DEF_HELPER_FLAGS_2(cr4_testbit, TCG_CALL_NO_WG, void, env, i32)
 
 DEF_HELPER_FLAGS_2(bndck, TCG_CALL_NO_WG, void, env, i32)
 DEF_HELPER_FLAGS_3(bndldx32, TCG_CALL_NO_WG, i64, env, tl, tl)
diff --git a/target-i386/int_helper.c b/target-i386/int_helper.c
index 9d0d21e..cf5bbb0 100644
--- a/target-i386/int_helper.c
+++ b/target-i386/int_helper.c
@@ -470,3 +470,13 @@  target_ulong helper_pext(target_ulong src, target_ulong mask)
 #include "shift_helper_template.h"
 #undef SHIFT
 #endif
+
+/* Test that BIT is enabled in CR4.  If not, raise an illegal opcode
+   exception.  This reduces the requirements for rare CR4 bits being
+   mapped into HFLAGS.  */
+void helper_cr4_testbit(CPUX86State *env, uint32_t bit)
+{
+    if (unlikely((env->cr[4] & bit) == 0)) {
+        raise_exception_ra(env, EXCP06_ILLOP, GETPC());
+    }
+}
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 59470f7..027c2fc 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -7898,6 +7898,40 @@  static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
             gen_nop_modrm(env, s, modrm);
             break;
 
+        case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */
+        case 0xc8 ... 0xc8: /* rdgsbase (f3 0f ae /1) */
+        case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */
+        case 0xd8 ... 0xd8: /* wrgsbase (f3 0f ae /3) */
+            if (CODE64(s)
+                && (prefixes & PREFIX_REPZ)
+                && !(prefixes & PREFIX_LOCK)
+                && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) {
+                TCGv base, treg, src, dst;
+
+                /* Preserve hflags bits by testing CR4 at runtime.  */
+                tcg_gen_movi_i32(cpu_tmp2_i32, CR4_FSGSBASE_MASK);
+                gen_helper_cr4_testbit(cpu_env, cpu_tmp2_i32);
+
+                base = cpu_seg_base[modrm & 8 ? R_GS : R_FS];
+                treg = cpu_regs[(modrm & 7) | REX_B(s)];
+
+                if (modrm & 0x10) {
+                    /* wr*base */
+                    dst = base, src = treg;
+                } else {
+                    /* rd*base */
+                    dst = treg, src = base;
+                }
+
+                if (s->dflag == MO_32) {
+                    tcg_gen_ext32u_tl(dst, src);
+                } else {
+                    tcg_gen_mov_tl(dst, src);
+                }
+                break;
+            }
+            goto illegal_op;
+
         case 0xf8: /* sfence / pcommit */
             if (prefixes & PREFIX_DATA) {
                 /* pcommit */