diff options
Diffstat (limited to 'vl.c')
-rw-r--r-- | vl.c | 117 |
1 files changed, 95 insertions, 22 deletions
@@ -2082,15 +2082,14 @@ static void cpu_get_seg(QEMUFile *f, SegmentCache *dt) void cpu_save(QEMUFile *f, void *opaque) { CPUState *env = opaque; - uint16_t fptag, fpus, fpuc; + uint16_t fptag, fpus, fpuc, fpregs_format; uint32_t hflags; int i; - + for(i = 0; i < CPU_NB_REGS; i++) qemu_put_betls(f, &env->regs[i]); qemu_put_betls(f, &env->eip); qemu_put_betls(f, &env->eflags); - qemu_put_betl(f, 0); /* XXX: suppress that */ hflags = env->hflags; /* XXX: suppress most of the redundant hflags */ qemu_put_be32s(f, &hflags); @@ -2098,23 +2097,37 @@ void cpu_save(QEMUFile *f, void *opaque) fpuc = env->fpuc; fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; - for (i=7; i>=0; i--) { - fptag <<= 2; - if (env->fptags[i]) { - fptag |= 3; - } + for(i = 0; i < 8; i++) { + fptag |= ((!env->fptags[i]) << i); } qemu_put_be16s(f, &fpuc); qemu_put_be16s(f, &fpus); qemu_put_be16s(f, &fptag); +#ifdef USE_X86LDOUBLE + fpregs_format = 0; +#else + fpregs_format = 1; +#endif + qemu_put_be16s(f, &fpregs_format); + for(i = 0; i < 8; i++) { uint64_t mant; uint16_t exp; - cpu_get_fp80(&mant, &exp, env->fpregs[i]); +#ifdef USE_X86LDOUBLE + /* we save the real CPU data (in case of MMX usage only 'mant' + contains the MMX register */ + cpu_get_fp80(&mant, &exp, env->fpregs[i].d); qemu_put_be64(f, mant); qemu_put_be16(f, exp); +#else + /* if we use doubles for float emulation, we save the doubles to + avoid losing information in case of MMX usage. It can give + problems if the image is restored on a CPU where long + doubles are used instead. */ + qemu_put_be64(f, env->fpregs[i].xmm.MMX_Q(0)); +#endif } for(i = 0; i < 6; i++) @@ -2139,12 +2152,14 @@ void cpu_save(QEMUFile *f, void *opaque) /* MMU */ qemu_put_be32s(f, &env->a20_mask); -#ifdef TARGET_X86_64 + /* XMM */ + qemu_put_be32s(f, &env->mxcsr); for(i = 0; i < CPU_NB_REGS; i++) { qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(0)); qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(1)); } +#ifdef TARGET_X86_64 qemu_put_be64s(f, &env->efer); qemu_put_be64s(f, &env->star); qemu_put_be64s(f, &env->lstar); @@ -2154,40 +2169,97 @@ void cpu_save(QEMUFile *f, void *opaque) #endif } +/* XXX: add that in a FPU generic layer */ +union x86_longdouble { + uint64_t mant; + uint16_t exp; +}; + +#define MANTD1(fp) (fp & ((1LL << 52) - 1)) +#define EXPBIAS1 1023 +#define EXPD1(fp) ((fp >> 52) & 0x7FF) +#define SIGND1(fp) ((fp >> 32) & 0x80000000) + +static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp) +{ + int e; + /* mantissa */ + p->mant = (MANTD1(temp) << 11) | (1LL << 63); + /* exponent + sign */ + e = EXPD1(temp) - EXPBIAS1 + 16383; + e |= SIGND1(temp) >> 16; + p->exp = e; +} + int cpu_load(QEMUFile *f, void *opaque, int version_id) { CPUState *env = opaque; - int i; + int i, guess_mmx; uint32_t hflags; - uint16_t fpus, fpuc, fptag; + uint16_t fpus, fpuc, fptag, fpregs_format; - if (version_id != 2) + if (version_id != 3) return -EINVAL; for(i = 0; i < CPU_NB_REGS; i++) qemu_get_betls(f, &env->regs[i]); qemu_get_betls(f, &env->eip); qemu_get_betls(f, &env->eflags); - qemu_get_betl(f); /* XXX: suppress that */ qemu_get_be32s(f, &hflags); qemu_get_be16s(f, &fpuc); qemu_get_be16s(f, &fpus); qemu_get_be16s(f, &fptag); - + qemu_get_be16s(f, &fpregs_format); + + /* NOTE: we cannot always restore the FPU state if the image come + from a host with a different 'USE_X86LDOUBLE' define. We guess + if we are in an MMX state to restore correctly in that case. */ + guess_mmx = ((fptag == 0xff) && (fpus & 0x3800) == 0); for(i = 0; i < 8; i++) { uint64_t mant; uint16_t exp; - mant = qemu_get_be64(f); - exp = qemu_get_be16(f); - env->fpregs[i] = cpu_set_fp80(mant, exp); + union x86_longdouble *p; + + switch(fpregs_format) { + case 0: + mant = qemu_get_be64(f); + exp = qemu_get_be16(f); +#ifdef USE_X86LDOUBLE + env->fpregs[i].d = cpu_set_fp80(mant, exp); +#else + /* difficult case */ + if (guess_mmx) + env->fpregs[i].xmm.MMX_Q(0) = mant; + else + env->fpregs[i].d = cpu_set_fp80(mant, exp); +#endif + break; + case 1: + mant = qemu_get_be64(f); +#ifdef USE_X86LDOUBLE + /* difficult case */ + p = (void *)&env->fpregs[i]; + if (guess_mmx) { + p->mant = mant; + p->exp = 0xffff; + } else { + fp64_to_fp80(p, mant); + } +#else + env->fpregs[i].xmm.MMX_Q(0) = mant; +#endif + break; + default: + return -EINVAL; + } } env->fpuc = fpuc; env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; + fptag ^= 0xff; for(i = 0; i < 8; i++) { - env->fptags[i] = ((fptag & 3) == 3); - fptag >>= 2; + env->fptags[i] = (fptag >> i) & 1; } for(i = 0; i < 6; i++) @@ -2212,12 +2284,13 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) /* MMU */ qemu_get_be32s(f, &env->a20_mask); -#ifdef TARGET_X86_64 + qemu_get_be32s(f, &env->mxcsr); for(i = 0; i < CPU_NB_REGS; i++) { qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(0)); qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(1)); } +#ifdef TARGET_X86_64 qemu_get_be64s(f, &env->efer); qemu_get_be64s(f, &env->star); qemu_get_be64s(f, &env->lstar); @@ -3433,7 +3506,7 @@ int main(int argc, char **argv) cpu_single_env = env; register_savevm("timer", 0, 1, timer_save, timer_load, env); - register_savevm("cpu", 0, 2, cpu_save, cpu_load, env); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); register_savevm("ram", 0, 1, ram_save, ram_load, NULL); qemu_register_reset(main_cpu_reset, global_env); |