| /* |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| #include "qemu/osdep.h" |
| |
| #include "cpu.h" |
| |
| void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) |
| { |
| CPUX86State *env = &cpu->env; |
| const ExtSaveArea *e, *f; |
| int i; |
| |
| X86LegacyXSaveArea *legacy; |
| X86XSaveHeader *header; |
| uint16_t cwd, swd, twd; |
| |
| memset(buf, 0, buflen); |
| |
| e = &x86_ext_save_areas[XSTATE_FP_BIT]; |
| |
| legacy = buf + e->offset; |
| header = buf + e->offset + sizeof(*legacy); |
| |
| twd = 0; |
| swd = env->fpus & ~(7 << 11); |
| swd |= (env->fpstt & 7) << 11; |
| cwd = env->fpuc; |
| for (i = 0; i < 8; ++i) { |
| twd |= (!env->fptags[i]) << i; |
| } |
| legacy->fcw = cwd; |
| legacy->fsw = swd; |
| legacy->ftw = twd; |
| legacy->fpop = env->fpop; |
| legacy->fpip = env->fpip; |
| legacy->fpdp = env->fpdp; |
| memcpy(&legacy->fpregs, env->fpregs, |
| sizeof(env->fpregs)); |
| legacy->mxcsr = env->mxcsr; |
| |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| uint8_t *xmm = legacy->xmm_regs[i]; |
| |
| stq_p(xmm, env->xmm_regs[i].ZMM_Q(0)); |
| stq_p(xmm + 8, env->xmm_regs[i].ZMM_Q(1)); |
| } |
| |
| header->xstate_bv = env->xstate_bv; |
| |
| e = &x86_ext_save_areas[XSTATE_YMM_BIT]; |
| if (e->size && e->offset) { |
| XSaveAVX *avx; |
| |
| avx = buf + e->offset; |
| |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| uint8_t *ymmh = avx->ymmh[i]; |
| |
| stq_p(ymmh, env->xmm_regs[i].ZMM_Q(2)); |
| stq_p(ymmh + 8, env->xmm_regs[i].ZMM_Q(3)); |
| } |
| } |
| |
| e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT]; |
| if (e->size && e->offset) { |
| XSaveBNDREG *bndreg; |
| XSaveBNDCSR *bndcsr; |
| |
| f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT]; |
| assert(f->size); |
| assert(f->offset); |
| |
| bndreg = buf + e->offset; |
| bndcsr = buf + f->offset; |
| |
| memcpy(&bndreg->bnd_regs, env->bnd_regs, |
| sizeof(env->bnd_regs)); |
| bndcsr->bndcsr = env->bndcs_regs; |
| } |
| |
| e = &x86_ext_save_areas[XSTATE_OPMASK_BIT]; |
| if (e->size && e->offset) { |
| XSaveOpmask *opmask; |
| XSaveZMM_Hi256 *zmm_hi256; |
| #ifdef TARGET_X86_64 |
| XSaveHi16_ZMM *hi16_zmm; |
| #endif |
| |
| f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT]; |
| assert(f->size); |
| assert(f->offset); |
| |
| opmask = buf + e->offset; |
| zmm_hi256 = buf + f->offset; |
| |
| memcpy(&opmask->opmask_regs, env->opmask_regs, |
| sizeof(env->opmask_regs)); |
| |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| uint8_t *zmmh = zmm_hi256->zmm_hi256[i]; |
| |
| stq_p(zmmh, env->xmm_regs[i].ZMM_Q(4)); |
| stq_p(zmmh + 8, env->xmm_regs[i].ZMM_Q(5)); |
| stq_p(zmmh + 16, env->xmm_regs[i].ZMM_Q(6)); |
| stq_p(zmmh + 24, env->xmm_regs[i].ZMM_Q(7)); |
| } |
| |
| #ifdef TARGET_X86_64 |
| f = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT]; |
| assert(f->size); |
| assert(f->offset); |
| |
| hi16_zmm = buf + f->offset; |
| |
| memcpy(&hi16_zmm->hi16_zmm, &env->xmm_regs[16], |
| 16 * sizeof(env->xmm_regs[16])); |
| #endif |
| } |
| |
| #ifdef TARGET_X86_64 |
| e = &x86_ext_save_areas[XSTATE_PKRU_BIT]; |
| if (e->size && e->offset) { |
| XSavePKRU *pkru = buf + e->offset; |
| |
| memcpy(pkru, &env->pkru, sizeof(env->pkru)); |
| } |
| #endif |
| } |
| |
| void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen) |
| { |
| CPUX86State *env = &cpu->env; |
| const ExtSaveArea *e, *f, *g; |
| int i; |
| |
| const X86LegacyXSaveArea *legacy; |
| const X86XSaveHeader *header; |
| uint16_t cwd, swd, twd; |
| |
| e = &x86_ext_save_areas[XSTATE_FP_BIT]; |
| |
| legacy = buf + e->offset; |
| header = buf + e->offset + sizeof(*legacy); |
| |
| cwd = legacy->fcw; |
| swd = legacy->fsw; |
| twd = legacy->ftw; |
| env->fpop = legacy->fpop; |
| env->fpstt = (swd >> 11) & 7; |
| env->fpus = swd; |
| env->fpuc = cwd; |
| for (i = 0; i < 8; ++i) { |
| env->fptags[i] = !((twd >> i) & 1); |
| } |
| env->fpip = legacy->fpip; |
| env->fpdp = legacy->fpdp; |
| env->mxcsr = legacy->mxcsr; |
| memcpy(env->fpregs, &legacy->fpregs, |
| sizeof(env->fpregs)); |
| |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| const uint8_t *xmm = legacy->xmm_regs[i]; |
| |
| env->xmm_regs[i].ZMM_Q(0) = ldq_p(xmm); |
| env->xmm_regs[i].ZMM_Q(1) = ldq_p(xmm + 8); |
| } |
| |
| env->xstate_bv = header->xstate_bv; |
| |
| e = &x86_ext_save_areas[XSTATE_YMM_BIT]; |
| if (e->size && e->offset) { |
| const XSaveAVX *avx; |
| |
| avx = buf + e->offset; |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| const uint8_t *ymmh = avx->ymmh[i]; |
| |
| env->xmm_regs[i].ZMM_Q(2) = ldq_p(ymmh); |
| env->xmm_regs[i].ZMM_Q(3) = ldq_p(ymmh + 8); |
| } |
| } |
| |
| e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT]; |
| if (e->size && e->offset) { |
| const XSaveBNDREG *bndreg; |
| const XSaveBNDCSR *bndcsr; |
| |
| f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT]; |
| assert(f->size); |
| assert(f->offset); |
| |
| bndreg = buf + e->offset; |
| bndcsr = buf + f->offset; |
| |
| memcpy(env->bnd_regs, &bndreg->bnd_regs, |
| sizeof(env->bnd_regs)); |
| env->bndcs_regs = bndcsr->bndcsr; |
| } |
| |
| e = &x86_ext_save_areas[XSTATE_OPMASK_BIT]; |
| if (e->size && e->offset) { |
| const XSaveOpmask *opmask; |
| const XSaveZMM_Hi256 *zmm_hi256; |
| #ifdef TARGET_X86_64 |
| const XSaveHi16_ZMM *hi16_zmm; |
| #endif |
| |
| f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT]; |
| assert(f->size); |
| assert(f->offset); |
| |
| g = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT]; |
| assert(g->size); |
| assert(g->offset); |
| |
| opmask = buf + e->offset; |
| zmm_hi256 = buf + f->offset; |
| #ifdef TARGET_X86_64 |
| hi16_zmm = buf + g->offset; |
| #endif |
| |
| memcpy(env->opmask_regs, &opmask->opmask_regs, |
| sizeof(env->opmask_regs)); |
| |
| for (i = 0; i < CPU_NB_REGS; i++) { |
| const uint8_t *zmmh = zmm_hi256->zmm_hi256[i]; |
| |
| env->xmm_regs[i].ZMM_Q(4) = ldq_p(zmmh); |
| env->xmm_regs[i].ZMM_Q(5) = ldq_p(zmmh + 8); |
| env->xmm_regs[i].ZMM_Q(6) = ldq_p(zmmh + 16); |
| env->xmm_regs[i].ZMM_Q(7) = ldq_p(zmmh + 24); |
| } |
| |
| #ifdef TARGET_X86_64 |
| memcpy(&env->xmm_regs[16], &hi16_zmm->hi16_zmm, |
| 16 * sizeof(env->xmm_regs[16])); |
| #endif |
| } |
| |
| #ifdef TARGET_X86_64 |
| e = &x86_ext_save_areas[XSTATE_PKRU_BIT]; |
| if (e->size && e->offset) { |
| const XSavePKRU *pkru; |
| |
| pkru = buf + e->offset; |
| memcpy(&env->pkru, pkru, sizeof(env->pkru)); |
| } |
| #endif |
| } |