diff mbox

[05/14] target-i386: Enable control registers for MPX

Message ID 1436429849-18052-6-git-send-email-rth@twiddle.net
State New
Headers show

Commit Message

Richard Henderson July 9, 2015, 8:17 a.m. UTC
Enable and disable at CPL changes, MSR changes, and XRSTOR changes.

Signed-off-by: Richard Henderson <rth@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

Comments

Paolo Bonzini July 9, 2015, 1:12 p.m. UTC | #1
On 09/07/2015 10:17, Richard Henderson wrote:
> +    /* ??? 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);

The format that QEMU (and KVM) are using is based on AMD's format for
the state save area.  In my copy of the AMD manual this is Table 10-1.

Reserved areas in there are 0xfef0-0xfefb and 0xff04-0xff1f.

I definitely should update KVM to save/restore BNDCFGS.  Thanks for the
heads up!

Paolo
Paolo Bonzini July 9, 2015, 1:18 p.m. UTC | #2
On 09/07/2015 10:17, Richard Henderson wrote:
> 
> +void cpu_sync_bndcs_hf(CPUX86State *env)

s/hf/hflags/ :)

> +{
> +    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/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);

Why aren't you just using a goto, like

    if (ret < 0) {
        goto out;
    }
    ret = 0;
out:
    cpu_sync_bndcs_hf(&cpu->env);
    return ret;

>      return 0;
Richard Henderson July 10, 2015, 7:44 a.m. UTC | #3
On 07/09/2015 02:18 PM, Paolo Bonzini wrote:
>
>
> On 09/07/2015 10:17, Richard Henderson wrote:
>>
>> +void cpu_sync_bndcs_hf(CPUX86State *env)
>
> s/hf/hflags/ :)

Heh.  Done.

> Why aren't you just using a goto, like
>
>      if (ret < 0) {
>          goto out;
>      }
>      ret = 0;
> out:
>      cpu_sync_bndcs_hf(&cpu->env);
>      return ret;

Doh.  Done.  Thanks,


r~
Paolo Bonzini Feb. 9, 2016, 1:28 p.m. UTC | #4
On 09/07/2015 10:17, Richard Henderson wrote:
> +    /* Disallow enabling only half of MPX.  */
> +    if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) {

I'm refreshing patches 1-4 to add PKE support, and this caught my eye...

What about just

	if (!!(mask & XSTATE_BNDCSR) != !!(mask & XSTATE_BNDREGS))

?

Paolo
Eric Blake Feb. 9, 2016, 3:50 p.m. UTC | #5
On 02/09/2016 06:28 AM, Paolo Bonzini wrote:
> On 09/07/2015 10:17, Richard Henderson wrote:
>> +    /* Disallow enabling only half of MPX.  */
>> +    if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) {
> 
> I'm refreshing patches 1-4 to add PKE support, and this caught my eye...
> 
> What about just
> 
> 	if (!!(mask & XSTATE_BNDCSR) != !!(mask & XSTATE_BNDREGS))

Or even:

    if (!(mask & XSTATE_BNDCSR) != !(mask & XSTATE_BNDREGS))
Paolo Bonzini Feb. 9, 2016, 3:50 p.m. UTC | #6
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256



On 09/02/2016 16:50, Eric Blake wrote:
>>> What about just
>>> 
>>> if (!!(mask & XSTATE_BNDCSR) != !!(mask & XSTATE_BNDREGS))
> Or even:
> 
> if (!(mask & XSTATE_BNDCSR) != !(mask & XSTATE_BNDREGS))
> 

This is more mysterious. :)

Paolo
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJWugrcAAoJEL/70l94x66DYRMH/3+xoIVWaY4x36J4KCO97Qbt
enRqt+3LHVf8c8lDPmlQoVeGv6AUV8UEztrHEE61lL9S11zsqLTIJ7VWuhfS4K8B
BzJa9PZ+JFLmLj7p0qMU0HyqQ6j8Db5AyaqUUWqCALy2GFspX8vGkY6mvtjyKJP2
xxZ4nb0ESiEBsDeyuEff0sC7s5N2TlPmLVn9Vp2WdlTDPIsPKchRy3XaLGZ07mkh
LT+ymbyqbq20v7rbLGZoxBnGmsE2RbR+OfkAAa+fqHWZo3kfkMusRCeRCRKTWpix
6jhZJjc1X9+mCAEsobuxOMRwZkZP+4kZv3JYCod7zSuORgK4b+RbMwTfM5Row6U=
=FIiX
-----END PGP SIGNATURE-----
Richard Henderson Feb. 9, 2016, 7:08 p.m. UTC | #7
On 02/10/2016 12:28 AM, Paolo Bonzini wrote:
> On 09/07/2015 10:17, Richard Henderson wrote:
>> +    /* Disallow enabling only half of MPX.  */
>> +    if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) {
>
> I'm refreshing patches 1-4 to add PKE support, and this caught my eye...
>
> What about just
>
> 	if (!!(mask & XSTATE_BNDCSR) != !!(mask & XSTATE_BNDREGS))
>
> ?

Hmph.  Missed optimization for your version.
It is a bit more readable though, I admit...


r~



void bar();
void foo(unsigned mask)
{
   if ((mask ^ (mask * 4)) & 8) {
     bar();
   }
}
void baz(unsigned mask)
{
   if (!!(mask & 8) != !!(mask & 2)) {
     bar();
   }
}


foo:
         leal    0(,%rdi,4), %eax
         xorl    %eax, %edi
         andl    $8, %edi
         jne     .L4
	...
baz:
         movl    %edi, %eax
         shrl    %edi
         shrl    $3, %eax
         andl    $1, %edi
         andl    $1, %eax
         cmpb    %dil, %al
         je      .L5
	...
clang_baz:
         movl    %edi, %eax
         shrl    $3, %eax
         shrl    %edi
         xorl    %eax, %edi
         testb   $1, %dil
         jne     .LBB1_2
diff mbox

Patch

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: