]> git.proxmox.com Git - mirror_qemu.git/commitdiff
precise exceptions - more accurate interrupt semantics
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 27 May 2003 23:28:08 +0000 (23:28 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 27 May 2003 23:28:08 +0000 (23:28 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@193 c046a42c-6fe2-441c-8c8c-71466251a162

linux-user/main.c
op-i386.c

index a6a84e5363704edeb3d1f2c6576852a3855a53a3..eeea342d2b148ae7155e91ac8d8aa38643248052 100644 (file)
@@ -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);
index cfae103639665e31da67d1dca752d78e1166b3a9..69bd4cffca5990aad98e8155ebd037f26acd3c9d 100644 (file)
--- 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)