commit 3ab493de4c524926bb75b04765b644f9189ccf01
Author: bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Date:   Thu Nov 13 01:42:19 2003 +0000

    added verr, verw, arpl - more precise segment rights checks
    
    
    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@453 c046a42c-6fe2-441c-8c8c-71466251a162

diff --git a/target-i386/exec.h b/target-i386/exec.h
index 6741485..dd11a3a 100644
--- a/target-i386/exec.h
+++ b/target-i386/exec.h
@@ -170,6 +170,8 @@ void helper_rdmsr(void);
 void helper_wrmsr(void);
 void helper_lsl(void);
 void helper_lar(void);
+void helper_verr(void);
+void helper_verw(void);
 
 void check_iob_T0(void);
 void check_iow_T0(void);
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 2f4a15d..0706515 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -1037,13 +1037,15 @@ void helper_ltr_T0(void)
     env->tr.selector = selector;
 }
 
-/* only works if protected mode and not VM86. Calling load_seg with
-   seg_reg == R_CS is discouraged */
-/* XXX: add ring level checks */
+/* only works if protected mode and not VM86. seg_reg must be != R_CS */
 void load_seg(int seg_reg, int selector, unsigned int cur_eip)
 {
     uint32_t e1, e2;
-    
+    int cpl, dpl, rpl;
+    SegmentCache *dt;
+    int index;
+    uint8_t *ptr;
+
     if ((selector & 0xfffc) == 0) {
         /* null selector case */
         if (seg_reg == R_SS) {
@@ -1053,26 +1055,51 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip)
             cpu_x86_load_seg_cache(env, seg_reg, selector, NULL, 0, 0);
         }
     } else {
-        if (load_segment(&e1, &e2, selector) != 0) {
+        
+        if (selector & 0x4)
+            dt = &env->ldt;
+        else
+            dt = &env->gdt;
+        index = selector & ~7;
+        if ((index + 7) > dt->limit) {
             EIP = cur_eip;
             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
         }
-        if (!(e2 & DESC_S_MASK) ||
-            (e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
+        ptr = dt->base + index;
+        e1 = ldl_kernel(ptr);
+        e2 = ldl_kernel(ptr + 4);
+
+        if (!(e2 & DESC_S_MASK)) {
             EIP = cur_eip;
             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
         }
-
+        rpl = selector & 3;
+        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+        cpl = env->hflags & HF_CPL_MASK;
         if (seg_reg == R_SS) {
+            /* must be writable segment */
             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
                 EIP = cur_eip;
                 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
             }
+            if (rpl != cpl || dpl != cpl) {
+                EIP = cur_eip;
+                raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+            }
         } else {
+            /* must be readable segment */
             if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
                 EIP = cur_eip;
                 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
             }
+            
+            if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
+                /* if not conforming code, test rights */
+                if (dpl < cpl || dpl < rpl) {
+                    EIP = cur_eip;
+                    raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+                }
+            }
         }
 
         if (!(e2 & DESC_P_MASK)) {
@@ -1082,6 +1109,13 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip)
             else
                 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
         }
+
+        /* set the access bit if not already set */
+        if (!(e2 & DESC_A_MASK)) {
+            e2 |= DESC_A_MASK;
+            stl_kernel(ptr + 4, e2);
+        }
+
         cpu_x86_load_seg_cache(env, seg_reg, selector, 
                        get_seg_base(e1, e2),
                        get_seg_limit(e1, e2),
@@ -1696,14 +1730,38 @@ void helper_lsl(void)
 {
     unsigned int selector, limit;
     uint32_t e1, e2;
+    int rpl, dpl, cpl, type;
 
     CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
     selector = T0 & 0xffff;
     if (load_segment(&e1, &e2, selector) != 0)
         return;
-    limit = (e1 & 0xffff) | (e2 & 0x000f0000);
-    if (e2 & (1 << 23))
-        limit = (limit << 12) | 0xfff;
+    rpl = selector & 3;
+    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+    cpl = env->hflags & HF_CPL_MASK;
+    if (e2 & DESC_S_MASK) {
+        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+            /* conforming */
+        } else {
+            if (dpl < cpl || dpl < rpl)
+                return;
+        }
+    } else {
+        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+        switch(type) {
+        case 1:
+        case 2:
+        case 3:
+        case 9:
+        case 11:
+            break;
+        default:
+            return;
+        }
+        if (dpl < cpl || dpl < rpl)
+            return;
+    }
+    limit = get_seg_limit(e1, e2);
     T1 = limit;
     CC_SRC |= CC_Z;
 }
@@ -1712,15 +1770,105 @@ void helper_lar(void)
 {
     unsigned int selector;
     uint32_t e1, e2;
+    int rpl, dpl, cpl, type;
 
     CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
     selector = T0 & 0xffff;
+    if ((selector & 0xfffc) == 0)
+        return;
     if (load_segment(&e1, &e2, selector) != 0)
         return;
+    rpl = selector & 3;
+    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+    cpl = env->hflags & HF_CPL_MASK;
+    if (e2 & DESC_S_MASK) {
+        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+            /* conforming */
+        } else {
+            if (dpl < cpl || dpl < rpl)
+                return;
+        }
+    } else {
+        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+        switch(type) {
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+        case 9:
+        case 11:
+        case 12:
+            break;
+        default:
+            return;
+        }
+        if (dpl < cpl || dpl < rpl)
+            return;
+    }
     T1 = e2 & 0x00f0ff00;
     CC_SRC |= CC_Z;
 }
 
+void helper_verr(void)
+{
+    unsigned int selector;
+    uint32_t e1, e2;
+    int rpl, dpl, cpl;
+
+    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    selector = T0 & 0xffff;
+    if ((selector & 0xfffc) == 0)
+        return;
+    if (load_segment(&e1, &e2, selector) != 0)
+        return;
+    if (!(e2 & DESC_S_MASK))
+        return;
+    rpl = selector & 3;
+    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+    cpl = env->hflags & HF_CPL_MASK;
+    if (e2 & DESC_CS_MASK) {
+        if (!(e2 & DESC_R_MASK))
+            return;
+        if (!(e2 & DESC_C_MASK)) {
+            if (dpl < cpl || dpl < rpl)
+                return;
+        }
+    } else {
+        if (dpl < cpl || dpl < rpl)
+            return;
+    }
+    /* ok */
+}
+
+void helper_verw(void)
+{
+    unsigned int selector;
+    uint32_t e1, e2;
+    int rpl, dpl, cpl;
+
+    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    selector = T0 & 0xffff;
+    if ((selector & 0xfffc) == 0)
+        return;
+    if (load_segment(&e1, &e2, selector) != 0)
+        return;
+    if (!(e2 & DESC_S_MASK))
+        return;
+    rpl = selector & 3;
+    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+    cpl = env->hflags & HF_CPL_MASK;
+    if (e2 & DESC_CS_MASK) {
+        return;
+    } else {
+        if (dpl < cpl || dpl < rpl)
+            return;
+        if (!(e2 & DESC_W_MASK))
+            return;
+    }
+    /* ok */
+}
+
 /* FPU helpers */
 
 void helper_fldt_ST0_A0(void)
diff --git a/target-i386/op.c b/target-i386/op.c
index f1276f7..d4742a3 100644
--- a/target-i386/op.c
+++ b/target-i386/op.c
@@ -936,6 +936,35 @@ void OPPROTO op_lar(void)
     helper_lar();
 }
 
+void OPPROTO op_verr(void)
+{
+    helper_verr();
+}
+
+void OPPROTO op_verw(void)
+{
+    helper_verw();
+}
+
+void OPPROTO op_arpl(void)
+{
+    if ((T0 & 3) < (T1 & 3)) {
+        /* XXX: emulate bug or 0xff3f0000 oring as in bochs ? */
+        T0 = (T0 & ~3) | (T1 & 3);
+        T1 = CC_Z;
+   } else {
+        T1 = 0;
+    }
+    FORCE_RET();
+}
+            
+void OPPROTO op_arpl_update(void)
+{
+    int eflags;
+    eflags = cc_table[CC_OP].compute_all();
+    CC_SRC = (eflags & ~CC_Z) | T1;
+}
+    
 /* T0: segment, T1:eip */
 void OPPROTO op_ljmp_protected_T0_T1(void)
 {
