aboutsummaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-11-13 01:42:19 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-11-13 01:42:19 +0000
commit3ab493de4c524926bb75b04765b644f9189ccf01 (patch)
tree24764a89faa67761bde836517a5cc7e634774194 /target-i386
parent3e25f9515a07d47da843d9b3835e25def785dcee (diff)
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
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/exec.h2
-rw-r--r--target-i386/helper.c170
-rw-r--r--target-i386/op.c29
3 files changed, 190 insertions, 11 deletions
diff --git a/target-i386/exec.h b/target-i386/exec.h
index 6741485a0a..dd11a3a3ed 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 2f4a15d873..0706515304 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 f1276f7a64..d4742a3847 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)
{