summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-05-27 23:28:08 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-05-27 23:28:08 +0000
commitf4beb510a41980e119f787746442ca1b87c06754 (patch)
treead59b9c88a1870f1a86fd5837c266c9b96796bf4
parentd731dae8e3c9591727073f6e54af08c1ee960540 (diff)
downloadqemu-f4beb510a41980e119f787746442ca1b87c06754.tar.gz
precise exceptions - more accurate interrupt semantics
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@193 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--linux-user/main.c108
-rw-r--r--op-i386.c196
2 files changed, 250 insertions, 54 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index a6a84e5363..eeea342d2b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -100,24 +100,35 @@ int cpu_x86_inl(CPUX86State *env, int addr)
return 0;
}
-void write_dt(void *ptr, unsigned long addr, unsigned long limit,
- int seg32_bit)
+static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
+ int flags)
{
- unsigned int e1, e2, limit_in_pages;
- limit_in_pages = 0;
- if (limit > 0xffff) {
- limit = limit >> 12;
- limit_in_pages = 1;
- }
+ unsigned int e1, e2;
e1 = (addr << 16) | (limit & 0xffff);
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
- e2 |= limit_in_pages << 23; /* byte granularity */
- e2 |= seg32_bit << 22; /* 32 bit segment */
+ e2 |= flags;
+ stl((uint8_t *)ptr, e1);
+ stl((uint8_t *)ptr + 4, e2);
+}
+
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ unsigned long addr, unsigned int sel)
+{
+ unsigned int e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
stl((uint8_t *)ptr, e1);
stl((uint8_t *)ptr + 4, e2);
}
uint64_t gdt_table[6];
+uint64_t idt_table[256];
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
void cpu_loop(CPUX86State *env)
{
@@ -128,30 +139,34 @@ void cpu_loop(CPUX86State *env)
for(;;) {
trapnr = cpu_x86_exec(env);
switch(trapnr) {
+ case 0x80:
+ /* linux syscall */
+ env->regs[R_EAX] = do_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP]);
+ break;
+ case EXCP0B_NOSEG:
+ case EXCP0C_STACK:
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(info.si_signo, &info);
+ break;
case EXCP0D_GPF:
if (env->eflags & VM_MASK) {
handle_vm86_fault(env);
} else {
- pc = env->seg_cache[R_CS].base + env->eip;
- if (pc[0] == 0xcd && pc[1] == 0x80) {
- /* syscall */
- env->eip += 2;
- env->regs[R_EAX] = do_syscall(env,
- env->regs[R_EAX],
- env->regs[R_EBX],
- env->regs[R_ECX],
- env->regs[R_EDX],
- env->regs[R_ESI],
- env->regs[R_EDI],
- env->regs[R_EBP]);
- } else {
- /* XXX: more precise info */
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = TARGET_SI_KERNEL;
- info._sifields._sigfault._addr = 0;
- queue_signal(info.si_signo, &info);
- }
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(info.si_signo, &info);
}
break;
case EXCP0E_PAGE:
@@ -365,11 +380,40 @@ int main(int argc, char **argv)
env->regs[R_ESP] = regs->esp;
env->eip = regs->eip;
+ /* linux interrupt setup */
+ env->idt.base = (void *)idt_table;
+ env->idt.limit = sizeof(idt_table) - 1;
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 3);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+ set_idt(0x80, 3);
+
/* linux segment setup */
env->gdt.base = (void *)gdt_table;
env->gdt.limit = sizeof(gdt_table) - 1;
- write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1);
- write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1);
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+ write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
diff --git a/op-i386.c b/op-i386.c
index cfae103639..69bd4cffca 100644
--- a/op-i386.c
+++ b/op-i386.c
@@ -364,8 +364,10 @@ void OPPROTO op_divb_AL_T0(void)
num = (EAX & 0xffff);
den = (T0 & 0xff);
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
q = (num / den) & 0xff;
r = (num % den) & 0xff;
EAX = (EAX & 0xffff0000) | (r << 8) | q;
@@ -377,8 +379,10 @@ void OPPROTO op_idivb_AL_T0(void)
num = (int16_t)EAX;
den = (int8_t)T0;
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
q = (num / den) & 0xff;
r = (num % den) & 0xff;
EAX = (EAX & 0xffff0000) | (r << 8) | q;
@@ -390,8 +394,10 @@ void OPPROTO op_divw_AX_T0(void)
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
den = (T0 & 0xffff);
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
q = (num / den) & 0xffff;
r = (num % den) & 0xffff;
EAX = (EAX & 0xffff0000) | q;
@@ -404,8 +410,10 @@ void OPPROTO op_idivw_AX_T0(void)
num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
den = (int16_t)T0;
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
q = (num / den) & 0xffff;
r = (num % den) & 0xffff;
EAX = (EAX & 0xffff0000) | q;
@@ -435,8 +443,10 @@ void OPPROTO op_divl_EAX_T0(void)
num = EAX | ((uint64_t)EDX << 32);
den = T0;
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
#ifdef BUGGY_GCC_DIV64
r = div64(&q, num, den);
#else
@@ -454,8 +464,10 @@ void OPPROTO op_idivl_EAX_T0(void)
num = EAX | ((uint64_t)EDX << 32);
den = T0;
- if (den == 0)
+ if (den == 0) {
+ EIP = PARAM1;
raise_exception(EXCP00_DIVZ);
+ }
#ifdef BUGGY_GCC_DIV64
r = idiv64(&q, num, den);
#else
@@ -614,12 +626,102 @@ void OPPROTO op_jmp_im(void)
EIP = PARAM1;
}
-void OPPROTO op_int_im(void)
+#if 0
+/* full interrupt support (only useful for real CPU emulation, not
+ finished) - I won't do it any time soon, finish it if you want ! */
+void raise_interrupt(int intno, int is_int, int error_code,
+ unsigned int next_eip)
+{
+ SegmentDescriptorTable *dt;
+ uint8_t *ptr;
+ int type, dpl, cpl;
+ uint32_t e1, e2;
+
+ dt = &env->idt;
+ if (intno * 8 + 7 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ ptr = dt->base + intno * 8;
+ e1 = ldl(ptr);
+ e2 = ldl(ptr + 4);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 5: /* task gate */
+ case 6: /* 286 interrupt gate */
+ case 7: /* 286 trap gate */
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->segs[R_CS] & 3;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
+}
+
+#else
+
+/*
+ * is_int is TRUE if coming from the int instruction. next_eip is the
+ * EIP value AFTER the interrupt instruction. It is only relevant if
+ * is_int is TRUE.
+ */
+void raise_interrupt(int intno, int is_int, int error_code,
+ unsigned int next_eip)
+{
+ SegmentDescriptorTable *dt;
+ uint8_t *ptr;
+ int dpl, cpl;
+ uint32_t e2;
+
+ dt = &env->idt;
+ ptr = dt->base + (intno * 8);
+ e2 = ldl(ptr + 4);
+
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = 3;
+ /* check privledge if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+
+ /* Since we emulate only user space, we cannot do more than
+ exiting the emulation with the suitable exception and error
+ code */
+ if (is_int)
+ EIP = next_eip;
+ env->exception_index = intno;
+ env->error_code = error_code;
+
+ cpu_loop_exit();
+}
+
+#endif
+
+/* shortcuts to generate exceptions */
+void raise_exception_err(int exception_index, int error_code)
+{
+ raise_interrupt(exception_index, 0, error_code, 0);
+}
+
+void raise_exception(int exception_index)
+{
+ raise_interrupt(exception_index, 0, 0, 0);
+}
+
+void OPPROTO op_raise_interrupt(void)
{
int intno;
+ unsigned int next_eip;
intno = PARAM1;
- EIP = PARAM2;
- raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ next_eip = PARAM2;
+ raise_interrupt(intno, 1, 0, next_eip);
}
void OPPROTO op_raise_exception(void)
@@ -634,8 +736,7 @@ void OPPROTO op_into(void)
int eflags;
eflags = cc_table[CC_OP].compute_all();
if (eflags & CC_O) {
- EIP = PARAM1;
- raise_exception(EXCP04_INTO);
+ raise_interrupt(EXCP04_INTO, 1, 0, PARAM1);
}
FORCE_RET();
}
@@ -674,8 +775,10 @@ void OPPROTO op_boundw(void)
low = ldsw((uint8_t *)A0);
high = ldsw((uint8_t *)A0 + 2);
v = (int16_t)T0;
- if (v < low || v > high)
+ if (v < low || v > high) {
+ EIP = PARAM1;
raise_exception(EXCP05_BOUND);
+ }
FORCE_RET();
}
@@ -685,8 +788,10 @@ void OPPROTO op_boundl(void)
low = ldl((uint8_t *)A0);
high = ldl((uint8_t *)A0 + 4);
v = T0;
- if (v < low || v > high)
+ if (v < low || v > high) {
+ EIP = PARAM1;
raise_exception(EXCP05_BOUND);
+ }
FORCE_RET();
}
@@ -1116,8 +1221,8 @@ void OPPROTO op_das(void)
/* segment handling */
-/* XXX: use static VM86 information */
-void load_seg(int seg_reg, int selector)
+/* only works if protected mode and not VM86 */
+void load_seg(int seg_reg, int selector, unsigned cur_eip)
{
SegmentCache *sc;
SegmentDescriptorTable *dt;
@@ -1126,21 +1231,56 @@ void load_seg(int seg_reg, int selector)
uint8_t *ptr;
sc = &env->seg_cache[seg_reg];
- if (env->eflags & VM_MASK) {
- sc->base = (void *)(selector << 4);
- sc->limit = 0xffff;
- sc->seg_32bit = 0;
+ if ((selector & 0xfffc) == 0) {
+ /* null selector case */
+ if (seg_reg == R_SS) {
+ EIP = cur_eip;
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ } else {
+ /* XXX: each access should trigger an exception */
+ sc->base = NULL;
+ sc->limit = 0;
+ sc->seg_32bit = 1;
+ }
} else {
if (selector & 0x4)
dt = &env->ldt;
else
dt = &env->gdt;
index = selector & ~7;
- if ((index + 7) > dt->limit)
- raise_exception_err(EXCP0D_GPF, selector);
+ if ((index + 7) > dt->limit) {
+ EIP = cur_eip;
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
ptr = dt->base + index;
e1 = ldl(ptr);
e2 = ldl(ptr + 4);
+ if (!(e2 & DESC_S_MASK) ||
+ (e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
+ EIP = cur_eip;
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
+
+ if (seg_reg == R_SS) {
+ if ((e2 & (DESC_CS_MASK | DESC_W_MASK)) == 0) {
+ EIP = cur_eip;
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
+ } else {
+ if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
+ EIP = cur_eip;
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
+ }
+
+ if (!(e2 & DESC_P_MASK)) {
+ EIP = cur_eip;
+ if (seg_reg == R_SS)
+ raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
+ else
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ }
+
sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000);
if (e2 & (1 << 23))
@@ -1156,7 +1296,19 @@ void load_seg(int seg_reg, int selector)
void OPPROTO op_movl_seg_T0(void)
{
- load_seg(PARAM1, T0 & 0xffff);
+ load_seg(PARAM1, T0 & 0xffff, PARAM2);
+}
+
+/* faster VM86 version */
+void OPPROTO op_movl_seg_T0_vm(void)
+{
+ int selector;
+
+ selector = T0 & 0xffff;
+ /* env->segs[] access */
+ *(uint32_t *)((char *)env + PARAM1) = selector;
+ /* env->seg_cache[] access */
+ ((SegmentCache *)((char *)env + PARAM2))->base = (void *)(selector << 4);
}
void OPPROTO op_movl_T0_seg(void)