precise exceptions - more accurate interrupt semantics


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@193 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/linux-user/main.c b/linux-user/main.c
index a6a84e5..eeea342 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -100,24 +100,35 @@
     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 @@
     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 @@
     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);