]> git.proxmox.com Git - qemu.git/blobdiff - target-i386/helper.c
x86_64 fixes (initial patch by Filip Navara)
[qemu.git] / target-i386 / helper.c
index 9907f8e0b0d49c61501ff2b6104cec0cbd3609fb..2d241a14310d44b8621b1bbfe15144b116ac69da 100644 (file)
@@ -933,6 +933,7 @@ static void do_interrupt64(int intno, int is_int, int error_code,
     }
     env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
 }
+#endif
 
 void helper_syscall(int next_eip_addend)
 {
@@ -942,6 +943,7 @@ void helper_syscall(int next_eip_addend)
         raise_exception_err(EXCP06_ILLOP, 0);
     }
     selector = (env->star >> 32) & 0xffff;
+#ifdef TARGET_X86_64
     if (env->hflags & HF_LMA_MASK) {
         ECX = env->eip + next_eip_addend;
         env->regs[11] = compute_eflags();
@@ -962,7 +964,9 @@ void helper_syscall(int next_eip_addend)
             env->eip = env->lstar;
         else
             env->eip = env->cstar;
-    } else {
+    } else 
+#endif
+    {
         ECX = (uint32_t)(env->eip + next_eip_addend);
         
         cpu_x86_set_cpl(env, 0);
@@ -985,11 +989,15 @@ void helper_sysret(int dflag)
 {
     int cpl, selector;
 
+    if (!(env->efer & MSR_EFER_SCE)) {
+        raise_exception_err(EXCP06_ILLOP, 0);
+    }
     cpl = env->hflags & HF_CPL_MASK;
     if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
         raise_exception_err(EXCP0D_GPF, 0);
     }
     selector = (env->star >> 48) & 0xffff;
+#ifdef TARGET_X86_64
     if (env->hflags & HF_LMA_MASK) {
         if (dflag == 2) {
             cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, 
@@ -1012,9 +1020,12 @@ void helper_sysret(int dflag)
                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
                                DESC_W_MASK | DESC_A_MASK);
-        load_eflags((uint32_t)(env->regs[11]), 0xffffffff);
+        load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK | 
+                    IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
         cpu_x86_set_cpl(env, 3);
-    } else {
+    } else 
+#endif
+    {
         cpu_x86_load_seg_cache(env, R_CS, selector | 3, 
                                0, 0xffffffff, 
                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
@@ -1029,8 +1040,15 @@ void helper_sysret(int dflag)
         env->eflags |= IF_MASK;
         cpu_x86_set_cpl(env, 3);
     }
-}
+#ifdef USE_KQEMU
+    if (kqemu_is_ok(env)) {
+        if (env->hflags & HF_LMA_MASK)
+            CC_OP = CC_OP_EFLAGS;
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
 #endif
+}
 
 /* real mode interrupt */
 static void do_interrupt_real(int intno, int is_int, int error_code,
@@ -1209,7 +1227,7 @@ void helper_divl_EAX_T0(void)
     unsigned int den, q, r;
     uint64_t num;
     
-    num = EAX | ((uint64_t)EDX << 32);
+    num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
     den = T0;
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
@@ -1229,7 +1247,7 @@ void helper_idivl_EAX_T0(void)
     int den, q, r;
     int64_t num;
     
-    num = EAX | ((uint64_t)EDX << 32);
+    num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
     den = T0;
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
@@ -1264,9 +1282,21 @@ void helper_cmpxchg8b(void)
 
 void helper_cpuid(void)
 {
-    switch((uint32_t)EAX) {
+    uint32_t index;
+    index = (uint32_t)EAX;
+    
+    /* test if maximum index reached */
+    if (index & 0x80000000) {
+        if (index > env->cpuid_xlevel) 
+            index = env->cpuid_level;
+    } else {
+        if (index > env->cpuid_level) 
+            index = env->cpuid_level;
+    }
+        
+    switch(index) {
     case 0:
-        EAX = 2; /* max EAX index supported */
+        EAX = env->cpuid_level;
         EBX = env->cpuid_vendor1;
         EDX = env->cpuid_vendor2;
         ECX = env->cpuid_vendor3;
@@ -1274,19 +1304,18 @@ void helper_cpuid(void)
     case 1:
         EAX = env->cpuid_version;
         EBX = 0;
-        ECX = 0;
+        ECX = env->cpuid_ext_features;
         EDX = env->cpuid_features;
         break;
-    default:
+    case 2:
         /* cache info: needed for Pentium Pro compatibility */
         EAX = 0x410601;
         EBX = 0;
         ECX = 0;
         EDX = 0;
         break;
-#ifdef TARGET_X86_64
     case 0x80000000:
-        EAX = 0x80000008;
+        EAX = env->cpuid_xlevel;
         EBX = env->cpuid_vendor1;
         EDX = env->cpuid_vendor2;
         ECX = env->cpuid_vendor3;
@@ -1295,8 +1324,29 @@ void helper_cpuid(void)
         EAX = env->cpuid_features;
         EBX = 0;
         ECX = 0;
-        /* long mode + syscall/sysret features */
-        EDX = (env->cpuid_features & 0x0183F3FF) | (1 << 29) | (1 << 11);
+        EDX = env->cpuid_ext2_features;
+        break;
+    case 0x80000002:
+    case 0x80000003:
+    case 0x80000004:
+        EAX = env->cpuid_model[(index - 0x80000002) * 4 + 0];
+        EBX = env->cpuid_model[(index - 0x80000002) * 4 + 1];
+        ECX = env->cpuid_model[(index - 0x80000002) * 4 + 2];
+        EDX = env->cpuid_model[(index - 0x80000002) * 4 + 3];
+        break;
+    case 0x80000005:
+        /* cache info (L1 cache) */
+        EAX = 0x01ff01ff;
+        EBX = 0x01ff01ff;
+        ECX = 0x40020140;
+        EDX = 0x40020140;
+        break;
+    case 0x80000006:
+        /* cache info (L2 cache) */
+        EAX = 0;
+        EBX = 0x42004200;
+        ECX = 0x02008140;
+        EDX = 0;
         break;
     case 0x80000008:
         /* virtual & phys address size in low 2 bytes. */
@@ -1305,7 +1355,13 @@ void helper_cpuid(void)
         ECX = 0;
         EDX = 0;
         break;
-#endif
+    default:
+        /* reserved values: zero */
+        EAX = 0;
+        EBX = 0;
+        ECX = 0;
+        EDX = 0;
+        break;
     }
 }
 
@@ -1341,6 +1397,37 @@ void helper_enter_level(int level, int data32)
     }
 }
 
+#ifdef TARGET_X86_64
+void helper_enter64_level(int level, int data64)
+{
+    target_ulong esp, ebp;
+    ebp = EBP;
+    esp = ESP;
+
+    if (data64) {
+        /* 64 bit */
+        esp -= 8;
+        while (--level) {
+            esp -= 8;
+            ebp -= 8;
+            stq(esp, ldq(ebp));
+        }
+        esp -= 8;
+        stq(esp, T1);
+    } else {
+        /* 16 bit */
+        esp -= 2;
+        while (--level) {
+            esp -= 2;
+            ebp -= 2;
+            stw(esp, lduw(ebp));
+        }
+        esp -= 2;
+        stw(esp, T1);
+    }
+}
+#endif
+
 void helper_lldt_T0(void)
 {
     int selector;
@@ -1452,9 +1539,14 @@ void load_seg(int seg_reg, int selector)
     target_ulong ptr;
 
     selector &= 0xffff;
+    cpl = env->hflags & HF_CPL_MASK;
     if ((selector & 0xfffc) == 0) {
         /* null selector case */
-        if (seg_reg == R_SS)
+        if (seg_reg == R_SS
+#ifdef TARGET_X86_64
+            && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
+#endif
+            )
             raise_exception_err(EXCP0D_GPF, 0);
         cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
     } else {
@@ -1474,7 +1566,6 @@ void load_seg(int seg_reg, int selector)
             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
         rpl = selector & 3;
         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
-        cpl = env->hflags & HF_CPL_MASK;
         if (seg_reg == R_SS) {
             /* must be writable segment */
             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
@@ -1518,11 +1609,11 @@ void load_seg(int seg_reg, int selector)
 }
 
 /* protected mode jump */
-void helper_ljmp_protected_T0_T1(int next_eip)
+void helper_ljmp_protected_T0_T1(int next_eip_addend)
 {
     int new_cs, gate_cs, type;
     uint32_t e1, e2, cpl, dpl, rpl, limit;
-    target_ulong new_eip;
+    target_ulong new_eip, next_eip;
     
     new_cs = T0;
     new_eip = T1;
@@ -1550,7 +1641,8 @@ void helper_ljmp_protected_T0_T1(int next_eip)
         if (!(e2 & DESC_P_MASK))
             raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
         limit = get_seg_limit(e1, e2);
-        if (new_eip > limit)
+        if (new_eip > limit && 
+            !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
             raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
         cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
                        get_seg_base(e1, e2), limit, e2);
@@ -1567,6 +1659,7 @@ void helper_ljmp_protected_T0_T1(int next_eip)
         case 5: /* task gate */
             if (dpl < cpl || dpl < rpl)
                 raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+            next_eip = env->eip + next_eip_addend;
             switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
             break;
         case 4: /* 286 call gate */
@@ -1632,16 +1725,17 @@ void helper_lcall_real_T0_T1(int shift, int next_eip)
 }
 
 /* protected mode call */
-void helper_lcall_protected_T0_T1(int shift, int next_eip)
+void helper_lcall_protected_T0_T1(int shift, int next_eip_addend)
 {
     int new_cs, new_eip, new_stack, i;
     uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
     uint32_t ss, ss_e1, ss_e2, sp, type, ss_dpl, sp_mask;
     uint32_t val, limit, old_sp_mask;
-    target_ulong ssp, old_ssp;
+    target_ulong ssp, old_ssp, next_eip;
     
     new_cs = T0;
     new_eip = T1;
+    next_eip = env->eip + next_eip_addend;
 #ifdef DEBUG_PCALL
     if (loglevel & CPU_LOG_PCALL) {
         fprintf(logfile, "lcall %04x:%08x s=%d\n",
@@ -1678,25 +1772,43 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
         if (!(e2 & DESC_P_MASK))
             raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
 
-        sp = ESP;
-        sp_mask = get_sp_mask(env->segs[R_SS].flags);
-        ssp = env->segs[R_SS].base;
-        if (shift) {
-            PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
-            PUSHL(ssp, sp, sp_mask, next_eip);
-        } else {
-            PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
-            PUSHW(ssp, sp, sp_mask, next_eip);
+#ifdef TARGET_X86_64
+        /* XXX: check 16/32 bit cases in long mode */
+        if (shift == 2) {
+            target_ulong rsp;
+            /* 64 bit case */
+            rsp = ESP;
+            PUSHQ(rsp, env->segs[R_CS].selector);
+            PUSHQ(rsp, next_eip);
+            /* from this point, not restartable */
+            ESP = rsp;
+            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+                                   get_seg_base(e1, e2), 
+                                   get_seg_limit(e1, e2), e2);
+            EIP = new_eip;
+        } else 
+#endif
+        {
+            sp = ESP;
+            sp_mask = get_sp_mask(env->segs[R_SS].flags);
+            ssp = env->segs[R_SS].base;
+            if (shift) {
+                PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+                PUSHL(ssp, sp, sp_mask, next_eip);
+            } else {
+                PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+                PUSHW(ssp, sp, sp_mask, next_eip);
+            }
+            
+            limit = get_seg_limit(e1, e2);
+            if (new_eip > limit)
+                raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+            /* from this point, not restartable */
+            ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+                                   get_seg_base(e1, e2), limit, e2);
+            EIP = new_eip;
         }
-        
-        limit = get_seg_limit(e1, e2);
-        if (new_eip > limit)
-            raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
-        /* from this point, not restartable */
-        ESP = (ESP & ~sp_mask) | (sp & sp_mask);
-        cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
-                       get_seg_base(e1, e2), limit, e2);
-        EIP = new_eip;
     } else {
         /* check gate type */
         type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
@@ -1823,6 +1935,12 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
         ESP = (ESP & ~sp_mask) | (sp & sp_mask);
         EIP = offset;
     }
+#ifdef USE_KQEMU
+    if (kqemu_is_ok(env)) {
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
+#endif
 }
 
 /* real and vm86 mode iret */
@@ -1890,6 +2008,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
 #endif
         sp_mask = get_sp_mask(env->segs[R_SS].flags);
     sp = ESP;
+    /* XXX: ssp is zero in 64 bit ? */
     ssp = env->segs[R_SS].base;
     new_eflags = 0; /* avoid warning */
 #ifdef TARGET_X86_64
@@ -1949,7 +2068,8 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
         raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
     
     sp += addend;
-    if (rpl == cpl && !(env->hflags & HF_CS64_MASK)) {
+    if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || 
+                       ((env->hflags & HF_CS64_MASK) && !is_iret))) {
         /* return to same priledge level */
         cpu_x86_load_seg_cache(env, R_CS, new_cs, 
                        get_seg_base(e1, e2),
@@ -1980,13 +2100,20 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
                     new_ss, new_esp);
         }
 #endif
-        if ((env->hflags & HF_LMA_MASK) && (new_ss & 0xfffc) == 0) {
-            /* NULL ss is allowed in long mode */
-            cpu_x86_load_seg_cache(env, R_SS, new_ss, 
-                                   0, 0xffffffff,
-                                   DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
-                                   DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
-                                   DESC_W_MASK | DESC_A_MASK);
+        if ((new_ss & 0xfffc) == 0) {
+#ifdef TARGET_X86_64
+            /* NULL ss is allowed in long mode if cpl != 3*/
+            if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
+                cpu_x86_load_seg_cache(env, R_SS, new_ss, 
+                                       0, 0xffffffff,
+                                       DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+                                       DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
+                                       DESC_W_MASK | DESC_A_MASK);
+            } else 
+#endif
+            {
+                raise_exception_err(EXCP0D_GPF, 0);
+            }
         } else {
             if ((new_ss & 3) != rpl)
                 raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
@@ -2091,11 +2218,24 @@ void helper_iret_protected(int shift, int next_eip)
     } else {
         helper_ret_protected(shift, 1, 0);
     }
+#ifdef USE_KQEMU
+    if (kqemu_is_ok(env)) {
+        CC_OP = CC_OP_EFLAGS;
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
+#endif
 }
 
 void helper_lret_protected(int shift, int addend)
 {
     helper_ret_protected(shift, 0, addend);
+#ifdef USE_KQEMU
+    if (kqemu_is_ok(env)) {
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
+#endif
 }
 
 void helper_sysenter(void)
@@ -2140,10 +2280,17 @@ void helper_sysexit(void)
                            DESC_W_MASK | DESC_A_MASK);
     ESP = ECX;
     EIP = EDX;
+#ifdef USE_KQEMU
+    if (kqemu_is_ok(env)) {
+        env->exception_index = -1;
+        cpu_loop_exit();
+    }
+#endif
 }
 
 void helper_movl_crN_T0(int reg)
 {
+#if !defined(CONFIG_USER_ONLY) 
     switch(reg) {
     case 0:
         cpu_x86_update_cr0(env, T0);
@@ -2154,10 +2301,14 @@ void helper_movl_crN_T0(int reg)
     case 4:
         cpu_x86_update_cr4(env, T0);
         break;
+    case 8:
+        cpu_set_apic_tpr(env, T0);
+        break;
     default:
         env->cr[reg] = T0;
         break;
     }
+#endif
 }
 
 /* XXX: do more */
@@ -2166,7 +2317,7 @@ void helper_movl_drN_T0(int reg)
     env->dr[reg] = T0;
 }
 
-void helper_invlpg(unsigned int addr)
+void helper_invlpg(target_ulong addr)
 {
     cpu_x86_flush_tlb(env, addr);
 }
@@ -2208,16 +2359,29 @@ void helper_wrmsr(void)
     case MSR_IA32_APICBASE:
         cpu_set_apic_base(env, val);
         break;
-#ifdef TARGET_X86_64
     case MSR_EFER:
-#define MSR_EFER_UPDATE_MASK (MSR_EFER_SCE | MSR_EFER_LME | \
-                              MSR_EFER_NXE | MSR_EFER_FFXSR)
-        env->efer = (env->efer & ~MSR_EFER_UPDATE_MASK) | 
-            (val & MSR_EFER_UPDATE_MASK);
+        {
+            uint64_t update_mask;
+            update_mask = 0;
+            if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
+                update_mask |= MSR_EFER_SCE;
+            if (env->cpuid_ext2_features & CPUID_EXT2_LM)
+                update_mask |= MSR_EFER_LME;
+            if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
+                update_mask |= MSR_EFER_FFXSR;
+            if (env->cpuid_ext2_features & CPUID_EXT2_NX)
+                update_mask |= MSR_EFER_NXE;
+            env->efer = (env->efer & ~update_mask) | 
+            (val & update_mask);
+        }
         break;
     case MSR_STAR:
         env->star = val;
         break;
+    case MSR_PAT:
+        env->pat = val;
+        break;
+#ifdef TARGET_X86_64
     case MSR_LSTAR:
         env->lstar = val;
         break;
@@ -2259,13 +2423,16 @@ void helper_rdmsr(void)
     case MSR_IA32_APICBASE:
         val = cpu_get_apic_base(env);
         break;
-#ifdef TARGET_X86_64
     case MSR_EFER:
         val = env->efer;
         break;
     case MSR_STAR:
         val = env->star;
         break;
+    case MSR_PAT:
+        val = env->pat;
+        break;
+#ifdef TARGET_X86_64
     case MSR_LSTAR:
         val = env->lstar;
         break;
@@ -2298,13 +2465,13 @@ void helper_rdmsr(void)
 void helper_lsl(void)
 {
     unsigned int selector, limit;
-    uint32_t e1, e2;
+    uint32_t e1, e2, eflags;
     int rpl, dpl, cpl, type;
 
-    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    eflags = cc_table[CC_OP].compute_all();
     selector = T0 & 0xffff;
     if (load_segment(&e1, &e2, selector) != 0)
-        return;
+        goto fail;
     rpl = selector & 3;
     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
     cpl = env->hflags & HF_CPL_MASK;
@@ -2313,7 +2480,7 @@ void helper_lsl(void)
             /* conforming */
         } else {
             if (dpl < cpl || dpl < rpl)
-                return;
+                goto fail;
         }
     } else {
         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
@@ -2325,28 +2492,31 @@ void helper_lsl(void)
         case 11:
             break;
         default:
-            return;
+            goto fail;
         }
-        if (dpl < cpl || dpl < rpl)
+        if (dpl < cpl || dpl < rpl) {
+        fail:
+            CC_SRC = eflags & ~CC_Z;
             return;
+        }
     }
     limit = get_seg_limit(e1, e2);
     T1 = limit;
-    CC_SRC |= CC_Z;
+    CC_SRC = eflags | CC_Z;
 }
 
 void helper_lar(void)
 {
     unsigned int selector;
-    uint32_t e1, e2;
+    uint32_t e1, e2, eflags;
     int rpl, dpl, cpl, type;
 
-    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    eflags = cc_table[CC_OP].compute_all();
     selector = T0 & 0xffff;
     if ((selector & 0xfffc) == 0)
-        return;
+        goto fail;
     if (load_segment(&e1, &e2, selector) != 0)
-        return;
+        goto fail;
     rpl = selector & 3;
     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
     cpl = env->hflags & HF_CPL_MASK;
@@ -2355,7 +2525,7 @@ void helper_lar(void)
             /* conforming */
         } else {
             if (dpl < cpl || dpl < rpl)
-                return;
+                goto fail;
         }
     } else {
         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
@@ -2370,72 +2540,81 @@ void helper_lar(void)
         case 12:
             break;
         default:
-            return;
+            goto fail;
         }
-        if (dpl < cpl || dpl < rpl)
+        if (dpl < cpl || dpl < rpl) {
+        fail:
+            CC_SRC = eflags & ~CC_Z;
             return;
+        }
     }
     T1 = e2 & 0x00f0ff00;
-    CC_SRC |= CC_Z;
+    CC_SRC = eflags | CC_Z;
 }
 
 void helper_verr(void)
 {
     unsigned int selector;
-    uint32_t e1, e2;
+    uint32_t e1, e2, eflags;
     int rpl, dpl, cpl;
 
-    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    eflags = cc_table[CC_OP].compute_all();
     selector = T0 & 0xffff;
     if ((selector & 0xfffc) == 0)
-        return;
+        goto fail;
     if (load_segment(&e1, &e2, selector) != 0)
-        return;
+        goto fail;
     if (!(e2 & DESC_S_MASK))
-        return;
+        goto fail;
     rpl = selector & 3;
     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
     cpl = env->hflags & HF_CPL_MASK;
     if (e2 & DESC_CS_MASK) {
         if (!(e2 & DESC_R_MASK))
-            return;
+            goto fail;
         if (!(e2 & DESC_C_MASK)) {
             if (dpl < cpl || dpl < rpl)
-                return;
+                goto fail;
         }
     } else {
-        if (dpl < cpl || dpl < rpl)
+        if (dpl < cpl || dpl < rpl) {
+        fail:
+            CC_SRC = eflags & ~CC_Z;
             return;
+        }
     }
-    CC_SRC |= CC_Z;
+    CC_SRC = eflags | CC_Z;
 }
 
 void helper_verw(void)
 {
     unsigned int selector;
-    uint32_t e1, e2;
+    uint32_t e1, e2, eflags;
     int rpl, dpl, cpl;
 
-    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    eflags = cc_table[CC_OP].compute_all();
     selector = T0 & 0xffff;
     if ((selector & 0xfffc) == 0)
-        return;
+        goto fail;
     if (load_segment(&e1, &e2, selector) != 0)
-        return;
+        goto fail;
     if (!(e2 & DESC_S_MASK))
-        return;
+        goto fail;
     rpl = selector & 3;
     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
     cpl = env->hflags & HF_CPL_MASK;
     if (e2 & DESC_CS_MASK) {
-        return;
+        goto fail;
     } else {
         if (dpl < cpl || dpl < rpl)
+            goto fail;
+        if (!(e2 & DESC_W_MASK)) {
+        fail:
+            CC_SRC = eflags & ~CC_Z;
             return;
-        if (!(e2 & DESC_W_MASK))
-            return;
+        }
     }
-    CC_SRC |= CC_Z;
+    CC_SRC = eflags | CC_Z;
 }
 
 /* FPU helpers */
@@ -2503,13 +2682,11 @@ void helper_fbld_ST0_A0(void)
 
 void helper_fbst_ST0_A0(void)
 {
-    CPU86_LDouble tmp;
     int v;
     target_ulong mem_ref, mem_end;
     int64_t val;
 
-    tmp = rint(ST0);
-    val = (int64_t)tmp;
+    val = floatx_to_int64(ST0, &env->fp_status);
     mem_ref = A0;
     mem_end = mem_ref + 9;
     if (val < 0) {
@@ -2702,29 +2879,7 @@ void helper_fsincos(void)
 
 void helper_frndint(void)
 {
-    CPU86_LDouble a;
-
-    a = ST0;
-#ifdef __arm__
-    switch(env->fpuc & RC_MASK) {
-    default:
-    case RC_NEAR:
-        asm("rndd %0, %1" : "=f" (a) : "f"(a));
-        break;
-    case RC_DOWN:
-        asm("rnddm %0, %1" : "=f" (a) : "f"(a));
-        break;
-    case RC_UP:
-        asm("rnddp %0, %1" : "=f" (a) : "f"(a));
-        break;
-    case RC_CHOP:
-        asm("rnddz %0, %1" : "=f" (a) : "f"(a));
-        break;
-    }
-#else
-    a = rint(a);
-#endif
-    ST0 = a;
+    ST0 = floatx_round_to_int(ST0, &env->fp_status);
 }
 
 void helper_fscale(void)
@@ -2807,7 +2962,6 @@ void helper_fstenv(target_ulong ptr, int data32)
             tmp.d = env->fpregs[i].d;
             exp = EXPD(tmp);
             mant = MANTD(tmp);
-            printf("mant=%llx exp=%x\n", mant, exp);
             if (exp == 0 && mant == 0) {
                 /* zero */
                fptag |= 1;
@@ -2916,11 +3070,11 @@ void helper_fxsave(target_ulong ptr, int data64)
     fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
     fptag = 0;
     for(i = 0; i < 8; i++) {
-        fptag |= ((!env->fptags[(env->fpstt + i) & 7]) << i);
+        fptag |= (env->fptags[i] << i);
     }
     stw(ptr, env->fpuc);
     stw(ptr + 2, fpus);
-    stw(ptr + 4, fptag);
+    stw(ptr + 4, fptag ^ 0xff);
 
     addr = ptr + 0x20;
     for(i = 0;i < 8; i++) {
@@ -2932,7 +3086,7 @@ void helper_fxsave(target_ulong ptr, int data64)
     if (env->cr[4] & CR4_OSFXSR_MASK) {
         /* XXX: finish it */
         stl(ptr + 0x18, env->mxcsr); /* mxcsr */
-        stl(ptr + 0x1c, 0); /* mxcsr_mask */
+        stl(ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
         nb_xmm_regs = 8 << data64;
         addr = ptr + 0xa0;
         for(i = 0; i < nb_xmm_regs; i++) {
@@ -2951,12 +3105,12 @@ void helper_fxrstor(target_ulong ptr, int data64)
 
     env->fpuc = lduw(ptr);
     fpus = lduw(ptr + 2);
-    fptag = ldub(ptr + 4);
+    fptag = lduw(ptr + 4);
     env->fpstt = (fpus >> 11) & 7;
     env->fpus = fpus & ~0x3800;
     fptag ^= 0xff;
     for(i = 0;i < 8; i++) {
-        env->fptags[(env->fpstt + i) & 7] = ((fptag >> i) & 1);
+        env->fptags[i] = ((fptag >> i) & 1);
     }
 
     addr = ptr + 0x20;
@@ -2967,7 +3121,7 @@ void helper_fxrstor(target_ulong ptr, int data64)
     }
 
     if (env->cr[4] & CR4_OSFXSR_MASK) {
-        /* XXX: finish it, endianness */
+        /* XXX: finish it */
         env->mxcsr = ldl(ptr + 0x18);
         //ldl(ptr + 0x1c);
         nb_xmm_regs = 8 << data64;
@@ -3134,7 +3288,7 @@ static void div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
     }
 }
 
-static void idiv64(uint64_t *plow, uint64_t *phigh, uint64_t b)
+static void idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
 {
     int sa, sb;
     sa = ((int64_t)*phigh < 0);
@@ -3146,7 +3300,7 @@ static void idiv64(uint64_t *plow, uint64_t *phigh, uint64_t b)
     div64(plow, phigh, b);
     if (sa ^ sb)
         *plow = - *plow;
-    if (sb)
+    if (sa)
         *phigh = - *phigh;
 }
 
@@ -3210,12 +3364,6 @@ void helper_idivq_EAX_T0(void)
 
 #endif
 
-/* XXX: do it */
-int fpu_isnan(double a)
-{
-    return 0;
-}
-
 float approx_rsqrt(float a)
 {
     return 1.0 / sqrt(a);
@@ -3226,6 +3374,43 @@ float approx_rcp(float a)
     return 1.0 / a;
 }
 
+void update_fp_status(void)
+{
+    int rnd_type;
+
+    /* set rounding mode */
+    switch(env->fpuc & RC_MASK) {
+    default:
+    case RC_NEAR:
+        rnd_type = float_round_nearest_even;
+        break;
+    case RC_DOWN:
+        rnd_type = float_round_down;
+        break;
+    case RC_UP:
+        rnd_type = float_round_up;
+        break;
+    case RC_CHOP:
+        rnd_type = float_round_to_zero;
+        break;
+    }
+    set_float_rounding_mode(rnd_type, &env->fp_status);
+#ifdef FLOATX80
+    switch((env->fpuc >> 8) & 3) {
+    case 0:
+        rnd_type = 32;
+        break;
+    case 2:
+        rnd_type = 64;
+        break;
+    case 3:
+    default:
+        rnd_type = 80;
+        break;
+    }
+    set_floatx80_rounding_precision(rnd_type, &env->fp_status);
+#endif
+}
 
 #if !defined(CONFIG_USER_ONLY)