fixed popf TF flag bug (should never hapen in user code except in test-i386!)
[qemu] / exec-i386.c
index b75c6c7..7575123 100644 (file)
 #include "disas.h"
 
 //#define DEBUG_EXEC
-#define DEBUG_FLUSH
 //#define DEBUG_SIGNAL
 
 /* main execution loop */
 
-/* maximum total translate dcode allocated */
-#define CODE_GEN_BUFFER_SIZE     (2048 * 1024)
-//#define CODE_GEN_BUFFER_SIZE     (128 * 1024)
-#define CODE_GEN_MAX_SIZE        65536
-#define CODE_GEN_ALIGN           16 /* must be >= of the size of a icache line */
-
-/* threshold to flush the translated code buffer */
-#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE)
-
-#define CODE_GEN_MAX_BLOCKS    (CODE_GEN_BUFFER_SIZE / 64)
-#define CODE_GEN_HASH_BITS     15
-#define CODE_GEN_HASH_SIZE     (1 << CODE_GEN_HASH_BITS)
-
-typedef struct TranslationBlock {
-    unsigned long pc;   /* simulated PC corresponding to this block (EIP + CS base) */
-    unsigned long cs_base; /* CS base for this block */
-    unsigned int flags; /* flags defining in which context the code was generated */
-    uint8_t *tc_ptr;    /* pointer to the translated code */
-    struct TranslationBlock *hash_next; /* next matching block */
-} TranslationBlock;
-
-TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
-TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE];
-int nb_tbs;
-
-uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
-uint8_t *code_gen_ptr;
-
 /* thread support */
 
-#ifdef __powerpc__
-static inline int testandset (int *p)
-{
-    int ret;
-    __asm__ __volatile__ (
-                          "0:    lwarx %0,0,%1 ;"
-                          "      xor. %0,%3,%0;"
-                          "      bne 1f;"
-                          "      stwcx. %2,0,%1;"
-                          "      bne- 0b;"
-                          "1:    "
-                          : "=&r" (ret)
-                          : "r" (p), "r" (1), "r" (0)
-                          : "cr0", "memory");
-    return ret;
-}
-#endif
-
-#ifdef __i386__
-static inline int testandset (int *p)
-{
-    char ret;
-    long int readval;
-    
-    __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
-                          : "=q" (ret), "=m" (*p), "=a" (readval)
-                          : "r" (1), "m" (*p), "a" (0)
-                          : "memory");
-    return ret;
-}
-#endif
-
-#ifdef __s390__
-static inline int testandset (int *p)
-{
-    int ret;
-
-    __asm__ __volatile__ ("0: cs    %0,%1,0(%2)\n"
-                         "   jl    0b"
-                         : "=&d" (ret)
-                         : "r" (1), "a" (p), "0" (*p) 
-                         : "cc", "memory" );
-    return ret;
-}
-#endif
-
-int global_cpu_lock = 0;
+spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
 
 void cpu_lock(void)
 {
-    while (testandset(&global_cpu_lock));
+    spin_lock(&global_cpu_lock);
 }
 
 void cpu_unlock(void)
 {
-    global_cpu_lock = 0;
+    spin_unlock(&global_cpu_lock);
 }
 
 /* exception support */
 /* NOTE: not static to force relocation generation by GCC */
-void raise_exception(int exception_index)
+void raise_exception_err(int exception_index, int error_code)
 {
     /* NOTE: the register at this point must be saved by hand because
        longjmp restore them */
+#ifdef __sparc__
+       /* We have to stay in the same register window as our caller,
+        * thus this trick.
+        */
+       __asm__ __volatile__("restore\n\t"
+                            "mov\t%o0, %i0");
+#endif
 #ifdef reg_EAX
     env->regs[R_EAX] = EAX;
 #endif
@@ -145,130 +77,14 @@ void raise_exception(int exception_index)
     env->regs[R_EDI] = EDI;
 #endif
     env->exception_index = exception_index;
+    env->error_code = error_code;
     longjmp(env->jmp_env, 1);
 }
 
-#if defined(DEBUG_EXEC)
-static const char *cc_op_str[] = {
-    "DYNAMIC",
-    "EFLAGS",
-    "MUL",
-    "ADDB",
-    "ADDW",
-    "ADDL",
-    "ADCB",
-    "ADCW",
-    "ADCL",
-    "SUBB",
-    "SUBW",
-    "SUBL",
-    "SBBB",
-    "SBBW",
-    "SBBL",
-    "LOGICB",
-    "LOGICW",
-    "LOGICL",
-    "INCB",
-    "INCW",
-    "INCL",
-    "DECB",
-    "DECW",
-    "DECL",
-    "SHLB",
-    "SHLW",
-    "SHLL",
-    "SARB",
-    "SARW",
-    "SARL",
-};
-
-static void cpu_x86_dump_state(FILE *f)
-{
-    int eflags;
-    eflags = cc_table[CC_OP].compute_all();
-    eflags |= (DF & DF_MASK);
-    fprintf(f, 
-            "EAX=%08x EBX=%08X ECX=%08x EDX=%08x\n"
-            "ESI=%08x EDI=%08X EBP=%08x ESP=%08x\n"
-            "CCS=%08x CCD=%08x CCO=%-8s EFL=%c%c%c%c%c%c%c\n"
-            "EIP=%08x\n",
-            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], env->regs[R_ESP], 
-            env->cc_src, env->cc_dst, cc_op_str[env->cc_op],
-            eflags & DF_MASK ? 'D' : '-',
-            eflags & CC_O ? 'O' : '-',
-            eflags & CC_S ? 'S' : '-',
-            eflags & CC_Z ? 'Z' : '-',
-            eflags & CC_A ? 'A' : '-',
-            eflags & CC_P ? 'P' : '-',
-            eflags & CC_C ? 'C' : '-',
-            env->eip);
-#if 1
-    fprintf(f, "ST0=%f ST1=%f ST2=%f ST3=%f\n", 
-            (double)ST0, (double)ST1, (double)ST(2), (double)ST(3));
-#endif
-}
-
-#endif
-
-void cpu_x86_tblocks_init(void)
-{
-    if (!code_gen_ptr) {
-        code_gen_ptr = code_gen_buffer;
-    }
-}
-
-/* flush all the translation blocks */
-static void tb_flush(void)
-{
-    int i;
-#ifdef DEBUG_FLUSH
-    printf("gemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", 
-           code_gen_ptr - code_gen_buffer, 
-           nb_tbs, 
-           (code_gen_ptr - code_gen_buffer) / nb_tbs);
-#endif
-    nb_tbs = 0;
-    for(i = 0;i < CODE_GEN_HASH_SIZE; i++)
-        tb_hash[i] = NULL;
-    code_gen_ptr = code_gen_buffer;
-    /* XXX: flush processor icache at this point */
-}
-
-/* find a translation block in the translation cache. If not found,
-   return NULL and the pointer to the last element of the list in pptb */
-static inline TranslationBlock *tb_find(TranslationBlock ***pptb,
-                                        unsigned long pc, 
-                                        unsigned long cs_base,
-                                        unsigned int flags)
-{
-    TranslationBlock **ptb, *tb;
-    unsigned int h;
-    h = pc & (CODE_GEN_HASH_SIZE - 1);
-    ptb = &tb_hash[h];
-    for(;;) {
-        tb = *ptb;
-        if (!tb)
-            break;
-        if (tb->pc == pc && tb->cs_base == cs_base && tb->flags == flags)
-            return tb;
-        ptb = &tb->hash_next;
-    }
-    *pptb = ptb;
-    return NULL;
-}
-
-/* allocate a new translation block. flush the translation buffer if
-   too many translation blocks or too much generated code */
-static inline TranslationBlock *tb_alloc(void)
+/* short cut if error_code is 0 or not present */
+void raise_exception(int exception_index)
 {
-    TranslationBlock *tb;
-    if (nb_tbs >= CODE_GEN_MAX_BLOCKS || 
-        (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE)
-        tb_flush();
-    tb = &tbs[nb_tbs++];
-    return tb;
+    raise_exception_err(exception_index, 0);
 }
 
 int cpu_x86_exec(CPUX86State *env1)
@@ -299,7 +115,7 @@ int cpu_x86_exec(CPUX86State *env1)
 #ifdef reg_EDI
     int saved_EDI;
 #endif
-    int code_gen_size, ret;
+    int code_gen_size, ret, code_size;
     void (*gen_func)(void);
     TranslationBlock *tb, **ptb;
     uint8_t *tc_ptr, *cs_base, *pc;
@@ -350,7 +166,7 @@ int cpu_x86_exec(CPUX86State *env1)
     CC_OP = CC_OP_EFLAGS;
     env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
     env->interrupt_request = 0;
-    
+
     /* prepare setjmp context for exception handling */
     if (setjmp(env->jmp_env) == 0) {
         for(;;) {
@@ -359,7 +175,19 @@ int cpu_x86_exec(CPUX86State *env1)
             }
 #ifdef DEBUG_EXEC
             if (loglevel) {
-                cpu_x86_dump_state(logfile);
+                /* XXX: save all volatile state in cpu state */
+                /* restore flags in standard format */
+                env->regs[R_EAX] = EAX;
+                env->regs[R_EBX] = EBX;
+                env->regs[R_ECX] = ECX;
+                env->regs[R_EDX] = EDX;
+                env->regs[R_ESI] = ESI;
+                env->regs[R_EDI] = EDI;
+                env->regs[R_EBP] = EBP;
+                env->regs[R_ESP] = ESP;
+                env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+                cpu_x86_dump_state(env, logfile, 0);
+                env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
             }
 #endif
             /* we compute the CPU state. We assume it will not
@@ -370,44 +198,61 @@ int cpu_x86_exec(CPUX86State *env1)
                        (unsigned long)env->seg_cache[R_ES].base |
                        (unsigned long)env->seg_cache[R_SS].base) != 0) << 
                 GEN_FLAG_ADDSEG_SHIFT;
-            flags |= (env->eflags & VM_MASK) >> (17 - GEN_FLAG_VM_SHIFT);
+            if (!(env->eflags & VM_MASK)) {
+                flags |= (env->segs[R_CS] & 3) << GEN_FLAG_CPL_SHIFT;
+            } else {
+                /* NOTE: a dummy CPL is kept */
+                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);
             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 */
-                /* XXX: very inefficient: we lock all the cpus when
-                   generating code */
-                cpu_lock();
+                /* very inefficient but safe: we lock all the cpus
+                   when generating code */
+                spin_lock(&tb_lock);
                 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_gen_size, pc, cs_base, flags,
+                                       &code_size);
                 /* if invalid instruction, signal it */
                 if (ret != 0) {
-                    cpu_unlock();
+                    spin_unlock(&tb_lock);
                     raise_exception(EXCP06_ILLOP);
                 }
-                tb = tb_alloc();
+                tb = tb_alloc((unsigned long)pc, code_size);
                 *ptb = tb;
-                tb->pc = (unsigned long)pc;
                 tb->cs_base = (unsigned long)cs_base;
                 tb->flags = flags;
                 tb->tc_ptr = tc_ptr;
                 tb->hash_next = NULL;
                 code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
-                cpu_unlock();
+                spin_unlock(&tb_lock);
             }
+#ifdef DEBUG_EXEC
            if (loglevel) {
                fprintf(logfile, "Trace 0x%08lx [0x%08lx] %s\n",
                        (long)tb->tc_ptr, (long)tb->pc,
                        lookup_symbol((void *)tb->pc));
-               fflush(logfile);
            }
+#endif
             /* execute the generated code */
             tc_ptr = tb->tc_ptr;
             gen_func = (void *)tc_ptr;
+#ifdef __sparc__
+           __asm__ __volatile__("call  %0\n\t"
+                                " mov  %%o7,%%i0"
+                                : /* no outputs */
+                                : "r" (gen_func)
+                                : "i0", "i1", "i2", "i3", "i4", "i5");
+#else
             gen_func();
+#endif
         }
     }
     ret = env->exception_index;
@@ -475,13 +320,21 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
 #include <signal.h>
 #include <sys/ucontext.h>
 
-static inline int handle_cpu_signal(unsigned long pc,
-                                    sigset_t *old_set)
+/* 'pc' is the host PC at which the exception was raised. 'address' is
+   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)
 {
-#ifdef DEBUG_SIGNAL
-    printf("gemu: SIGSEGV pc=0x%08lx oldset=0x%08lx\n", 
-           pc, *(unsigned long *)old_set);
+#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)) {
+        return 1;
+    }
     if (pc >= (unsigned long)code_gen_buffer &&
         pc < (unsigned long)code_gen_buffer + CODE_GEN_BUFFER_SIZE) {
         /* the PC is inside the translated code. It means that we have
@@ -491,7 +344,8 @@ static inline int handle_cpu_signal(unsigned long pc,
         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. */
-        raise_exception(EXCP0D_GPF);
+        env->cr2 = address;
+        raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1));
         /* never comes here */
         return 1;
     } else {
@@ -499,23 +353,53 @@ 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 */
-#define REG_EIP EIP
+#define REG_EIP    EIP
+#define REG_ERR    ERR
+#define REG_TRAPNO TRAPNO
 #endif
     pc = uc->uc_mcontext.gregs[REG_EIP];
-    pold_set = &uc->uc_sigmask;
-    return handle_cpu_signal(pc, pold_set);
+    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,
+                             &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;
+    int is_write;
+
+    pc = regs->nip;
+    is_write = 0;
+#if 0
+    /* ppc 4xx case */
+    if (regs->dsisr & 0x00800000)
+        is_write = 1;
 #else
-#warning No CPU specific signal handler: cannot handle target SIGSEGV events
-    return 0;
+    if (regs->trap != 0x400 && (regs->dsisr & 0x02000000))
+        is_write = 1;
 #endif
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr, 
+                             is_write, &uc->uc_sigmask);
 }
+
+#else
+
+#error CPU specific signal handler needed
+
+#endif