added fsave/frstor/fstenv/fldenv/fcomi - fixed cpuid - make lret/iret restartable
[qemu] / exec-i386.c
index cde3da7..2114d65 100644 (file)
@@ -39,9 +39,7 @@ void cpu_unlock(void)
     spin_unlock(&global_cpu_lock);
 }
 
-/* exception support */
-/* NOTE: not static to force relocation generation by GCC */
-void raise_exception_err(int exception_index, int error_code)
+void cpu_loop_exit(void)
 {
     /* NOTE: the register at this point must be saved by hand because
        longjmp restore them */
@@ -76,17 +74,9 @@ void raise_exception_err(int exception_index, int error_code)
 #ifdef reg_EDI
     env->regs[R_EDI] = EDI;
 #endif
-    env->exception_index = exception_index;
-    env->error_code = error_code;
     longjmp(env->jmp_env, 1);
 }
 
-/* short cut if error_code is 0 or not present */
-void raise_exception(int exception_index)
-{
-    raise_exception_err(exception_index, 0);
-}
-
 int cpu_x86_exec(CPUX86State *env1)
 {
     int saved_T0, saved_T1, saved_A0;
@@ -115,12 +105,12 @@ int cpu_x86_exec(CPUX86State *env1)
 #ifdef reg_EDI
     int saved_EDI;
 #endif
-    int code_gen_size, ret, code_size;
+    int code_gen_size, ret;
     void (*gen_func)(void);
     TranslationBlock *tb, **ptb;
     uint8_t *tc_ptr, *cs_base, *pc;
     unsigned int flags;
-
+    
     /* first we save global registers */
     saved_T0 = T0;
     saved_T1 = T1;
@@ -169,9 +159,11 @@ int cpu_x86_exec(CPUX86State *env1)
 
     /* prepare setjmp context for exception handling */
     if (setjmp(env->jmp_env) == 0) {
+        T0 = 0; /* force lookup of first TB */
         for(;;) {
             if (env->interrupt_request) {
-                raise_exception(EXCP_INTERRUPT);
+                env->exception_index = EXCP_INTERRUPT;
+                cpu_loop_exit();
             }
 #ifdef DEBUG_EXEC
             if (loglevel) {
@@ -205,32 +197,39 @@ int cpu_x86_exec(CPUX86State *env1)
                 flags |= (1 << GEN_FLAG_VM_SHIFT);
                 flags |= (3 << GEN_FLAG_CPL_SHIFT);
             }
-            flags |= (env->eflags & IOPL_MASK) >> (12 - GEN_FLAG_IOPL_SHIFT);
-            flags |= (env->eflags & TF_MASK) << (GEN_FLAG_TF_SHIFT - 8);
+            flags |= (env->eflags & (IOPL_MASK | TF_MASK));
             cs_base = env->seg_cache[R_CS].base;
             pc = cs_base + env->eip;
             tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base, 
                          flags);
             if (!tb) {
-                /* if no translated code available, then translate it now */
-                /* very inefficient but safe: we lock all the cpus
-                   when generating code */
                 spin_lock(&tb_lock);
+                /* if no translated code available, then translate it now */
+                tb = tb_alloc((unsigned long)pc);
+                if (!tb) {
+                    /* flush must be done */
+                    tb_flush();
+                    /* cannot fail at this point */
+                    tb = tb_alloc((unsigned long)pc);
+                    /* don't forget to invalidate previous TB info */
+                    ptb = &tb_hash[tb_hash_func((unsigned long)pc)];
+                    T0 = 0;
+                }
                 tc_ptr = code_gen_ptr;
-                ret = cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, 
-                                       &code_gen_size, pc, cs_base, flags,
-                                       &code_size);
+                tb->tc_ptr = tc_ptr;
+                tb->cs_base = (unsigned long)cs_base;
+                tb->flags = flags;
+                ret = cpu_x86_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size);
                 /* if invalid instruction, signal it */
                 if (ret != 0) {
+                    /* NOTE: the tb is allocated but not linked, so we
+                       can leave it */
                     spin_unlock(&tb_lock);
                     raise_exception(EXCP06_ILLOP);
                 }
-                tb = tb_alloc((unsigned long)pc, code_size);
                 *ptb = tb;
-                tb->cs_base = (unsigned long)cs_base;
-                tb->flags = flags;
-                tb->tc_ptr = tc_ptr;
                 tb->hash_next = NULL;
+                tb_link(tb);
                 code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
                 spin_unlock(&tb_lock);
             }
@@ -241,14 +240,22 @@ int cpu_x86_exec(CPUX86State *env1)
                        lookup_symbol((void *)tb->pc));
            }
 #endif
-            /* execute the generated code */
+            /* see if we can patch the calling TB */
+            if (T0 != 0 && !(env->eflags & TF_MASK)) {
+                spin_lock(&tb_lock);
+                tb_add_jump((TranslationBlock *)(T0 & ~3), T0 & 3, tb);
+                spin_unlock(&tb_lock);
+            }
+
             tc_ptr = tb->tc_ptr;
+
+            /* execute the generated code */
             gen_func = (void *)tc_ptr;
 #ifdef __sparc__
            __asm__ __volatile__("call  %0\n\t"
                                 " mov  %%o7,%%i0"
                                 : /* no outputs */
-                                : "r" (gen_func)
+                                : "r" (gen_func) 
                                 : "i0", "i1", "i2", "i3", "i4", "i5");
 #else
             gen_func();
@@ -304,7 +311,43 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
 
     saved_env = env;
     env = s;
-    load_seg(seg_reg, selector);
+    if (env->eflags & VM_MASK) {
+        SegmentCache *sc;
+        selector &= 0xffff;
+        sc = &env->seg_cache[seg_reg];
+        /* NOTE: in VM86 mode, limit and seg_32bit are never reloaded,
+           so we must load them here */
+        sc->base = (void *)(selector << 4);
+        sc->limit = 0xffff;
+        sc->seg_32bit = 0;
+        env->segs[seg_reg] = selector;
+    } else {
+        load_seg(seg_reg, selector, 0);
+    }
+    env = saved_env;
+}
+
+void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32)
+{
+    CPUX86State *saved_env;
+
+    saved_env = env;
+    env = s;
+    
+    helper_fsave(ptr, data32);
+
+    env = saved_env;
+}
+
+void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32)
+{
+    CPUX86State *saved_env;
+
+    saved_env = env;
+    env = s;
+    
+    helper_frstor(ptr, data32);
+
     env = saved_env;
 }
 
@@ -324,30 +367,33 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
    the effective address of the memory exception. 'is_write' is 1 if a
    write caused the exception and otherwise 0'. 'old_set' is the
    signal set which should be restored */
-static inline int handle_cpu_signal(unsigned long pc,
-                                    unsigned long address,
-                                    int is_write,
-                                    sigset_t *old_set)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set)
 {
+    TranslationBlock *tb;
+    int ret;
+    uint32_t found_pc;
+    
 #if defined(DEBUG_SIGNAL)
     printf("qemu: SIGSEGV pc=0x%08lx address=%08lx wr=%d oldset=0x%08lx\n", 
            pc, address, is_write, *(unsigned long *)old_set);
 #endif
     /* XXX: locking issue */
     if (is_write && page_unprotect(address)) {
-        sigprocmask(SIG_SETMASK, old_set, NULL);
         return 1;
     }
-    if (pc >= (unsigned long)code_gen_buffer &&
-        pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
+    tb = tb_find_pc(pc);
+    if (tb) {
         /* the PC is inside the translated code. It means that we have
            a virtual CPU fault */
+        ret = cpu_x86_search_pc(tb, &found_pc, pc);
+        if (ret < 0)
+            return 0;
+        env->eip = found_pc - tb->cs_base;
+        env->cr2 = address;
         /* we restore the process signal mask as the sigreturn should
-           do it */
+           do it (XXX: use sigsetjmp) */
         sigprocmask(SIG_SETMASK, old_set, NULL);
-        /* XXX: need to compute virtual pc position by retranslating
-           code. The rest of the CPU state should be correct. */
-        env->cr2 = address;
         raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1));
         /* never comes here */
         return 1;
@@ -356,13 +402,13 @@ static inline int handle_cpu_signal(unsigned long pc,
     }
 }
 
+#if defined(__i386__)
+
 int cpu_x86_signal_handler(int host_signum, struct siginfo *info, 
                            void *puc)
 {
-#if defined(__i386__)
     struct ucontext *uc = puc;
     unsigned long pc;
-    sigset_t *pold_set;
     
 #ifndef REG_EIP
 /* for glibc 2.1 */
@@ -371,20 +417,23 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
 #define REG_TRAPNO TRAPNO
 #endif
     pc = uc->uc_mcontext.gregs[REG_EIP];
-    pold_set = &uc->uc_sigmask;
     return handle_cpu_signal(pc, (unsigned long)info->si_addr, 
                              uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? 
                              (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0,
-                             pold_set);
+                             &uc->uc_sigmask);
+}
+
 #elif defined(__powerpc)
+
+int cpu_x86_signal_handler(int host_signum, struct siginfo *info, 
+                           void *puc)
+{
     struct ucontext *uc = puc;
     struct pt_regs *regs = uc->uc_mcontext.regs;
     unsigned long pc;
-    sigset_t *pold_set;
     int is_write;
 
     pc = regs->nip;
-    pold_set = &uc->uc_sigmask;
     is_write = 0;
 #if 0
     /* ppc 4xx case */
@@ -395,9 +444,11 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
         is_write = 1;
 #endif
     return handle_cpu_signal(pc, (unsigned long)info->si_addr, 
-                             is_write, pold_set);
+                             is_write, &uc->uc_sigmask);
+}
+
 #else
+
 #error CPU specific signal handler needed
-    return 0;
+
 #endif
-}