qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks
@ 2008-07-09 12:12 Jan Kiszka
  2008-07-14 10:34 ` [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2 Jan Kiszka
  0 siblings, 1 reply; 13+ messages in thread
From: Jan Kiszka @ 2008-07-09 12:12 UTC (permalink / raw)
  To: qemu-devel

As announced earlier, I developed an add-on feature for x86 emulation to
support segment type and limit checks. Now I finally completed porting
it over to latest SVN and added (hopefully) all the cases that were
missing so far (as the customer didn't need them).

The idea of this patch is to generate calls to a check helper only in
case the user requested this support via "-seg-checks". This feature
remains off by default as most x86 OSes do not care about protection via
segmentation anymore (and it was even removed from 64-bit modes by the
CPU vendors). Moreover, checking the segment type and limit on every
memory access is nothing that makes QEMU faster, so you will only want
this if you are looking for very accurate emulation.

The attached patch passed our own test cases, and it boots standard
Linux into X, both scenarios with -seg-checks enabled. However, though
things have been checked twice, I wouldn't be surprised if there are a
few typos or other errors in the check instrumentations. But at least,
those won't have any impact on the disabled, standard case.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 target-i386/cpu.h       |    8 
 target-i386/helper.h    |    5 
 target-i386/op_helper.c |   59 ++++++
 target-i386/translate.c |  409 +++++++++++++++++++++++++++++++++++++++---------
 vl.c                    |    7 
 5 files changed, 417 insertions(+), 71 deletions(-)

Index: b/target-i386/cpu.h
===================================================================
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -94,6 +94,10 @@
 
 #define DESC_TSS_BUSY_MASK (1 << 9)
 
+/* flags for segment access checks */
+#define ACC_READ        0x0000
+#define ACC_WRITE       0x0100
+
 /* eflags masks */
 #define CC_C   	0x0001
 #define CC_P 	0x0004
@@ -751,6 +755,10 @@ static inline void cpu_clone_regs(CPUSta
         env->regs[R_ESP] = newsp;
     env->regs[R_EAX] = 0;
 }
+
+#define seg_checks 0
+#else
+extern int seg_checks;
 #endif
 
 #define CPU_PC_FROM_TB(env, tb) env->eip = tb->pc - tb->cs_base
Index: b/target-i386/translate.c
===================================================================
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -591,7 +591,7 @@ static inline void gen_jmp_im(target_ulo
     tcg_gen_st_tl(cpu_tmp0, cpu_env, offsetof(CPUState, eip));
 }
 
-static inline void gen_string_movl_A0_ESI(DisasContext *s)
+static inline int gen_string_movl_A0_ESI(DisasContext *s)
 {
     int override;
 
@@ -604,6 +604,7 @@ static inline void gen_string_movl_A0_ES
         } else {
             gen_op_movq_A0_reg(R_ESI);
         }
+        return -1;
     } else
 #endif
     if (s->aflag) {
@@ -615,6 +616,7 @@ static inline void gen_string_movl_A0_ES
             gen_op_addl_A0_reg_sN(0, R_ESI);
         } else {
             gen_op_movl_A0_reg(R_ESI);
+            return R_DS;
         }
     } else {
         /* 16 address, always override */
@@ -624,13 +626,15 @@ static inline void gen_string_movl_A0_ES
         gen_op_andl_A0_ffff();
         gen_op_addl_A0_seg(override);
     }
+    return override;
 }
 
-static inline void gen_string_movl_A0_EDI(DisasContext *s)
+static inline int gen_string_movl_A0_EDI(DisasContext *s)
 {
 #ifdef TARGET_X86_64
     if (s->aflag == 2) {
         gen_op_movq_A0_reg(R_EDI);
+        return -1;
     } else
 #endif
     if (s->aflag) {
@@ -645,6 +649,7 @@ static inline void gen_string_movl_A0_ED
         gen_op_andl_A0_ffff();
         gen_op_addl_A0_seg(R_ES);
     }
+    return R_ES;
 }
 
 static inline void gen_op_movl_T0_Dshift(int ot) 
@@ -752,11 +757,31 @@ static void gen_check_io(DisasContext *s
     }
 }
 
+static inline void gen_check_segmented_access(int seg_reg, int type)
+{
+    tcg_gen_helper_0_3(helper_check_segmented_access, cpu_A0,
+                       tcg_const_i32(seg_reg), tcg_const_i32(type));
+}
+
+static inline void gen_check_segmented_access_size(int seg_reg, int type,
+                                                   int size)
+{
+    tcg_gen_helper_0_4(helper_check_segmented_access_size, cpu_A0,
+                       tcg_const_i32(seg_reg), tcg_const_i32(type),
+                       tcg_const_i32(size));
+}
+
 static inline void gen_movs(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
     gen_op_st_T0_A0(ot + s->mem_index);
     gen_op_movl_T0_Dshift(ot);
     gen_op_add_reg_T0(s->aflag, R_ESI);
@@ -1165,8 +1190,12 @@ static int gen_jz_ecx_string(DisasContex
 
 static inline void gen_stos(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
     gen_op_st_T0_A0(ot + s->mem_index);
     gen_op_movl_T0_Dshift(ot);
     gen_op_add_reg_T0(s->aflag, R_EDI);
@@ -1174,7 +1203,11 @@ static inline void gen_stos(DisasContext
 
 static inline void gen_lods(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
     gen_op_mov_reg_T0(ot, R_EAX);
     gen_op_movl_T0_Dshift(ot);
@@ -1183,8 +1216,12 @@ static inline void gen_lods(DisasContext
 
 static inline void gen_scas(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T1_A0(ot + s->mem_index);
     gen_op_cmpl_T0_T1_cc();
     gen_op_movl_T0_Dshift(ot);
@@ -1193,9 +1230,15 @@ static inline void gen_scas(DisasContext
 
 static inline void gen_cmps(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T1_A0(ot + s->mem_index);
     gen_op_cmpl_T0_T1_cc();
     gen_op_movl_T0_Dshift(ot);
@@ -1205,9 +1248,13 @@ static inline void gen_cmps(DisasContext
 
 static inline void gen_ins(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     if (use_icount)
         gen_io_start();
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
     /* Note: we must do this dummy write first to be restartable in
        case of page fault. */
     gen_op_movl_T0_0();
@@ -1225,9 +1272,13 @@ static inline void gen_ins(DisasContext
 
 static inline void gen_outs(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     if (use_icount)
         gen_io_start();
-    gen_string_movl_A0_ESI(s);
+    seg_reg = gen_string_movl_A0_ESI(s);
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
 
     gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
@@ -1882,7 +1933,7 @@ static void gen_shifti(DisasContext *s1,
     }
 }
 
-static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
+static int gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
 {
     target_long disp;
     int havesib;
@@ -2064,6 +2115,7 @@ static void gen_lea_modrm(DisasContext *
     disp = 0;
     *reg_ptr = opreg;
     *offset_ptr = disp;
+    return override;
 }
 
 static void gen_nop_modrm(DisasContext *s, int modrm)
@@ -2117,11 +2169,10 @@ static void gen_nop_modrm(DisasContext *
 }
 
 /* used for LEA and MOV AX, mem */
-static void gen_add_A0_ds_seg(DisasContext *s)
+static int gen_add_A0_ds_seg(DisasContext *s)
 {
     int override, must_add_seg;
     must_add_seg = s->addseg;
-    override = R_DS;
     if (s->override >= 0) {
         override = s->override;
         must_add_seg = 1;
@@ -2138,13 +2189,14 @@ static void gen_add_A0_ds_seg(DisasConte
             gen_op_addl_A0_seg(override);
         }
     }
+    return override;
 }
 
 /* generate modrm memory load or store of 'reg'. TMP0 is used if reg !=
    OR_TMP0 */
 static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store)
 {
-    int mod, rm, opreg, disp;
+    int mod, rm, opreg, disp, seg_reg;
 
     mod = (modrm >> 6) & 3;
     rm = (modrm & 7) | REX_B(s);
@@ -2159,12 +2211,16 @@ static void gen_ldst_modrm(DisasContext
                 gen_op_mov_reg_T0(ot, reg);
         }
     } else {
-        gen_lea_modrm(s, modrm, &opreg, &disp);
+        seg_reg = gen_lea_modrm(s, modrm, &opreg, &disp);
         if (is_store) {
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             if (reg != OR_TMP0)
                 gen_op_mov_TN_reg(ot, 0, reg);
             gen_op_st_T0_A0(ot + s->mem_index);
         } else {
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_READ | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
             if (reg != OR_TMP0)
                 gen_op_mov_reg_T0(ot, reg);
@@ -2398,6 +2454,8 @@ static void gen_push_T0(DisasContext *s)
             tcg_gen_mov_tl(cpu_T[1], cpu_A0);
             gen_op_addl_A0_seg(R_SS);
         }
+        if (seg_checks)
+            gen_check_segmented_access(R_SS, ACC_WRITE | (OT_WORD + s->dflag));
         gen_op_st_T0_A0(s->dflag + 1 + s->mem_index);
         if (s->ss32 && !s->addseg)
             gen_op_mov_reg_A0(1, R_ESP);
@@ -2437,6 +2495,8 @@ static void gen_push_T1(DisasContext *s)
             gen_op_andl_A0_ffff();
             gen_op_addl_A0_seg(R_SS);
         }
+        if (seg_checks)
+            gen_check_segmented_access(R_SS, ACC_WRITE | (OT_WORD + s->dflag));
         gen_op_st_T1_A0(s->dflag + 1 + s->mem_index);
 
         if (s->ss32 && !s->addseg)
@@ -2464,6 +2524,8 @@ static void gen_pop_T0(DisasContext *s)
             gen_op_andl_A0_ffff();
             gen_op_addl_A0_seg(R_SS);
         }
+        if (seg_checks)
+            gen_check_segmented_access(R_SS, ACC_READ | (OT_WORD + s->dflag));
         gen_op_ld_T0_A0(s->dflag + 1 + s->mem_index);
     }
 }
@@ -2501,6 +2563,8 @@ static void gen_pusha(DisasContext *s)
     tcg_gen_mov_tl(cpu_T[1], cpu_A0);
     if (s->addseg)
         gen_op_addl_A0_seg(R_SS);
+    if (seg_checks)
+        gen_check_segmented_access(R_SS, ACC_WRITE | (OT_WORD + s->dflag + 3));
     for(i = 0;i < 8; i++) {
         gen_op_mov_TN_reg(OT_LONG, 0, 7 - i);
         gen_op_st_T0_A0(OT_WORD + s->dflag + s->mem_index);
@@ -2520,6 +2584,8 @@ static void gen_popa(DisasContext *s)
     tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 16 <<  s->dflag);
     if (s->addseg)
         gen_op_addl_A0_seg(R_SS);
+    if (seg_checks)
+        gen_check_segmented_access(R_SS, ACC_READ | (OT_WORD + s->dflag + 3));
     for(i = 0;i < 8; i++) {
         /* ESP is not reloaded */
         if (i != 3) {
@@ -2571,6 +2637,8 @@ static void gen_enter(DisasContext *s, i
         tcg_gen_mov_tl(cpu_T[1], cpu_A0);
         if (s->addseg)
             gen_op_addl_A0_seg(R_SS);
+        if (seg_checks)
+            gen_check_segmented_access(R_SS, ACC_WRITE | ot);
         /* push bp */
         gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
         gen_op_st_T0_A0(ot + s->mem_index);
@@ -2924,7 +2992,7 @@ static void *sse_op_table5[256] = {
 static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r)
 {
     int b1, op1_offset, op2_offset, is_xmm, val, ot;
-    int modrm, mod, rm, reg, reg_addr, offset_addr;
+    int seg_reg, modrm, mod, rm, reg, reg_addr, offset_addr;
     void *sse_op2;
 
     b &= 0xff;
@@ -2990,7 +3058,9 @@ static void gen_sse(DisasContext *s, int
         case 0x0e7: /* movntq */
             if (mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
             gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             break;
         case 0x1e7: /* movntdq */
@@ -2999,7 +3069,9 @@ static void gen_sse(DisasContext *s, int
         case 0x3f0: /* lddqu */
             if (mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | (OT_QUAD + 1));
             gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             break;
         case 0x6e: /* movd mm, ea */
@@ -3035,7 +3107,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x6f: /* movq mm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             } else {
                 rm = (modrm & 7);
@@ -3052,7 +3126,9 @@ static void gen_sse(DisasContext *s, int
         case 0x16f: /* movdqa xmm, ea */
         case 0x26f: /* movdqu xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | (OT_QUAD + 1));
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3062,7 +3138,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x210: /* movss xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_LONG);
                 gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
                 gen_op_movl_T0_0();
@@ -3077,7 +3155,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x310: /* movsd xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
                 gen_op_movl_T0_0();
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
@@ -3091,7 +3171,9 @@ static void gen_sse(DisasContext *s, int
         case 0x012: /* movlps */
         case 0x112: /* movlpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 /* movhlps */
@@ -3102,7 +3184,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x212: /* movsldup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | (OT_QUAD + 1));
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3118,7 +3202,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x312: /* movddup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3131,7 +3217,9 @@ static void gen_sse(DisasContext *s, int
         case 0x016: /* movhps */
         case 0x116: /* movhpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
             } else {
                 /* movlhps */
@@ -3142,7 +3230,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x216: /* movshdup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | (OT_QUAD + 1));
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3186,7 +3276,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x27e: /* movq xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3197,7 +3289,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x7f: /* movq ea, mm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             } else {
                 rm = (modrm & 7);
@@ -3212,7 +3306,9 @@ static void gen_sse(DisasContext *s, int
         case 0x17f: /* movdqa ea, xmm */
         case 0x27f: /* movdqu ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | (OT_QUAD + 1));
                 gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3222,7 +3318,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x211: /* movss ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_LONG);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
                 gen_op_st_T0_A0(OT_LONG + s->mem_index);
             } else {
@@ -3233,7 +3331,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x311: /* movsd ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3244,7 +3344,9 @@ static void gen_sse(DisasContext *s, int
         case 0x013: /* movlps */
         case 0x113: /* movlpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 goto illegal_op;
@@ -3253,7 +3355,9 @@ static void gen_sse(DisasContext *s, int
         case 0x017: /* movhps */
         case 0x117: /* movhpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
             } else {
                 goto illegal_op;
@@ -3313,7 +3417,9 @@ static void gen_sse(DisasContext *s, int
         case 0x12a: /* cvtpi2pd */
             tcg_gen_helper_0_0(helper_enter_mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 op2_offset = offsetof(CPUX86State,mmx_t0);
                 gen_ldq_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3353,7 +3459,9 @@ static void gen_sse(DisasContext *s, int
         case 0x12d: /* cvtpd2pi */
             tcg_gen_helper_0_0(helper_enter_mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | (OT_QUAD + 1));
                 op2_offset = offsetof(CPUX86State,xmm_t0);
                 gen_ldo_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3384,10 +3492,14 @@ static void gen_sse(DisasContext *s, int
         case 0x32d: /* cvtsd2si */
             ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
                 if ((b >> 8) & 1) {
+                    if (seg_checks && seg_reg >= 0)
+                        gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                     gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_Q(0)));
                 } else {
+                    if (seg_checks && seg_reg >= 0)
+                        gen_check_segmented_access(seg_reg, ACC_READ | OT_LONG);
                     gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                     tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
                 }
@@ -3443,7 +3555,9 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x1d6: /* movq ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3499,20 +3613,26 @@ static void gen_sse(DisasContext *s, int
         if (is_xmm) {
             op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
                 op2_offset = offsetof(CPUX86State,xmm_t0);
                 if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) ||
                                 b == 0xc2)) {
                     /* specific case for SSE single instructions */
                     if (b1 == 2) {
                         /* 32 bit access */
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
                     } else {
                         /* 64 bit access */
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                         gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0)));
                     }
                 } else {
+                    if (seg_checks && seg_reg >= 0)
+                        gen_check_segmented_access(seg_reg, ACC_READ | (OT_QUAD + 1));
                     gen_ldo_env_A0(s->mem_index, op2_offset);
                 }
             } else {
@@ -3522,7 +3642,9 @@ static void gen_sse(DisasContext *s, int
         } else {
             op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 op2_offset = offsetof(CPUX86State,mmx_t0);
                 gen_ldq_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3597,7 +3719,7 @@ static target_ulong disas_insn(DisasCont
 {
     int b, prefixes, aflag, dflag;
     int shift, ot;
-    int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val;
+    int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val, seg_reg;
     target_ulong next_eip, tval;
     int rex_w, rex_r;
 
@@ -3762,7 +3884,10 @@ static target_ulong disas_insn(DisasCont
                 mod = (modrm >> 6) & 3;
                 rm = (modrm & 7) | REX_B(s);
                 if (mod != 3) {
-                    gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    if (seg_checks && seg_reg >= 0)
+                        gen_check_segmented_access(seg_reg,
+                            ((op == OP_CMPL) ? ACC_READ : ACC_WRITE) | ot);
                     opreg = OR_TMP0;
                 } else if (op == OP_XORL && rm == reg) {
                 xor_zero:
@@ -3784,7 +3909,9 @@ static target_ulong disas_insn(DisasCont
                 reg = ((modrm >> 3) & 7) | rex_r;
                 rm = (modrm & 7) | REX_B(s);
                 if (mod != 3) {
-                    gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    if (seg_checks && seg_reg >= 0)
+                        gen_check_segmented_access(seg_reg, ACC_READ | ot);
                     gen_op_ld_T1_A0(ot + s->mem_index);
                 } else if (op == OP_XORL && rm == reg) {
                     goto xor_zero;
@@ -3826,7 +3953,10 @@ static target_ulong disas_insn(DisasCont
                     s->rip_offset = 1;
                 else
                     s->rip_offset = insn_const_size(ot);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg,
+                        ((op == OP_CMPL) ? ACC_READ : ACC_WRITE) | ot);
                 opreg = OR_TMP0;
             } else {
                 opreg = rm;
@@ -3872,7 +4002,10 @@ static target_ulong disas_insn(DisasCont
         if (mod != 3) {
             if (op == 0)
                 s->rip_offset = insn_const_size(ot);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg,
+                    ((op == 2 || op == 3) ? ACC_WRITE : ACC_READ) | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
             gen_op_mov_TN_reg(ot, 0, rm);
@@ -4118,7 +4251,10 @@ static target_ulong disas_insn(DisasCont
             }
         }
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg,
+                    ((op < 2) ? ACC_WRITE : ACC_READ) | ot);
             if (op >= 2 && op != 3 && op != 5)
                 gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
@@ -4357,7 +4493,9 @@ static target_ulong disas_insn(DisasCont
             gen_op_mov_reg_T1(ot, reg);
             gen_op_mov_reg_T0(ot, rm);
         } else {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             gen_op_mov_TN_reg(ot, 0, reg);
             gen_op_ld_T1_A0(ot + s->mem_index);
             gen_op_addl_T0_T1();
@@ -4389,7 +4527,9 @@ static target_ulong disas_insn(DisasCont
                 rm = (modrm & 7) | REX_B(s);
                 gen_op_mov_v_reg(ot, t0, rm);
             } else {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
                 tcg_gen_mov_tl(a0, cpu_A0);
                 gen_op_ld_v(ot + s->mem_index, t0, a0);
                 rm = 0; /* avoid warning */
@@ -4444,7 +4584,9 @@ static target_ulong disas_insn(DisasCont
             gen_jmp_im(pc_start - s->cs_base);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | OT_BYTE);
             tcg_gen_helper_0_1(helper_cmpxchg8b, cpu_A0);
         }
         s->cc_op = CC_OP_EFLAGS;
@@ -4614,7 +4756,9 @@ static target_ulong disas_insn(DisasCont
         mod = (modrm >> 6) & 3;
         if (mod != 3) {
             s->rip_offset = insn_const_size(ot);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
         }
         val = insn_get(s, ot);
         gen_op_movl_T0_im(val);
@@ -4703,7 +4847,9 @@ static target_ulong disas_insn(DisasCont
                 }
                 gen_op_mov_reg_T0(d_ot, reg);
             } else {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | ot);
                 if (b & 8) {
                     gen_op_lds_T0_A0(ot + s->mem_index);
                 } else {
@@ -4725,7 +4871,9 @@ static target_ulong disas_insn(DisasCont
         s->override = -1;
         val = s->addseg;
         s->addseg = 0;
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        if (seg_checks && seg_reg >= 0)
+            gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
         s->addseg = val;
         gen_op_mov_reg_A0(ot - OT_WORD, reg);
         break;
@@ -4756,11 +4904,15 @@ static target_ulong disas_insn(DisasCont
                 }
                 gen_op_movl_A0_im(offset_addr);
             }
-            gen_add_A0_ds_seg(s);
+            seg_reg = gen_add_A0_ds_seg(s);
             if ((b & 2) == 0) {
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | ot);
                 gen_op_ld_T0_A0(ot + s->mem_index);
                 gen_op_mov_reg_T0(ot, R_EAX);
             } else {
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
                 gen_op_mov_TN_reg(ot, 0, R_EAX);
                 gen_op_st_T0_A0(ot + s->mem_index);
             }
@@ -4785,7 +4937,9 @@ static target_ulong disas_insn(DisasCont
             else
                 tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
         }
-        gen_add_A0_ds_seg(s);
+        seg_reg = gen_add_A0_ds_seg(s);
+        if (seg_checks && seg_reg >= 0)
+            gen_check_segmented_access(seg_reg, ACC_READ | OT_BYTE);
         gen_op_ldu_T0_A0(OT_BYTE + s->mem_index);
         gen_op_mov_reg_T0(OT_BYTE, R_EAX);
         break;
@@ -4837,7 +4991,9 @@ static target_ulong disas_insn(DisasCont
             gen_op_mov_reg_T0(ot, rm);
             gen_op_mov_reg_T1(ot, reg);
         } else {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             gen_op_mov_TN_reg(ot, 0, reg);
             /* for xchg, lock is implicit */
             if (!(prefixes & PREFIX_LOCK))
@@ -4874,7 +5030,10 @@ static target_ulong disas_insn(DisasCont
         mod = (modrm >> 6) & 3;
         if (mod == 3)
             goto illegal_op;
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        if (seg_checks && seg_reg >= 0)
+            gen_check_segmented_access_size(seg_reg, ACC_READ,
+                                             (1 << ot) + 2);
         gen_op_ld_T1_A0(ot + s->mem_index);
         gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
         /* load the segment first to handle exceptions properly */
@@ -4909,7 +5068,9 @@ static target_ulong disas_insn(DisasCont
                 if (shift == 2) {
                     s->rip_offset = 1;
                 }
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
                 opreg = OR_TMP0;
             } else {
                 opreg = (modrm & 7) | REX_B(s);
@@ -4959,7 +5120,9 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         reg = ((modrm >> 3) & 7) | rex_r;
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             opreg = OR_TMP0;
         } else {
             opreg = rm;
@@ -4990,7 +5153,7 @@ static target_ulong disas_insn(DisasCont
         op = ((b & 7) << 3) | ((modrm >> 3) & 7);
         if (mod != 3) {
             /* memory op */
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
             switch(op) {
             case 0x00 ... 0x07: /* fxxxs */
             case 0x10 ... 0x17: /* fixxxl */
@@ -5002,22 +5165,34 @@ static target_ulong disas_insn(DisasCont
 
                     switch(op >> 4) {
                     case 0:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_flds_FT0, cpu_tmp2_i32);
                         break;
                     case 1:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_FT0, cpu_tmp2_i32);
                         break;
                     case 2:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_QUAD);
                         tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         tcg_gen_helper_0_1(helper_fldl_FT0, cpu_tmp1_i64);
                         break;
                     case 3:
                     default:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_WORD);
                         gen_op_lds_T0_A0(OT_WORD + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_FT0, cpu_tmp2_i32);
@@ -5041,22 +5216,34 @@ static target_ulong disas_insn(DisasCont
                 case 0:
                     switch(op >> 4) {
                     case 0:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_flds_ST0, cpu_tmp2_i32);
                         break;
                     case 1:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_ST0, cpu_tmp2_i32);
                         break;
                     case 2:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_QUAD);
                         tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         tcg_gen_helper_0_1(helper_fldl_ST0, cpu_tmp1_i64);
                         break;
                     case 3:
                     default:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_READ | OT_WORD);
                         gen_op_lds_T0_A0(OT_WORD + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_ST0, cpu_tmp2_i32);
@@ -5067,17 +5254,26 @@ static target_ulong disas_insn(DisasCont
                     /* XXX: the corresponding CPUID bit must be tested ! */
                     switch(op >> 4) {
                     case 1:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fisttl_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 2:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_QUAD);
                         tcg_gen_helper_1_0(helper_fisttll_ST0, cpu_tmp1_i64);
                         tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         break;
                     case 3:
                     default:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_WORD);
                         tcg_gen_helper_1_0(helper_fistt_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_WORD + s->mem_index);
@@ -5088,22 +5284,34 @@ static target_ulong disas_insn(DisasCont
                 default:
                     switch(op >> 4) {
                     case 0:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fsts_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 1:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fistl_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 2:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_QUAD);
                         tcg_gen_helper_1_0(helper_fstl_ST0, cpu_tmp1_i64);
                         tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         break;
                     case 3:
                     default:
+                        if (seg_checks && seg_reg >= 0)
+                            gen_check_segmented_access(seg_reg,
+                                                       ACC_WRITE | OT_WORD);
                         tcg_gen_helper_1_0(helper_fist_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_WORD + s->mem_index);
@@ -5115,6 +5323,9 @@ static target_ulong disas_insn(DisasCont
                 }
                 break;
             case 0x0c: /* fldenv mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_READ,
+                                                     (s->dflag) ? 28 : 14);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5122,11 +5333,16 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x0d: /* fldcw mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_WORD);
                 gen_op_ld_T0_A0(OT_WORD + s->mem_index);
                 tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                 tcg_gen_helper_0_1(helper_fldcw, cpu_tmp2_i32);
                 break;
             case 0x0e: /* fnstenv mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE,
+                                                     (s->dflag) ? 28 : 14);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5134,17 +5350,23 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x0f: /* fnstcw mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_WORD);
                 tcg_gen_helper_1_0(helper_fnstcw, cpu_tmp2_i32);
                 tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 break;
             case 0x1d: /* fldt mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_READ, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
                 tcg_gen_helper_0_1(helper_fldt_ST0, cpu_A0);
                 break;
             case 0x1f: /* fstpt mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5152,6 +5374,9 @@ static target_ulong disas_insn(DisasCont
                 tcg_gen_helper_0_0(helper_fpop);
                 break;
             case 0x2c: /* frstor mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE,
+                                                     (s->dflag) ? 108 : 94);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5159,6 +5384,9 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x2e: /* fnsave mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE,
+                                                     (s->dflag) ? 108 : 94);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5166,17 +5394,23 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x2f: /* fnstsw mem */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_WORD);
                 tcg_gen_helper_1_0(helper_fnstsw, cpu_tmp2_i32);
                 tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 break;
             case 0x3c: /* fbld */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_READ, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
                 tcg_gen_helper_0_1(helper_fbld_ST0, cpu_A0);
                 break;
             case 0x3e: /* fbstp */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5184,11 +5418,15 @@ static target_ulong disas_insn(DisasCont
                 tcg_gen_helper_0_0(helper_fpop);
                 break;
             case 0x3d: /* fildll */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_QUAD);
                 tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                   (s->mem_index >> 2) - 1);
                 tcg_gen_helper_0_1(helper_fildll_ST0, cpu_tmp1_i64);
                 break;
             case 0x3f: /* fistpll */
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_QUAD);
                 tcg_gen_helper_1_0(helper_fistll_ST0, cpu_tmp1_i64);
                 tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                   (s->mem_index >> 2) - 1);
@@ -5734,6 +5972,8 @@ static target_ulong disas_insn(DisasCont
                                tcg_const_i32(val));
         } else {
             gen_stack_A0(s);
+            if (seg_checks)
+                gen_check_segmented_access(R_SS, ACC_READ | (OT_LONG + s->dflag));
             /* pop offset */
             gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
             if (s->dflag == 0)
@@ -5870,7 +6110,9 @@ static target_ulong disas_insn(DisasCont
             mod = (modrm >> 6) & 3;
             t0 = tcg_temp_local_new(TCG_TYPE_TL);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
                 gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -6012,7 +6254,9 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         if (mod != 3) {
             s->rip_offset = 1;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
             gen_op_mov_TN_reg(ot, 0, rm);
@@ -6043,7 +6287,9 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         gen_op_mov_TN_reg(OT_LONG, 1, reg);
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
             /* specific case: we need to add a displacement */
             gen_exts(ot, cpu_T[1]);
             tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot);
@@ -6272,7 +6518,9 @@ static target_ulong disas_insn(DisasCont
         if (mod == 3)
             goto illegal_op;
         gen_op_mov_TN_reg(ot, 0, reg);
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        if (seg_checks && seg_reg >= 0)
+            gen_check_segmented_access(seg_reg, ACC_READ | (ot + 1));
         gen_jmp_im(pc_start - s->cs_base);
         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
         if (ot == OT_WORD)
@@ -6553,7 +6801,10 @@ static target_ulong disas_insn(DisasCont
             if (mod == 3)
                 goto illegal_op;
             gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access_size(seg_reg, ACC_WRITE,
+                                                 CODE64(s) ? 10 : 6);
             tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit));
             gen_op_st_T0_A0(OT_WORD + s->mem_index);
             gen_add_A0_im(s, 2);
@@ -6602,7 +6853,10 @@ static target_ulong disas_insn(DisasCont
                 }
             } else { /* sidt */
                 gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_WRITE,
+                                                     CODE64(s) ? 10 : 6);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit));
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 gen_add_A0_im(s, 2);
@@ -6708,7 +6962,10 @@ static target_ulong disas_insn(DisasCont
             } else {
                 gen_svm_check_intercept(s, pc_start,
                                         op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access_size(seg_reg, ACC_READ,
+                                                     CODE64(s) ? 10 : 6);
                 gen_op_ld_T1_A0(OT_WORD + s->mem_index);
                 gen_add_A0_im(s, 2);
                 gen_op_ld_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
@@ -6824,7 +7081,9 @@ static target_ulong disas_insn(DisasCont
             mod = (modrm >> 6) & 3;
             rm = modrm & 7;
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | ot);
                 gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
             } else {
                 gen_op_mov_v_reg(ot, t0, rm);
@@ -7013,7 +7272,9 @@ static target_ulong disas_insn(DisasCont
                 gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
                 break;
             }
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access_size(seg_reg, ACC_WRITE, 512);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
             gen_jmp_im(pc_start - s->cs_base);
@@ -7028,7 +7289,9 @@ static target_ulong disas_insn(DisasCont
                 gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
                 break;
             }
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            if (seg_checks && seg_reg >= 0)
+                gen_check_segmented_access_size(seg_reg, ACC_READ, 512);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
             gen_jmp_im(pc_start - s->cs_base);
@@ -7044,11 +7307,15 @@ static target_ulong disas_insn(DisasCont
             if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) ||
                 mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
             if (op == 2) {
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_READ | OT_LONG);
                 gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
             } else {
+                if (seg_checks && seg_reg >= 0)
+                    gen_check_segmented_access(seg_reg, ACC_WRITE | OT_LONG);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
                 gen_op_st_T0_A0(OT_LONG + s->mem_index);
             }
Index: b/vl.c
===================================================================
--- a/vl.c
+++ b/vl.c
@@ -198,6 +198,7 @@ CharDriverState *serial_hds[MAX_SERIAL_P
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 #ifdef TARGET_I386
 int win2k_install_hack = 0;
+int seg_checks = 0;
 #endif
 int usb_enabled = 0;
 static VLANState *first_vlan;
@@ -7434,6 +7435,7 @@ static void help(int exitcode)
            "-std-vga        simulate a standard VGA card with VESA Bochs Extensions\n"
            "                (default is CL-GD5446 PCI VGA)\n"
            "-no-acpi        disable ACPI\n"
+           "-seg-checks     enable runtime segment access checks\n"
 #endif
 #ifdef CONFIG_CURSES
            "-curses         use a curses/ncurses interface instead of SDL\n"
@@ -7495,6 +7497,7 @@ enum {
     QEMU_OPTION_snapshot,
 #ifdef TARGET_I386
     QEMU_OPTION_no_fd_bootchk,
+    QEMU_OPTION_seg_checks,
 #endif
     QEMU_OPTION_m,
     QEMU_OPTION_nographic,
@@ -7588,6 +7591,7 @@ const QEMUOption qemu_options[] = {
     { "snapshot", 0, QEMU_OPTION_snapshot },
 #ifdef TARGET_I386
     { "no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk },
+    { "seg-checks", 0, QEMU_OPTION_seg_checks },
 #endif
     { "m", HAS_ARG, QEMU_OPTION_m },
     { "nographic", 0, QEMU_OPTION_nographic },
@@ -8383,6 +8387,9 @@ int main(int argc, char **argv)
             case QEMU_OPTION_kernel_kqemu:
                 kqemu_allowed = 2;
                 break;
+            case QEMU_OPTION_seg_checks:
+                seg_checks = 1;
+                break;
 #endif
             case QEMU_OPTION_usb:
                 usb_enabled = 1;
Index: b/target-i386/helper.h
===================================================================
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -218,4 +218,9 @@ DEF_HELPER(target_ulong, helper_rclq, (t
 DEF_HELPER(target_ulong, helper_rcrq, (target_ulong t0, target_ulong t1))
 #endif
 
+DEF_HELPER(void, helper_check_segmented_access, (target_ulong a0,
+                         int seg_reg, int type))
+DEF_HELPER(void, helper_check_segmented_access_size, (target_ulong a0,
+                         int seg_reg, int type, int size))
+
 #undef DEF_HELPER
Index: b/target-i386/op_helper.c
===================================================================
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -2231,6 +2231,65 @@ void helper_load_seg(int seg_reg, int se
     }
 }
 
+static void log_seg_violation(target_ulong a0, int seg_reg, int type, int size)
+{
+    static const char *seg_name[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+    fprintf(logfile, "segment violation: %s %d byte via %s from %x:"
+                     TARGET_FMT_lx ", base " TARGET_FMT_lx ", limit %x, "
+                     "flags %x\n",
+            (type & ACC_WRITE) ? "write" : "read", size, seg_name[seg_reg],
+            env->segs[seg_reg].selector, a0 - env->segs[seg_reg].base,
+            env->segs[seg_reg].base, env->segs[seg_reg].limit,
+            env->segs[seg_reg].flags);
+}
+
+void helper_check_segmented_access_size(target_ulong a0, int seg_reg,
+                                        int type, int size)
+{
+    int seg_type = env->segs[seg_reg].flags & 0x1F00;
+    target_long addr = a0 - env->segs[seg_reg].base;
+
+    if (!(env->cr[0] & CR0_PE_MASK) || (env->hflags & HF_CS64_MASK))
+        return;
+
+    if (!(seg_type & 0x1000)) {
+        if (loglevel & CPU_LOG_INT)
+            log_seg_violation(a0, seg_reg, type, size);
+        raise_exception(EXCP0D_GPF);
+    }
+
+    if (type & ACC_WRITE) {
+        if (seg_type & 0x0800 || !(seg_type & 0x0200)) {
+            if (loglevel & CPU_LOG_INT)
+                log_seg_violation(a0, seg_reg, type, size);
+            raise_exception(EXCP0D_GPF);
+        }
+    } else {
+        if (seg_type & 0x0800 && !(seg_type & 0x0200)) {
+            if (loglevel & CPU_LOG_INT)
+                log_seg_violation(a0, seg_reg, type, size);
+            raise_exception(EXCP0D_GPF);
+        }
+    }
+
+    if (((seg_type & 0x0C00) == 0x0400
+            && (addr > (((env->segs[seg_reg].flags & 0x400000) ?
+                         0xFFFFFFFF : 0xFFFF) - size)
+            || addr <= env->segs[seg_reg].limit))
+            || addr > env->segs[seg_reg].limit - size) {
+        if (loglevel & CPU_LOG_INT)
+            log_seg_violation(a0, seg_reg, type, size);
+        raise_exception(EXCP0D_GPF);
+    }
+}
+
+void helper_check_segmented_access(target_ulong a0, int seg_reg, int type)
+{
+    helper_check_segmented_access_size(a0, seg_reg, type,
+                                       (1 << (type & 0xFF)) - 1);
+}
+
 /* protected mode jump */
 void helper_ljmp_protected(int new_cs, target_ulong new_eip,
                            int next_eip_addend)

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-09 12:12 [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks Jan Kiszka
@ 2008-07-14 10:34 ` Jan Kiszka
  2008-07-14 10:55   ` Jamie Lokier
  2008-07-14 11:05   ` [Qemu-devel] " Daniel P. Berrange
  0 siblings, 2 replies; 13+ messages in thread
From: Jan Kiszka @ 2008-07-14 10:34 UTC (permalink / raw)
  To: qemu-devel

This is the second version of my segment type and register check. It
reduces the impact on the translator code significantly, and it also
fixes a bug of the "size" helper variant in the previous version.

The idea of this patch is to generate calls to a check helper only in
case the user requested this support via "-seg-checks". This feature
remains off by default as most x86 OSes do not care about protection via
segmentation anymore (and it was even removed from 64-bit modes by the
CPU vendors). Moreover, checking the segment type and limit on every
memory access is nothing that makes QEMU faster, so you will only want
this if you are looking for very accurate emulation.

On Fabrice's request I tried to find the conditions which allow enabling
-seg-checks by default but kicking it out most of the time during code
translation. That works for 64-bit mode, of course, but I still see no
clear indication for the case that 32-bit guests are not interested in
type checking specifically. If you see one, let me know.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 target-i386/cpu.h       |    8 +
 target-i386/helper.h    |    3 
 target-i386/op_helper.c |   50 ++++++++
 target-i386/translate.c |  299 ++++++++++++++++++++++++++++++++++++------------
 vl.c                    |    7 +
 5 files changed, 296 insertions(+), 71 deletions(-)

Index: b/target-i386/cpu.h
===================================================================
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -94,6 +94,10 @@
 
 #define DESC_TSS_BUSY_MASK (1 << 9)
 
+/* flags for segment access checks */
+#define ACC_READ        0x0000
+#define ACC_WRITE       0x0100
+
 /* eflags masks */
 #define CC_C   	0x0001
 #define CC_P 	0x0004
@@ -751,6 +755,10 @@ static inline void cpu_clone_regs(CPUSta
         env->regs[R_ESP] = newsp;
     env->regs[R_EAX] = 0;
 }
+
+#define seg_checks 0
+#else
+extern int seg_checks;
 #endif
 
 #define CPU_PC_FROM_TB(env, tb) env->eip = tb->pc - tb->cs_base
Index: b/target-i386/translate.c
===================================================================
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -591,7 +591,7 @@ static inline void gen_jmp_im(target_ulo
     tcg_gen_st_tl(cpu_tmp0, cpu_env, offsetof(CPUState, eip));
 }
 
-static inline void gen_string_movl_A0_ESI(DisasContext *s)
+static inline int gen_string_movl_A0_ESI(DisasContext *s)
 {
     int override;
 
@@ -604,6 +604,7 @@ static inline void gen_string_movl_A0_ES
         } else {
             gen_op_movq_A0_reg(R_ESI);
         }
+        return -1;
     } else
 #endif
     if (s->aflag) {
@@ -615,6 +616,7 @@ static inline void gen_string_movl_A0_ES
             gen_op_addl_A0_reg_sN(0, R_ESI);
         } else {
             gen_op_movl_A0_reg(R_ESI);
+            return R_DS;
         }
     } else {
         /* 16 address, always override */
@@ -624,13 +626,15 @@ static inline void gen_string_movl_A0_ES
         gen_op_andl_A0_ffff();
         gen_op_addl_A0_seg(override);
     }
+    return override;
 }
 
-static inline void gen_string_movl_A0_EDI(DisasContext *s)
+static inline int gen_string_movl_A0_EDI(DisasContext *s)
 {
 #ifdef TARGET_X86_64
     if (s->aflag == 2) {
         gen_op_movq_A0_reg(R_EDI);
+        return -1;
     } else
 #endif
     if (s->aflag) {
@@ -645,6 +649,7 @@ static inline void gen_string_movl_A0_ED
         gen_op_andl_A0_ffff();
         gen_op_addl_A0_seg(R_ES);
     }
+    return R_ES;
 }
 
 static inline void gen_op_movl_T0_Dshift(int ot) 
@@ -752,11 +757,38 @@ static void gen_check_io(DisasContext *s
     }
 }
 
+static void gen_check_segmented_access(DisasContext *s, int seg_reg, int type,
+                                       int size)
+{
+    if (!s->pe || CODE64(s))
+        return;
+    tcg_gen_helper_0_4(helper_check_segmented_access, cpu_A0,
+                       tcg_const_i32(seg_reg), tcg_const_i32(type),
+                       tcg_const_i32(size));
+}
+
+static inline void gen_check_seg_access(DisasContext *s, int seg_reg, int type)
+{
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(s, seg_reg, type, 1 << (type & 0xFF));
+}
+
+static inline void gen_check_seg_access_size(DisasContext *s, int seg_reg,
+                                             int type, int size)
+{
+    if (seg_checks && seg_reg >= 0)
+        gen_check_segmented_access(s, seg_reg, type, size);
+}
+
 static inline void gen_movs(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
     gen_op_st_T0_A0(ot + s->mem_index);
     gen_op_movl_T0_Dshift(ot);
     gen_op_add_reg_T0(s->aflag, R_ESI);
@@ -1165,8 +1197,11 @@ static int gen_jz_ecx_string(DisasContex
 
 static inline void gen_stos(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
     gen_op_st_T0_A0(ot + s->mem_index);
     gen_op_movl_T0_Dshift(ot);
     gen_op_add_reg_T0(s->aflag, R_EDI);
@@ -1174,7 +1209,10 @@ static inline void gen_stos(DisasContext
 
 static inline void gen_lods(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
     gen_op_mov_reg_T0(ot, R_EAX);
     gen_op_movl_T0_Dshift(ot);
@@ -1183,8 +1221,11 @@ static inline void gen_lods(DisasContext
 
 static inline void gen_scas(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T1_A0(ot + s->mem_index);
     gen_op_cmpl_T0_T1_cc();
     gen_op_movl_T0_Dshift(ot);
@@ -1193,9 +1234,13 @@ static inline void gen_scas(DisasContext
 
 static inline void gen_cmps(DisasContext *s, int ot)
 {
-    gen_string_movl_A0_ESI(s);
+    int seg_reg;
+
+    seg_reg = gen_string_movl_A0_ESI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T1_A0(ot + s->mem_index);
     gen_op_cmpl_T0_T1_cc();
     gen_op_movl_T0_Dshift(ot);
@@ -1205,9 +1250,12 @@ static inline void gen_cmps(DisasContext
 
 static inline void gen_ins(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     if (use_icount)
         gen_io_start();
-    gen_string_movl_A0_EDI(s);
+    seg_reg = gen_string_movl_A0_EDI(s);
+    gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
     /* Note: we must do this dummy write first to be restartable in
        case of page fault. */
     gen_op_movl_T0_0();
@@ -1225,9 +1273,12 @@ static inline void gen_ins(DisasContext
 
 static inline void gen_outs(DisasContext *s, int ot)
 {
+    int seg_reg;
+
     if (use_icount)
         gen_io_start();
-    gen_string_movl_A0_ESI(s);
+    seg_reg = gen_string_movl_A0_ESI(s);
+    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
     gen_op_ld_T0_A0(ot + s->mem_index);
 
     gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
@@ -1882,7 +1933,7 @@ static void gen_shifti(DisasContext *s1,
     }
 }
 
-static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
+static int gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
 {
     target_long disp;
     int havesib;
@@ -2064,6 +2115,7 @@ static void gen_lea_modrm(DisasContext *
     disp = 0;
     *reg_ptr = opreg;
     *offset_ptr = disp;
+    return override;
 }
 
 static void gen_nop_modrm(DisasContext *s, int modrm)
@@ -2117,11 +2169,10 @@ static void gen_nop_modrm(DisasContext *
 }
 
 /* used for LEA and MOV AX, mem */
-static void gen_add_A0_ds_seg(DisasContext *s)
+static int gen_add_A0_ds_seg(DisasContext *s)
 {
     int override, must_add_seg;
     must_add_seg = s->addseg;
-    override = R_DS;
     if (s->override >= 0) {
         override = s->override;
         must_add_seg = 1;
@@ -2138,13 +2189,14 @@ static void gen_add_A0_ds_seg(DisasConte
             gen_op_addl_A0_seg(override);
         }
     }
+    return override;
 }
 
 /* generate modrm memory load or store of 'reg'. TMP0 is used if reg !=
    OR_TMP0 */
 static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store)
 {
-    int mod, rm, opreg, disp;
+    int mod, rm, opreg, disp, seg_reg;
 
     mod = (modrm >> 6) & 3;
     rm = (modrm & 7) | REX_B(s);
@@ -2159,12 +2211,14 @@ static void gen_ldst_modrm(DisasContext
                 gen_op_mov_reg_T0(ot, reg);
         }
     } else {
-        gen_lea_modrm(s, modrm, &opreg, &disp);
+        seg_reg = gen_lea_modrm(s, modrm, &opreg, &disp);
         if (is_store) {
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             if (reg != OR_TMP0)
                 gen_op_mov_TN_reg(ot, 0, reg);
             gen_op_st_T0_A0(ot + s->mem_index);
         } else {
+            gen_check_seg_access(s, seg_reg, ACC_READ | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
             if (reg != OR_TMP0)
                 gen_op_mov_reg_T0(ot, reg);
@@ -2398,6 +2452,7 @@ static void gen_push_T0(DisasContext *s)
             tcg_gen_mov_tl(cpu_T[1], cpu_A0);
             gen_op_addl_A0_seg(R_SS);
         }
+        gen_check_seg_access(s, R_SS, ACC_WRITE | (OT_WORD + s->dflag));
         gen_op_st_T0_A0(s->dflag + 1 + s->mem_index);
         if (s->ss32 && !s->addseg)
             gen_op_mov_reg_A0(1, R_ESP);
@@ -2437,6 +2492,7 @@ static void gen_push_T1(DisasContext *s)
             gen_op_andl_A0_ffff();
             gen_op_addl_A0_seg(R_SS);
         }
+        gen_check_seg_access(s, R_SS, ACC_WRITE | (OT_WORD + s->dflag));
         gen_op_st_T1_A0(s->dflag + 1 + s->mem_index);
 
         if (s->ss32 && !s->addseg)
@@ -2464,6 +2520,7 @@ static void gen_pop_T0(DisasContext *s)
             gen_op_andl_A0_ffff();
             gen_op_addl_A0_seg(R_SS);
         }
+        gen_check_seg_access(s, R_SS, ACC_READ | (OT_WORD + s->dflag));
         gen_op_ld_T0_A0(s->dflag + 1 + s->mem_index);
     }
 }
@@ -2501,6 +2558,7 @@ static void gen_pusha(DisasContext *s)
     tcg_gen_mov_tl(cpu_T[1], cpu_A0);
     if (s->addseg)
         gen_op_addl_A0_seg(R_SS);
+    gen_check_seg_access(s, R_SS, ACC_WRITE | (OT_WORD + s->dflag + 3));
     for(i = 0;i < 8; i++) {
         gen_op_mov_TN_reg(OT_LONG, 0, 7 - i);
         gen_op_st_T0_A0(OT_WORD + s->dflag + s->mem_index);
@@ -2520,6 +2578,7 @@ static void gen_popa(DisasContext *s)
     tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 16 <<  s->dflag);
     if (s->addseg)
         gen_op_addl_A0_seg(R_SS);
+    gen_check_seg_access(s, R_SS, ACC_READ | (OT_WORD + s->dflag + 3));
     for(i = 0;i < 8; i++) {
         /* ESP is not reloaded */
         if (i != 3) {
@@ -2571,6 +2630,7 @@ static void gen_enter(DisasContext *s, i
         tcg_gen_mov_tl(cpu_T[1], cpu_A0);
         if (s->addseg)
             gen_op_addl_A0_seg(R_SS);
+        gen_check_seg_access(s, R_SS, ACC_WRITE | ot);
         /* push bp */
         gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
         gen_op_st_T0_A0(ot + s->mem_index);
@@ -2924,7 +2984,7 @@ static void *sse_op_table5[256] = {
 static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r)
 {
     int b1, op1_offset, op2_offset, is_xmm, val, ot;
-    int modrm, mod, rm, reg, reg_addr, offset_addr;
+    int seg_reg, modrm, mod, rm, reg, reg_addr, offset_addr;
     void *sse_op2;
 
     b &= 0xff;
@@ -2990,7 +3050,8 @@ static void gen_sse(DisasContext *s, int
         case 0x0e7: /* movntq */
             if (mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
             gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             break;
         case 0x1e7: /* movntdq */
@@ -2999,7 +3060,8 @@ static void gen_sse(DisasContext *s, int
         case 0x3f0: /* lddqu */
             if (mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access_size(s, seg_reg, ACC_WRITE, 16);
             gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             break;
         case 0x6e: /* movd mm, ea */
@@ -3035,7 +3097,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x6f: /* movq mm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             } else {
                 rm = (modrm & 7);
@@ -3052,7 +3115,8 @@ static void gen_sse(DisasContext *s, int
         case 0x16f: /* movdqa xmm, ea */
         case 0x26f: /* movdqu xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 16);
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3062,7 +3126,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x210: /* movss xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                 gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
                 gen_op_movl_T0_0();
@@ -3077,7 +3142,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x310: /* movsd xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
                 gen_op_movl_T0_0();
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
@@ -3091,7 +3157,8 @@ static void gen_sse(DisasContext *s, int
         case 0x012: /* movlps */
         case 0x112: /* movlpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 /* movhlps */
@@ -3102,7 +3169,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x212: /* movsldup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 16);
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3118,7 +3186,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x312: /* movddup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3131,7 +3200,8 @@ static void gen_sse(DisasContext *s, int
         case 0x016: /* movhps */
         case 0x116: /* movhpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
             } else {
                 /* movlhps */
@@ -3142,7 +3212,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x216: /* movshdup */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 16);
                 gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3186,7 +3257,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x27e: /* movq xmm, ea */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3197,7 +3269,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x7f: /* movq ea, mm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
             } else {
                 rm = (modrm & 7);
@@ -3212,7 +3285,8 @@ static void gen_sse(DisasContext *s, int
         case 0x17f: /* movdqa ea, xmm */
         case 0x27f: /* movdqu ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE, 16);
                 gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3222,7 +3296,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x211: /* movss ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_LONG);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
                 gen_op_st_T0_A0(OT_LONG + s->mem_index);
             } else {
@@ -3233,7 +3308,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x311: /* movsd ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3244,7 +3320,8 @@ static void gen_sse(DisasContext *s, int
         case 0x013: /* movlps */
         case 0x113: /* movlpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 goto illegal_op;
@@ -3253,7 +3330,8 @@ static void gen_sse(DisasContext *s, int
         case 0x017: /* movhps */
         case 0x117: /* movhpd */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
             } else {
                 goto illegal_op;
@@ -3313,7 +3391,8 @@ static void gen_sse(DisasContext *s, int
         case 0x12a: /* cvtpi2pd */
             tcg_gen_helper_0_0(helper_enter_mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 op2_offset = offsetof(CPUX86State,mmx_t0);
                 gen_ldq_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3353,7 +3432,8 @@ static void gen_sse(DisasContext *s, int
         case 0x12d: /* cvtpd2pi */
             tcg_gen_helper_0_0(helper_enter_mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 16);
                 op2_offset = offsetof(CPUX86State,xmm_t0);
                 gen_ldo_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3384,10 +3464,12 @@ static void gen_sse(DisasContext *s, int
         case 0x32d: /* cvtsd2si */
             ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
                 if ((b >> 8) & 1) {
+                    gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                     gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_Q(0)));
                 } else {
+                    gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                     gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                     tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
                 }
@@ -3443,7 +3525,8 @@ static void gen_sse(DisasContext *s, int
             break;
         case 0x1d6: /* movq ea, xmm */
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -3499,20 +3582,23 @@ static void gen_sse(DisasContext *s, int
         if (is_xmm) {
             op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
                 op2_offset = offsetof(CPUX86State,xmm_t0);
                 if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) ||
                                 b == 0xc2)) {
                     /* specific case for SSE single instructions */
                     if (b1 == 2) {
                         /* 32 bit access */
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
                     } else {
                         /* 64 bit access */
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                         gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0)));
                     }
                 } else {
+                    gen_check_seg_access_size(s, seg_reg, ACC_READ, 16);
                     gen_ldo_env_A0(s->mem_index, op2_offset);
                 }
             } else {
@@ -3522,7 +3608,8 @@ static void gen_sse(DisasContext *s, int
         } else {
             op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 op2_offset = offsetof(CPUX86State,mmx_t0);
                 gen_ldq_env_A0(s->mem_index, op2_offset);
             } else {
@@ -3597,7 +3684,7 @@ static target_ulong disas_insn(DisasCont
 {
     int b, prefixes, aflag, dflag;
     int shift, ot;
-    int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val;
+    int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val, seg_reg;
     target_ulong next_eip, tval;
     int rex_w, rex_r;
 
@@ -3762,7 +3849,9 @@ static target_ulong disas_insn(DisasCont
                 mod = (modrm >> 6) & 3;
                 rm = (modrm & 7) | REX_B(s);
                 if (mod != 3) {
-                    gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    gen_check_seg_access(s, seg_reg,
+                            ((op == OP_CMPL) ? ACC_READ : ACC_WRITE) | ot);
                     opreg = OR_TMP0;
                 } else if (op == OP_XORL && rm == reg) {
                 xor_zero:
@@ -3784,7 +3873,8 @@ static target_ulong disas_insn(DisasCont
                 reg = ((modrm >> 3) & 7) | rex_r;
                 rm = (modrm & 7) | REX_B(s);
                 if (mod != 3) {
-                    gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                    gen_check_seg_access(s, seg_reg, ACC_READ | ot);
                     gen_op_ld_T1_A0(ot + s->mem_index);
                 } else if (op == OP_XORL && rm == reg) {
                     goto xor_zero;
@@ -3826,7 +3916,9 @@ static target_ulong disas_insn(DisasCont
                     s->rip_offset = 1;
                 else
                     s->rip_offset = insn_const_size(ot);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg,
+                        ((op == OP_CMPL) ? ACC_READ : ACC_WRITE) | ot);
                 opreg = OR_TMP0;
             } else {
                 opreg = rm;
@@ -3872,7 +3964,9 @@ static target_ulong disas_insn(DisasCont
         if (mod != 3) {
             if (op == 0)
                 s->rip_offset = insn_const_size(ot);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg,
+                    ((op == 2 || op == 3) ? ACC_WRITE : ACC_READ) | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
             gen_op_mov_TN_reg(ot, 0, rm);
@@ -4118,7 +4212,9 @@ static target_ulong disas_insn(DisasCont
             }
         }
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg,
+                    ((op < 2) ? ACC_WRITE : ACC_READ) | ot);
             if (op >= 2 && op != 3 && op != 5)
                 gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
@@ -4357,7 +4453,8 @@ static target_ulong disas_insn(DisasCont
             gen_op_mov_reg_T1(ot, reg);
             gen_op_mov_reg_T0(ot, rm);
         } else {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             gen_op_mov_TN_reg(ot, 0, reg);
             gen_op_ld_T1_A0(ot + s->mem_index);
             gen_op_addl_T0_T1();
@@ -4389,7 +4486,8 @@ static target_ulong disas_insn(DisasCont
                 rm = (modrm & 7) | REX_B(s);
                 gen_op_mov_v_reg(ot, t0, rm);
             } else {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
                 tcg_gen_mov_tl(a0, cpu_A0);
                 gen_op_ld_v(ot + s->mem_index, t0, a0);
                 rm = 0; /* avoid warning */
@@ -4444,7 +4542,8 @@ static target_ulong disas_insn(DisasCont
             gen_jmp_im(pc_start - s->cs_base);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_BYTE);
             tcg_gen_helper_0_1(helper_cmpxchg8b, cpu_A0);
         }
         s->cc_op = CC_OP_EFLAGS;
@@ -4614,7 +4713,8 @@ static target_ulong disas_insn(DisasCont
         mod = (modrm >> 6) & 3;
         if (mod != 3) {
             s->rip_offset = insn_const_size(ot);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
         }
         val = insn_get(s, ot);
         gen_op_movl_T0_im(val);
@@ -4703,7 +4803,8 @@ static target_ulong disas_insn(DisasCont
                 }
                 gen_op_mov_reg_T0(d_ot, reg);
             } else {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_READ | ot);
                 if (b & 8) {
                     gen_op_lds_T0_A0(ot + s->mem_index);
                 } else {
@@ -4725,7 +4826,8 @@ static target_ulong disas_insn(DisasCont
         s->override = -1;
         val = s->addseg;
         s->addseg = 0;
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
         s->addseg = val;
         gen_op_mov_reg_A0(ot - OT_WORD, reg);
         break;
@@ -4756,11 +4858,13 @@ static target_ulong disas_insn(DisasCont
                 }
                 gen_op_movl_A0_im(offset_addr);
             }
-            gen_add_A0_ds_seg(s);
+            seg_reg = gen_add_A0_ds_seg(s);
             if ((b & 2) == 0) {
+                gen_check_seg_access(s, seg_reg, ACC_READ | ot);
                 gen_op_ld_T0_A0(ot + s->mem_index);
                 gen_op_mov_reg_T0(ot, R_EAX);
             } else {
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
                 gen_op_mov_TN_reg(ot, 0, R_EAX);
                 gen_op_st_T0_A0(ot + s->mem_index);
             }
@@ -4785,7 +4889,8 @@ static target_ulong disas_insn(DisasCont
             else
                 tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
         }
-        gen_add_A0_ds_seg(s);
+        seg_reg = gen_add_A0_ds_seg(s);
+        gen_check_seg_access(s, seg_reg, ACC_READ | OT_BYTE);
         gen_op_ldu_T0_A0(OT_BYTE + s->mem_index);
         gen_op_mov_reg_T0(OT_BYTE, R_EAX);
         break;
@@ -4837,7 +4942,8 @@ static target_ulong disas_insn(DisasCont
             gen_op_mov_reg_T0(ot, rm);
             gen_op_mov_reg_T1(ot, reg);
         } else {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             gen_op_mov_TN_reg(ot, 0, reg);
             /* for xchg, lock is implicit */
             if (!(prefixes & PREFIX_LOCK))
@@ -4874,7 +4980,8 @@ static target_ulong disas_insn(DisasCont
         mod = (modrm >> 6) & 3;
         if (mod == 3)
             goto illegal_op;
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        gen_check_seg_access_size(s, seg_reg, ACC_READ, (1 << ot) + 2);
         gen_op_ld_T1_A0(ot + s->mem_index);
         gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
         /* load the segment first to handle exceptions properly */
@@ -4909,7 +5016,8 @@ static target_ulong disas_insn(DisasCont
                 if (shift == 2) {
                     s->rip_offset = 1;
                 }
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
                 opreg = OR_TMP0;
             } else {
                 opreg = (modrm & 7) | REX_B(s);
@@ -4959,7 +5067,8 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         reg = ((modrm >> 3) & 7) | rex_r;
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             opreg = OR_TMP0;
         } else {
             opreg = rm;
@@ -4990,7 +5099,7 @@ static target_ulong disas_insn(DisasCont
         op = ((b & 7) << 3) | ((modrm >> 3) & 7);
         if (mod != 3) {
             /* memory op */
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
             switch(op) {
             case 0x00 ... 0x07: /* fxxxs */
             case 0x10 ... 0x17: /* fixxxl */
@@ -5002,22 +5111,26 @@ static target_ulong disas_insn(DisasCont
 
                     switch(op >> 4) {
                     case 0:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_flds_FT0, cpu_tmp2_i32);
                         break;
                     case 1:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_FT0, cpu_tmp2_i32);
                         break;
                     case 2:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                         tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         tcg_gen_helper_0_1(helper_fldl_FT0, cpu_tmp1_i64);
                         break;
                     case 3:
                     default:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_WORD);
                         gen_op_lds_T0_A0(OT_WORD + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_FT0, cpu_tmp2_i32);
@@ -5041,22 +5154,26 @@ static target_ulong disas_insn(DisasCont
                 case 0:
                     switch(op >> 4) {
                     case 0:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_flds_ST0, cpu_tmp2_i32);
                         break;
                     case 1:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                         gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_ST0, cpu_tmp2_i32);
                         break;
                     case 2:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                         tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         tcg_gen_helper_0_1(helper_fldl_ST0, cpu_tmp1_i64);
                         break;
                     case 3:
                     default:
+                        gen_check_seg_access(s, seg_reg, ACC_READ | OT_WORD);
                         gen_op_lds_T0_A0(OT_WORD + s->mem_index);
                         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                         tcg_gen_helper_0_1(helper_fildl_ST0, cpu_tmp2_i32);
@@ -5067,17 +5184,20 @@ static target_ulong disas_insn(DisasCont
                     /* XXX: the corresponding CPUID bit must be tested ! */
                     switch(op >> 4) {
                     case 1:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fisttl_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 2:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                         tcg_gen_helper_1_0(helper_fisttll_ST0, cpu_tmp1_i64);
                         tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         break;
                     case 3:
                     default:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_WORD);
                         tcg_gen_helper_1_0(helper_fistt_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_WORD + s->mem_index);
@@ -5088,22 +5208,26 @@ static target_ulong disas_insn(DisasCont
                 default:
                     switch(op >> 4) {
                     case 0:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fsts_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 1:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_LONG);
                         tcg_gen_helper_1_0(helper_fistl_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_LONG + s->mem_index);
                         break;
                     case 2:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                         tcg_gen_helper_1_0(helper_fstl_ST0, cpu_tmp1_i64);
                         tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                           (s->mem_index >> 2) - 1);
                         break;
                     case 3:
                     default:
+                        gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_WORD);
                         tcg_gen_helper_1_0(helper_fist_ST0, cpu_tmp2_i32);
                         tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                         gen_op_st_T0_A0(OT_WORD + s->mem_index);
@@ -5115,6 +5239,8 @@ static target_ulong disas_insn(DisasCont
                 }
                 break;
             case 0x0c: /* fldenv mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_READ,
+                                          (s->dflag) ? 28 : 14);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5122,11 +5248,14 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x0d: /* fldcw mem */
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_WORD);
                 gen_op_ld_T0_A0(OT_WORD + s->mem_index);
                 tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
                 tcg_gen_helper_0_1(helper_fldcw, cpu_tmp2_i32);
                 break;
             case 0x0e: /* fnstenv mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE,
+                                          (s->dflag) ? 28 : 14);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5134,17 +5263,20 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x0f: /* fnstcw mem */
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_WORD);
                 tcg_gen_helper_1_0(helper_fnstcw, cpu_tmp2_i32);
                 tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 break;
             case 0x1d: /* fldt mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
                 tcg_gen_helper_0_1(helper_fldt_ST0, cpu_A0);
                 break;
             case 0x1f: /* fstpt mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5152,6 +5284,8 @@ static target_ulong disas_insn(DisasCont
                 tcg_gen_helper_0_0(helper_fpop);
                 break;
             case 0x2c: /* frstor mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE,
+                                          (s->dflag) ? 108 : 94);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5159,6 +5293,8 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x2e: /* fnsave mem */
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE,
+                                          (s->dflag) ? 108 : 94);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5166,17 +5302,20 @@ static target_ulong disas_insn(DisasCont
                                    cpu_A0, tcg_const_i32(s->dflag));
                 break;
             case 0x2f: /* fnstsw mem */
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_WORD);
                 tcg_gen_helper_1_0(helper_fnstsw, cpu_tmp2_i32);
                 tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 break;
             case 0x3c: /* fbld */
+                gen_check_seg_access_size(s, seg_reg, ACC_READ, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
                 tcg_gen_helper_0_1(helper_fbld_ST0, cpu_A0);
                 break;
             case 0x3e: /* fbstp */
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE, 10);
                 if (s->cc_op != CC_OP_DYNAMIC)
                     gen_op_set_cc_op(s->cc_op);
                 gen_jmp_im(pc_start - s->cs_base);
@@ -5184,11 +5323,13 @@ static target_ulong disas_insn(DisasCont
                 tcg_gen_helper_0_0(helper_fpop);
                 break;
             case 0x3d: /* fildll */
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_QUAD);
                 tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, 
                                   (s->mem_index >> 2) - 1);
                 tcg_gen_helper_0_1(helper_fildll_ST0, cpu_tmp1_i64);
                 break;
             case 0x3f: /* fistpll */
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_QUAD);
                 tcg_gen_helper_1_0(helper_fistll_ST0, cpu_tmp1_i64);
                 tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, 
                                   (s->mem_index >> 2) - 1);
@@ -5734,6 +5875,7 @@ static target_ulong disas_insn(DisasCont
                                tcg_const_i32(val));
         } else {
             gen_stack_A0(s);
+            gen_check_seg_access(s, R_SS, ACC_READ | (OT_LONG + s->dflag));
             /* pop offset */
             gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
             if (s->dflag == 0)
@@ -5870,7 +6012,8 @@ static target_ulong disas_insn(DisasCont
             mod = (modrm >> 6) & 3;
             t0 = tcg_temp_local_new(TCG_TYPE_TL);
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
                 gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
             } else {
                 rm = (modrm & 7) | REX_B(s);
@@ -6012,7 +6155,8 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         if (mod != 3) {
             s->rip_offset = 1;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             gen_op_ld_T0_A0(ot + s->mem_index);
         } else {
             gen_op_mov_TN_reg(ot, 0, rm);
@@ -6043,7 +6187,8 @@ static target_ulong disas_insn(DisasCont
         rm = (modrm & 7) | REX_B(s);
         gen_op_mov_TN_reg(OT_LONG, 1, reg);
         if (mod != 3) {
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
             /* specific case: we need to add a displacement */
             gen_exts(ot, cpu_T[1]);
             tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot);
@@ -6272,7 +6417,8 @@ static target_ulong disas_insn(DisasCont
         if (mod == 3)
             goto illegal_op;
         gen_op_mov_TN_reg(ot, 0, reg);
-        gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+        gen_check_seg_access(s, seg_reg, ACC_READ | (ot + 1));
         gen_jmp_im(pc_start - s->cs_base);
         tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
         if (ot == OT_WORD)
@@ -6553,7 +6699,9 @@ static target_ulong disas_insn(DisasCont
             if (mod == 3)
                 goto illegal_op;
             gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ);
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access_size(s, seg_reg, ACC_WRITE,
+                                      CODE64(s) ? 10 : 6);
             tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit));
             gen_op_st_T0_A0(OT_WORD + s->mem_index);
             gen_add_A0_im(s, 2);
@@ -6602,7 +6750,9 @@ static target_ulong disas_insn(DisasCont
                 }
             } else { /* sidt */
                 gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_WRITE,
+                                          CODE64(s) ? 10 : 6);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit));
                 gen_op_st_T0_A0(OT_WORD + s->mem_index);
                 gen_add_A0_im(s, 2);
@@ -6708,7 +6858,9 @@ static target_ulong disas_insn(DisasCont
             } else {
                 gen_svm_check_intercept(s, pc_start,
                                         op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE);
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access_size(s, seg_reg, ACC_READ,
+                                          CODE64(s) ? 10 : 6);
                 gen_op_ld_T1_A0(OT_WORD + s->mem_index);
                 gen_add_A0_im(s, 2);
                 gen_op_ld_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
@@ -6824,7 +6976,8 @@ static target_ulong disas_insn(DisasCont
             mod = (modrm >> 6) & 3;
             rm = modrm & 7;
             if (mod != 3) {
-                gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | ot);
                 gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
             } else {
                 gen_op_mov_v_reg(ot, t0, rm);
@@ -7013,7 +7166,8 @@ static target_ulong disas_insn(DisasCont
                 gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
                 break;
             }
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access_size(s, seg_reg, ACC_WRITE, 512);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
             gen_jmp_im(pc_start - s->cs_base);
@@ -7028,7 +7182,8 @@ static target_ulong disas_insn(DisasCont
                 gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
                 break;
             }
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            gen_check_seg_access_size(s, seg_reg, ACC_READ, 512);
             if (s->cc_op != CC_OP_DYNAMIC)
                 gen_op_set_cc_op(s->cc_op);
             gen_jmp_im(pc_start - s->cs_base);
@@ -7044,11 +7199,13 @@ static target_ulong disas_insn(DisasCont
             if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) ||
                 mod == 3)
                 goto illegal_op;
-            gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+            seg_reg = gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
             if (op == 2) {
+                gen_check_seg_access(s, seg_reg, ACC_READ | OT_LONG);
                 gen_op_ld_T0_A0(OT_LONG + s->mem_index);
                 tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
             } else {
+                gen_check_seg_access(s, seg_reg, ACC_WRITE | OT_LONG);
                 tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
                 gen_op_st_T0_A0(OT_LONG + s->mem_index);
             }
Index: b/vl.c
===================================================================
--- a/vl.c
+++ b/vl.c
@@ -198,6 +198,7 @@ CharDriverState *serial_hds[MAX_SERIAL_P
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 #ifdef TARGET_I386
 int win2k_install_hack = 0;
+int seg_checks = 0;
 #endif
 int usb_enabled = 0;
 static VLANState *first_vlan;
@@ -7434,6 +7435,7 @@ static void help(int exitcode)
            "-std-vga        simulate a standard VGA card with VESA Bochs Extensions\n"
            "                (default is CL-GD5446 PCI VGA)\n"
            "-no-acpi        disable ACPI\n"
+           "-seg-checks     enable runtime segment access checks\n"
 #endif
 #ifdef CONFIG_CURSES
            "-curses         use a curses/ncurses interface instead of SDL\n"
@@ -7495,6 +7497,7 @@ enum {
     QEMU_OPTION_snapshot,
 #ifdef TARGET_I386
     QEMU_OPTION_no_fd_bootchk,
+    QEMU_OPTION_seg_checks,
 #endif
     QEMU_OPTION_m,
     QEMU_OPTION_nographic,
@@ -7588,6 +7591,7 @@ const QEMUOption qemu_options[] = {
     { "snapshot", 0, QEMU_OPTION_snapshot },
 #ifdef TARGET_I386
     { "no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk },
+    { "seg-checks", 0, QEMU_OPTION_seg_checks },
 #endif
     { "m", HAS_ARG, QEMU_OPTION_m },
     { "nographic", 0, QEMU_OPTION_nographic },
@@ -8383,6 +8387,9 @@ int main(int argc, char **argv)
             case QEMU_OPTION_kernel_kqemu:
                 kqemu_allowed = 2;
                 break;
+            case QEMU_OPTION_seg_checks:
+                seg_checks = 1;
+                break;
 #endif
             case QEMU_OPTION_usb:
                 usb_enabled = 1;
Index: b/target-i386/helper.h
===================================================================
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -218,4 +218,7 @@ DEF_HELPER(target_ulong, helper_rclq, (t
 DEF_HELPER(target_ulong, helper_rcrq, (target_ulong t0, target_ulong t1))
 #endif
 
+DEF_HELPER(void, helper_check_segmented_access, (target_ulong a0,
+                         int seg_reg, int type, int size))
+
 #undef DEF_HELPER
Index: b/target-i386/op_helper.c
===================================================================
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -2231,6 +2231,56 @@ void helper_load_seg(int seg_reg, int se
     }
 }
 
+static void log_seg_violation(target_ulong a0, int seg_reg, int type, int size)
+{
+    static const char *seg_name[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+    fprintf(logfile, "segment violation: %s %d byte via %s from %x:"
+                     TARGET_FMT_lx ", base " TARGET_FMT_lx ", limit %x, "
+                     "flags %x\n",
+            (type & ACC_WRITE) ? "write" : "read", size, seg_name[seg_reg],
+            env->segs[seg_reg].selector, a0 - env->segs[seg_reg].base,
+            env->segs[seg_reg].base, env->segs[seg_reg].limit,
+            env->segs[seg_reg].flags);
+}
+
+void helper_check_segmented_access(target_ulong a0, int seg_reg, int type,
+                                   int size)
+{
+    int seg_type = env->segs[seg_reg].flags & 0x1F00;
+    target_long addr = a0 - env->segs[seg_reg].base;
+
+    if (!(seg_type & 0x1000)) {
+        if (loglevel & CPU_LOG_INT)
+            log_seg_violation(a0, seg_reg, type, size);
+        raise_exception(EXCP0D_GPF);
+    }
+
+    if (type & ACC_WRITE) {
+        if (seg_type & 0x0800 || !(seg_type & 0x0200)) {
+            if (loglevel & CPU_LOG_INT)
+                log_seg_violation(a0, seg_reg, type, size);
+            raise_exception(EXCP0D_GPF);
+        }
+    } else {
+        if (seg_type & 0x0800 && !(seg_type & 0x0200)) {
+            if (loglevel & CPU_LOG_INT)
+                log_seg_violation(a0, seg_reg, type, size);
+            raise_exception(EXCP0D_GPF);
+        }
+    }
+
+    if (((seg_type & 0x0C00) == 0x0400
+            && (addr > (((env->segs[seg_reg].flags & 0x400000) ?
+                          0xFFFFFFFF : 0xFFFF) - (size - 1))
+            || addr <= env->segs[seg_reg].limit))
+            || addr > env->segs[seg_reg].limit - (size - 1)) {
+        if (loglevel & CPU_LOG_INT)
+            log_seg_violation(a0, seg_reg, type, size);
+        raise_exception(EXCP0D_GPF);
+    }
+}
+
 /* protected mode jump */
 void helper_ljmp_protected(int new_cs, target_ulong new_eip,
                            int next_eip_addend)

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 10:34 ` [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2 Jan Kiszka
@ 2008-07-14 10:55   ` Jamie Lokier
  2008-07-14 11:11     ` Paul Brook
  2008-07-14 11:05   ` [Qemu-devel] " Daniel P. Berrange
  1 sibling, 1 reply; 13+ messages in thread
From: Jamie Lokier @ 2008-07-14 10:55 UTC (permalink / raw)
  To: qemu-devel

Jan Kiszka wrote:
> This is the second version of my segment type and register check. It
> reduces the impact on the translator code significantly, and it also
> fixes a bug of the "size" helper variant in the previous version.
> 
> The idea of this patch is to generate calls to a check helper only in
> case the user requested this support via "-seg-checks". This feature
> remains off by default as most x86 OSes do not care about protection via
> segmentation anymore (and it was even removed from 64-bit modes by the
> CPU vendors). Moreover, checking the segment type and limit on every
> memory access is nothing that makes QEMU faster, so you will only want
> this if you are looking for very accurate emulation.
> 
> On Fabrice's request I tried to find the conditions which allow enabling
> -seg-checks by default but kicking it out most of the time during code
> translation. That works for 64-bit mode, of course, but I still see no
> clear indication for the case that 32-bit guests are not interested in
> type checking specifically. If you see one, let me know.

Some 32-bit guests effectively disable segment range calculations
checks by setting the maximum limit and zero offset.  Apparently, this
is faster on some real CPUs too.

Could type checking be done at translation time, including the segment
types in the translation cache key?

For guests like older Linux, with zero base and non-maximum limit in
user mode, could limit checking be done by the MMU TLB instead?

-- Jamie

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 10:34 ` [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2 Jan Kiszka
  2008-07-14 10:55   ` Jamie Lokier
@ 2008-07-14 11:05   ` Daniel P. Berrange
  2008-07-15 15:43     ` [Qemu-devel] " Jan Kiszka
  1 sibling, 1 reply; 13+ messages in thread
From: Daniel P. Berrange @ 2008-07-14 11:05 UTC (permalink / raw)
  To: qemu-devel

On Mon, Jul 14, 2008 at 12:34:48PM +0200, Jan Kiszka wrote:
> This is the second version of my segment type and register check. It
> reduces the impact on the translator code significantly, and it also
> fixes a bug of the "size" helper variant in the previous version.
> 
> The idea of this patch is to generate calls to a check helper only in
> case the user requested this support via "-seg-checks". This feature
> remains off by default as most x86 OSes do not care about protection via
> segmentation anymore (and it was even removed from 64-bit modes by the
> CPU vendors). 

Two current users of protection via segmentation I know of

 - 32-bit linux with the ExecShield capability will still use segmentation 
   to split the address space into executable vs non-executable regions, if
   the CPU doesn't have NX bit support.
 - 32-bit Xen uses segmentation for protecting the hypervisor.

Regards,
Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 10:55   ` Jamie Lokier
@ 2008-07-14 11:11     ` Paul Brook
  2008-07-14 14:02       ` Jamie Lokier
  0 siblings, 1 reply; 13+ messages in thread
From: Paul Brook @ 2008-07-14 11:11 UTC (permalink / raw)
  To: qemu-devel

> Some 32-bit guests effectively disable segment range calculations
> checks by setting the maximum limit and zero offset.  Apparently, this
> is faster on some real CPUs too.
>
> Could type checking be done at translation time, including the segment
> types in the translation cache key?

Maybe. If we have a spare hflags bit you could probably use that to indicate 
whether segment limit checks are needed.

> For guests like older Linux, with zero base and non-maximum limit in
> user mode, could limit checking be done by the MMU TLB instead?

Not really. The only resonable way to do this would be to use a very large 
virtual address space, with the high bits being the segment descriptor.  This 
might work for 32-bit targets on 64-bit hosts, but even then it's liable to 
be more pain than it's worth.

Paul

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 11:11     ` Paul Brook
@ 2008-07-14 14:02       ` Jamie Lokier
  2008-07-14 17:50         ` Kevin O'Connor
  0 siblings, 1 reply; 13+ messages in thread
From: Jamie Lokier @ 2008-07-14 14:02 UTC (permalink / raw)
  To: Paul Brook; +Cc: qemu-devel

Paul Brook wrote:
> > For guests like older Linux, with zero base and non-maximum limit in
> > user mode, could limit checking be done by the MMU TLB instead?
> 
> Not really. The only resonable way to do this would be to use a very
> large virtual address space, with the high bits being the segment
> descriptor.  This might work for 32-bit targets on 64-bit hosts, but
> even then it's liable to be more pain than it's worth.

I was thinking more like this, on any host:

   - All segment bases are zero, and all limits are LIMIT
     (3GiB for old Linux in user mode).
   - When filling the MMU TLB, if it's for an address >= LIMIT,
     treat as MMU exception.
   - Flush MMU TLB on any interesting segment change (limit gets
     smaller, etc.).
   - Count rate of interesting segment changes.  When it's high,
     switch to including segment checks in translated code (same as
     non-zero bases) and not flushing TLB.  When it's low, don't put
     segment checks into translated code, and use TLB flushes on
     segment changes.
   - Keep separate count for ring 0 and ring 3, or for
     "code which uses segment prefixes" vs "code which doesn't".

     This would suit old Linux, as kernel code uses segment to limit
     copy-from/to-user range, but user code has no segment changes
     normally, and the user limit check is equivalent to forcing all
     MMU pages above that virtual address to be supervisor-only.

-- Jamie

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 14:02       ` Jamie Lokier
@ 2008-07-14 17:50         ` Kevin O'Connor
  2008-07-14 18:51           ` Jamie Lokier
  0 siblings, 1 reply; 13+ messages in thread
From: Kevin O'Connor @ 2008-07-14 17:50 UTC (permalink / raw)
  To: qemu-devel

Hi Jamie,

On Mon, Jul 14, 2008 at 03:02:38PM +0100, Jamie Lokier wrote:
> Paul Brook wrote:
> > > For guests like older Linux, with zero base and non-maximum limit in
> > > user mode, could limit checking be done by the MMU TLB instead?
> > 
> > Not really. The only resonable way to do this would be to use a very
> > large virtual address space, with the high bits being the segment
> > descriptor.  This might work for 32-bit targets on 64-bit hosts, but
> > even then it's liable to be more pain than it's worth.
> 
> I was thinking more like this, on any host:
> 
>    - All segment bases are zero, and all limits are LIMIT
>      (3GiB for old Linux in user mode).
>    - When filling the MMU TLB, if it's for an address >= LIMIT,
>      treat as MMU exception.

That's interesting.

If I understand correctly, you're suggesting using segment checks in
translated code if any segment has a non-zero base or a different size
limit.  Thus limiting the optimization to those (common) cases where
the limits and bases are all the same.  Presumably this would also
require LIMIT to be page aligned.

One could probably allow ES, FS, and GS to have different bases/limits
as long as their ranges all fit within the primary range (0..LIMIT of
CS, DS, and SS).  It should be okay to always emit segment checks for
accesses that use these segments as they should be pretty rare.

Would this still work in 32bit flat mode?

>    - Flush MMU TLB on any interesting segment change (limit gets
>      smaller, etc.).
>    - Count rate of interesting segment changes.  When it's high,
>      switch to including segment checks in translated code (same as
>      non-zero bases) and not flushing TLB.  When it's low, don't put
>      segment checks into translated code, and use TLB flushes on
>      segment changes.
>    - Keep separate count for ring 0 and ring 3, or for
>      "code which uses segment prefixes" vs "code which doesn't".

Why are the heuristics needed?  I wonder if the tlb flush could just
be optimized.

One would only need to flush the tlb when transitioning from "segment
checks in translated code" mode to "segment checks in mmu" mode, or
when directly going to a new LIMIT.  In these cases one could just
flush NEWLIMIT..OLDLIMIT.

-Kevin

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 17:50         ` Kevin O'Connor
@ 2008-07-14 18:51           ` Jamie Lokier
  2008-07-15 15:48             ` [Qemu-devel] " Jan Kiszka
  0 siblings, 1 reply; 13+ messages in thread
From: Jamie Lokier @ 2008-07-14 18:51 UTC (permalink / raw)
  To: Kevin O'Connor; +Cc: qemu-devel

Kevin O'Connor wrote:
> >    - All segment bases are zero, and all limits are LIMIT
> >      (3GiB for old Linux in user mode).
> >    - When filling the MMU TLB, if it's for an address >= LIMIT,
> >      treat as MMU exception.
> 
> That's interesting.
> 
> If I understand correctly, you're suggesting using segment checks in
> translated code if any segment has a non-zero base or a different size
> limit.  Thus limiting the optimization to those (common) cases where
> the limits and bases are all the same.  Presumably this would also
> require LIMIT to be page aligned.

Yes.  I hadn't thought of non-zero bases, but that would work too.
Base and limit have to be page aligned.  The only code where that
isn't true is likely 16 bit MS-DOS and Windows code, which being
designed for much slower processes, should be fine with fully inlined
segment checks anyway.

> One could probably allow ES, FS, and GS to have different bases/limits
> as long as their ranges all fit within the primary range (0..LIMIT of
> CS, DS, and SS).

You could also translate just the base offset adding, in cases where
the limit covers the whole 4GiB.  But that adds more translation key
bits.

It's probably worth including ES as a "primary" segment like DS.

> It should be okay to always emit segment checks for
> accesses that use these segments as they should be pretty rare.

In Windows, and modern Linux, %fs or %gs are used in userspace code
for thread-specific variables.  In older Linux kernels, %fs is used to
copy data to/from userspace memory.  Maybe the translated checks for
these are not enough overhead to matter?

> Would this still work in 32bit flat mode?

It will if QEMU's MMU TLB is always used, and uses an identity
mapping.  I'm not familiar with that part of QEMU, but I had the
impression it's always used as it also traps memory-mapped I/O.

In which case, it should work in 16 bit flat mode too :)

> >    - Flush MMU TLB on any interesting segment change (limit gets
> >      smaller, etc.).
> >    - Count rate of interesting segment changes.  When it's high,
> >      switch to including segment checks in translated code (same as
> >      non-zero bases) and not flushing TLB.  When it's low, don't put
> >      segment checks into translated code, and use TLB flushes on
> >      segment changes.
> >    - Keep separate count for ring 0 and ring 3, or for
> >      "code which uses segment prefixes" vs "code which doesn't".
> 
> Why are the heuristics needed?  I wonder if the tlb flush could just
> be optimized.

Even if TLB flush itself is fast, you need to refill the TLB entries
on subsequent memory accesses.  It's good to avoid TLB flushes for
that reason.

I'm thinking of code like this from Linux which does

   movl %fs:(%eax),%ebx
   movl %ebx,(%ecx)

I.e. rapidly switching between segments with different limits, and the
%ds accesses are to addresses forbidden by %fs.  If you're inlining
%fs segment checks, though, then no TLB flush will be needed.

> One would only need to flush the tlb when transitioning from "segment
> checks in translated code" mode to "segment checks in mmu" mode, or
> when directly going to a new LIMIT.  In these cases one could just
> flush NEWLIMIT..OLDLIMIT.

That's true, you could optimise the flush in other ways too, such as
when changing protection ring, just flush certain types of TLB entry.
Or even keep multiple TLBs on the go, hashed on mostly-constant values
like the translation cache, and the TLB choice being a translation
cache key so it can be inlined into translated code.  I didn't want to
overcomplicate the suggestion, but you seem to like funky optimisations :-)

-- Jamie

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [Qemu-devel] Re: [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 11:05   ` [Qemu-devel] " Daniel P. Berrange
@ 2008-07-15 15:43     ` Jan Kiszka
  0 siblings, 0 replies; 13+ messages in thread
From: Jan Kiszka @ 2008-07-15 15:43 UTC (permalink / raw)
  To: qemu-devel

Daniel P. Berrange wrote:
> On Mon, Jul 14, 2008 at 12:34:48PM +0200, Jan Kiszka wrote:
>> This is the second version of my segment type and register check. It
>> reduces the impact on the translator code significantly, and it also
>> fixes a bug of the "size" helper variant in the previous version.
>>
>> The idea of this patch is to generate calls to a check helper only in
>> case the user requested this support via "-seg-checks". This feature
>> remains off by default as most x86 OSes do not care about protection via
>> segmentation anymore (and it was even removed from 64-bit modes by the
>> CPU vendors). 
> 
> Two current users of protection via segmentation I know of
> 
>  - 32-bit linux with the ExecShield capability will still use segmentation 
>    to split the address space into executable vs non-executable regions, if
>    the CPU doesn't have NX bit support.
>  - 32-bit Xen uses segmentation for protecting the hypervisor.

Ah, good to be reminded that we are not alone with our segmented OS here. ;)

That makes me realize that my patch lacks range checks for code
segments. I think I left it out as it is not that trivial...

Jan

-- 
Siemens AG, Corporate Technology, CT SE 2
Corporate Competence Center Embedded Linux

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [Qemu-devel] Re: [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-14 18:51           ` Jamie Lokier
@ 2008-07-15 15:48             ` Jan Kiszka
  2008-07-15 16:12               ` Jamie Lokier
  2008-07-16  1:20               ` Kevin O'Connor
  0 siblings, 2 replies; 13+ messages in thread
From: Jan Kiszka @ 2008-07-15 15:48 UTC (permalink / raw)
  To: qemu-devel

Jamie Lokier wrote:
> Kevin O'Connor wrote:
>>>    - All segment bases are zero, and all limits are LIMIT
>>>      (3GiB for old Linux in user mode).
>>>    - When filling the MMU TLB, if it's for an address >= LIMIT,
>>>      treat as MMU exception.
>> That's interesting.
>>
>> If I understand correctly, you're suggesting using segment checks in
>> translated code if any segment has a non-zero base or a different size
>> limit.  Thus limiting the optimization to those (common) cases where
>> the limits and bases are all the same.  Presumably this would also
>> require LIMIT to be page aligned.
> 
> Yes.  I hadn't thought of non-zero bases, but that would work too.
> Base and limit have to be page aligned.  The only code where that
> isn't true is likely 16 bit MS-DOS and Windows code, which being
> designed for much slower processes, should be fine with fully inlined
> segment checks anyway.
> 
>> One could probably allow ES, FS, and GS to have different bases/limits
>> as long as their ranges all fit within the primary range (0..LIMIT of
>> CS, DS, and SS).
> 
> You could also translate just the base offset adding, in cases where
> the limit covers the whole 4GiB.  But that adds more translation key
> bits.
> 
> It's probably worth including ES as a "primary" segment like DS.
> 
>> It should be okay to always emit segment checks for
>> accesses that use these segments as they should be pretty rare.
> 
> In Windows, and modern Linux, %fs or %gs are used in userspace code
> for thread-specific variables.  In older Linux kernels, %fs is used to
> copy data to/from userspace memory.  Maybe the translated checks for
> these are not enough overhead to matter?
> 
>> Would this still work in 32bit flat mode?
> 
> It will if QEMU's MMU TLB is always used, and uses an identity
> mapping.  I'm not familiar with that part of QEMU, but I had the
> impression it's always used as it also traps memory-mapped I/O.
> 
> In which case, it should work in 16 bit flat mode too :)
> 
>>>    - Flush MMU TLB on any interesting segment change (limit gets
>>>      smaller, etc.).
>>>    - Count rate of interesting segment changes.  When it's high,
>>>      switch to including segment checks in translated code (same as
>>>      non-zero bases) and not flushing TLB.  When it's low, don't put
>>>      segment checks into translated code, and use TLB flushes on
>>>      segment changes.
>>>    - Keep separate count for ring 0 and ring 3, or for
>>>      "code which uses segment prefixes" vs "code which doesn't".
>> Why are the heuristics needed?  I wonder if the tlb flush could just
>> be optimized.
> 
> Even if TLB flush itself is fast, you need to refill the TLB entries
> on subsequent memory accesses.  It's good to avoid TLB flushes for
> that reason.
> 
> I'm thinking of code like this from Linux which does
> 
>    movl %fs:(%eax),%ebx
>    movl %ebx,(%ecx)
> 
> I.e. rapidly switching between segments with different limits, and the
> %ds accesses are to addresses forbidden by %fs.  If you're inlining
> %fs segment checks, though, then no TLB flush will be needed.
> 
>> One would only need to flush the tlb when transitioning from "segment
>> checks in translated code" mode to "segment checks in mmu" mode, or
>> when directly going to a new LIMIT.  In these cases one could just
>> flush NEWLIMIT..OLDLIMIT.
> 
> That's true, you could optimise the flush in other ways too, such as
> when changing protection ring, just flush certain types of TLB entry.
> Or even keep multiple TLBs on the go, hashed on mostly-constant values
> like the translation cache, and the TLB choice being a translation
> cache key so it can be inlined into translated code.  I didn't want to
> overcomplicate the suggestion, but you seem to like funky optimisations :-)

Don't want to stop all your creativity, but just like Paul I'm also a
bit skeptical about the TLB way of achieving range and type safety.

My major concern once was that the TLB works on a global scope so that
you cannot tell the original segments behind some address apart. And
extending the virtual address space for this is a no-go on 32-bit hosts
(which I unfortunately had and still have to support here :->).

Jan

-- 
Siemens AG, Corporate Technology, CT SE 2
Corporate Competence Center Embedded Linux

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] Re: [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-15 15:48             ` [Qemu-devel] " Jan Kiszka
@ 2008-07-15 16:12               ` Jamie Lokier
  2008-07-16  1:20               ` Kevin O'Connor
  1 sibling, 0 replies; 13+ messages in thread
From: Jamie Lokier @ 2008-07-15 16:12 UTC (permalink / raw)
  To: qemu-devel

Jan Kiszka wrote:
> Don't want to stop all your creativity, but just like Paul I'm also a
> bit skeptical about the TLB way of achieving range and type safety.

Sure, that's understandable.  There has to be some "show me the code"
- I wish I had the time.  These are not simple optimisations, but they
could make segment checks free for most guests, so it's worth
considering them even if they're discarded.

> My major concern once was that the TLB works on a global scope so that
> you cannot tell the original segments behind some address apart.

Yes.  The only way around it is context - either having multiple TLBs,
or inlining segments into translated code, so you tell the original
segments apart at the call site.

> And extending the virtual address space for this is a no-go on
> 32-bit hosts (which I unfortunately had and still have to support
> here :->).

Definitely not increasing the virtual address space - I'm still using
32-bit hosts too :-)  I'm not sure that would be safe even on 64-bit
hosts - what if the guest has its own uses for the whole address
space?

Expanding the TLB key space (or equivalently, multiple TLBs) is
different from expanding the virtual address space, though.

-- Jamie

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] Re: [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-15 15:48             ` [Qemu-devel] " Jan Kiszka
  2008-07-15 16:12               ` Jamie Lokier
@ 2008-07-16  1:20               ` Kevin O'Connor
  2008-07-16  2:43                 ` Kevin O'Connor
  1 sibling, 1 reply; 13+ messages in thread
From: Kevin O'Connor @ 2008-07-16  1:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jan Kiszka

Hi Jan,

On Tue, Jul 15, 2008 at 05:48:23PM +0200, Jan Kiszka wrote:
> > Kevin O'Connor wrote:
> >>Thus limiting the optimization to those (common) cases where
> >> the limits and bases are all the same.
> >> One could probably allow ES, FS, and GS to have different bases/limits
> >> as long as their ranges all fit within the primary range (0..LIMIT of
> >> CS, DS, and SS).
> >> It should be okay to always emit segment checks for
> >> accesses that use these segments as they should be pretty rare.
[...]
> Don't want to stop all your creativity, but just like Paul I'm also a
> bit skeptical about the TLB way of achieving range and type safety.
> 
> My major concern once was that the TLB works on a global scope so that
> you cannot tell the original segments behind some address apart.

If all five segments have the same base and limit (0..LIMIT) then I
would think it would not matter what segment filled a TLB.  (It should
not be possible to fill a TLB outside of 0..LIMIT, and any access to a
tlb in 0..LIMIT should be okay regardless of the segment used.)

Of course, it's not uncommon for FS and GS to have a different base.
In this case, it should still be safe as long as FS/GS don't allow
access outside of 0..LIMIT (so that invalid TLBs don't get loaded) and
one emits limit checks on each FS/GS use (so that the limits on FS/GS
are enforced).

Qemu already has an optimization (HF_ADDSEG_MASK) for the case where
DS/ES/SS all have a zero base.  I was thinking that an optimization
could noticeably reduce the cost of limit checks for the common case
where DS/ES/SS all have a zero base and the same limit.  (I
incorrectly stated CS/DS/SS above.)

Of course, it would require a lot of work, and it would be hard to
verify that all the corner cases were handled.  So, I was more
thinking out loud than proposing a solution.  :-)

>And
> extending the virtual address space for this is a no-go on 32-bit hosts
> (which I unfortunately had and still have to support here :->).

Yeah - I wouldn't want to go there.  Trying to maintain multiple TLBs
or adding more bits to the TLB hash just seems silly to me.

-Kevin

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Qemu-devel] Re: [RFC][PATCH] x86: Optional segment type and limit checks - v2
  2008-07-16  1:20               ` Kevin O'Connor
@ 2008-07-16  2:43                 ` Kevin O'Connor
  0 siblings, 0 replies; 13+ messages in thread
From: Kevin O'Connor @ 2008-07-16  2:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jan Kiszka

On Tue, Jul 15, 2008 at 09:20:39PM -0400, Kevin O'Connor wrote:
> Qemu already has an optimization (HF_ADDSEG_MASK) for the case where
> DS/ES/SS all have a zero base.  I was thinking that an optimization
> could noticeably reduce the cost of limit checks for the common case
> where DS/ES/SS all have a zero base and the same limit.  (I
> incorrectly stated CS/DS/SS above.)
> 
> Of course, it would require a lot of work, and it would be hard to
> verify that all the corner cases were handled.  So, I was more
> thinking out loud than proposing a solution.  :-)

Just "thinking out loud" further - a simpler optimization would be to
extend the existing HF_ADDSEG_MASK flag to the case where DS/ES/SS
have a base a zero, a limit of 0xffffffff, and are read/writable.
Then, one would only need to emit segment checks when one of the above
conditions are not met or an explicit segment override to another
segment is given.

This simpler optimization would miss some opportunities, but it would
be much easier.

-Kevin

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2008-07-16  2:43 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-09 12:12 [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks Jan Kiszka
2008-07-14 10:34 ` [Qemu-devel] [RFC][PATCH] x86: Optional segment type and limit checks - v2 Jan Kiszka
2008-07-14 10:55   ` Jamie Lokier
2008-07-14 11:11     ` Paul Brook
2008-07-14 14:02       ` Jamie Lokier
2008-07-14 17:50         ` Kevin O'Connor
2008-07-14 18:51           ` Jamie Lokier
2008-07-15 15:48             ` [Qemu-devel] " Jan Kiszka
2008-07-15 16:12               ` Jamie Lokier
2008-07-16  1:20               ` Kevin O'Connor
2008-07-16  2:43                 ` Kevin O'Connor
2008-07-14 11:05   ` [Qemu-devel] " Daniel P. Berrange
2008-07-15 15:43     ` [Qemu-devel] " Jan Kiszka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).