e56dd3c70a
When dynamic xfeatures (e.g. AMX) are used by the guest, the xsave area would be larger than 4KB. KVM_GET_XSAVE2 and KVM_SET_XSAVE under KVM_CAP_XSAVE2 works with a xsave buffer larger than 4KB. Always use the new ioctls under KVM_CAP_XSAVE2 when KVM supports it. Signed-off-by: Jing Liu <jing2.liu@intel.com> Signed-off-by: Zeng Guang <guang.zeng@intel.com> Signed-off-by: Wei Wang <wei.w.wang@intel.com> Signed-off-by: Yang Zhong <yang.zhong@intel.com> Message-Id: <20220217060434.52460-7-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
280 lines
7.3 KiB
C
280 lines
7.3 KiB
C
/*
|
|
* 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));
|
|
}
|
|
|
|
e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT];
|
|
if (e->size && e->offset) {
|
|
XSaveXTILECFG *tilecfg = buf + e->offset;
|
|
|
|
memcpy(tilecfg, &env->xtilecfg, sizeof(env->xtilecfg));
|
|
}
|
|
|
|
e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT];
|
|
if (e->size && e->offset && buflen >= e->size + e->offset) {
|
|
XSaveXTILEDATA *tiledata = buf + e->offset;
|
|
|
|
memcpy(tiledata, &env->xtiledata, sizeof(env->xtiledata));
|
|
}
|
|
#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));
|
|
}
|
|
|
|
e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT];
|
|
if (e->size && e->offset) {
|
|
const XSaveXTILECFG *tilecfg = buf + e->offset;
|
|
|
|
memcpy(&env->xtilecfg, tilecfg, sizeof(env->xtilecfg));
|
|
}
|
|
|
|
e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT];
|
|
if (e->size && e->offset && buflen >= e->size + e->offset) {
|
|
const XSaveXTILEDATA *tiledata = buf + e->offset;
|
|
|
|
memcpy(&env->xtiledata, tiledata, sizeof(env->xtiledata));
|
|
}
|
|
#endif
|
|
}
|