master
c 558 lines 17.6 KB
Raw
1 /*
2 * This work is licensed under the terms of the GNU GPL, version 2 or later.
3 * See the COPYING file in the top-level directory.
4 */
5 #include "qemu/osdep.h"
6 #include "qemu/error-report.h"
7
8 #include "cpu.h"
9
10 void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen)
11 {
12 CPUX86State *env = &cpu->env;
13 const ExtSaveArea *e, *f;
14 int i;
15
16 X86LegacyXSaveArea *legacy;
17 X86XSaveHeader *header;
18 uint16_t cwd, swd, twd;
19
20 memset(buf, 0, buflen);
21
22 e = &x86_ext_save_areas[XSTATE_FP_BIT];
23
24 legacy = buf + e->offset;
25 header = buf + e->offset + sizeof(*legacy);
26
27 twd = 0;
28 swd = env->fpus & ~(7 << 11);
29 swd |= (env->fpstt & 7) << 11;
30 cwd = env->fpuc;
31 for (i = 0; i < 8; ++i) {
32 twd |= (!env->fptags[i]) << i;
33 }
34 legacy->fcw = cwd;
35 legacy->fsw = swd;
36 legacy->ftw = twd;
37 legacy->fpop = env->fpop;
38 legacy->fpip = env->fpip;
39 legacy->fpdp = env->fpdp;
40 memcpy(&legacy->fpregs, env->fpregs,
41 sizeof(env->fpregs));
42 legacy->mxcsr = env->mxcsr;
43
44 for (i = 0; i < CPU_NB_REGS; i++) {
45 uint8_t *xmm = legacy->xmm_regs[i];
46
47 stq_p(xmm, env->xmm_regs[i].ZMM_Q(0));
48 stq_p(xmm + 8, env->xmm_regs[i].ZMM_Q(1));
49 }
50
51 header->xstate_bv = env->xstate_bv;
52
53 e = &x86_ext_save_areas[XSTATE_YMM_BIT];
54 if (e->size && e->offset) {
55 XSaveAVX *avx;
56
57 avx = buf + e->offset;
58
59 for (i = 0; i < CPU_NB_REGS; i++) {
60 uint8_t *ymmh = avx->ymmh[i];
61
62 stq_p(ymmh, env->xmm_regs[i].ZMM_Q(2));
63 stq_p(ymmh + 8, env->xmm_regs[i].ZMM_Q(3));
64 }
65 }
66
67 e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT];
68 if (e->size && e->offset) {
69 XSaveBNDREG *bndreg;
70 XSaveBNDCSR *bndcsr;
71
72 f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT];
73 assert(f->size);
74 assert(f->offset);
75
76 bndreg = buf + e->offset;
77 bndcsr = buf + f->offset;
78
79 memcpy(&bndreg->bnd_regs, env->bnd_regs,
80 sizeof(env->bnd_regs));
81 bndcsr->bndcsr = env->bndcs_regs;
82 }
83
84 e = &x86_ext_save_areas[XSTATE_OPMASK_BIT];
85 if (e->size && e->offset) {
86 XSaveOpmask *opmask;
87 XSaveZMM_Hi256 *zmm_hi256;
88 #ifdef TARGET_X86_64
89 XSaveHi16_ZMM *hi16_zmm;
90 #endif
91
92 f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT];
93 assert(f->size);
94 assert(f->offset);
95
96 opmask = buf + e->offset;
97 zmm_hi256 = buf + f->offset;
98
99 memcpy(&opmask->opmask_regs, env->opmask_regs,
100 sizeof(env->opmask_regs));
101
102 for (i = 0; i < CPU_NB_REGS; i++) {
103 uint8_t *zmmh = zmm_hi256->zmm_hi256[i];
104
105 stq_p(zmmh, env->xmm_regs[i].ZMM_Q(4));
106 stq_p(zmmh + 8, env->xmm_regs[i].ZMM_Q(5));
107 stq_p(zmmh + 16, env->xmm_regs[i].ZMM_Q(6));
108 stq_p(zmmh + 24, env->xmm_regs[i].ZMM_Q(7));
109 }
110
111 #ifdef TARGET_X86_64
112 f = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT];
113 assert(f->size);
114 assert(f->offset);
115
116 hi16_zmm = buf + f->offset;
117
118 memcpy(&hi16_zmm->hi16_zmm, &env->xmm_regs[16],
119 16 * sizeof(env->xmm_regs[16]));
120 #endif
121 }
122
123 #ifdef TARGET_X86_64
124 e = &x86_ext_save_areas[XSTATE_PKRU_BIT];
125 if (e->size && e->offset) {
126 XSavePKRU *pkru = buf + e->offset;
127
128 memcpy(pkru, &env->pkru, sizeof(env->pkru));
129 }
130
131 e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT];
132 if (e->size && e->offset) {
133 XSaveXTILECFG *tilecfg = buf + e->offset;
134
135 memcpy(tilecfg, &env->xtilecfg, sizeof(env->xtilecfg));
136 }
137
138 e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT];
139 if (e->size && e->offset && buflen >= e->size + e->offset) {
140 XSaveXTILEDATA *tiledata = buf + e->offset;
141
142 memcpy(tiledata, &env->xtiledata, sizeof(env->xtiledata));
143 }
144
145 e = &x86_ext_save_areas[XSTATE_APX_BIT];
146 if (e->size && e->offset && buflen) {
147 XSaveAPX *apx = buf + e->offset;
148
149 memcpy(apx, &env->regs[CPU_NB_REGS],
150 sizeof(env->regs[CPU_NB_REGS]) * (CPU_NB_EREGS - CPU_NB_REGS));
151 }
152 #endif
153 }
154
155 void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen)
156 {
157 CPUX86State *env = &cpu->env;
158 const ExtSaveArea *e, *f, *g;
159 int i;
160
161 const X86LegacyXSaveArea *legacy;
162 const X86XSaveHeader *header;
163 uint16_t cwd, swd, twd;
164
165 e = &x86_ext_save_areas[XSTATE_FP_BIT];
166
167 legacy = buf + e->offset;
168 header = buf + e->offset + sizeof(*legacy);
169
170 cwd = legacy->fcw;
171 swd = legacy->fsw;
172 twd = legacy->ftw;
173 env->fpop = legacy->fpop;
174 env->fpstt = (swd >> 11) & 7;
175 env->fpus = swd;
176 env->fpuc = cwd;
177 for (i = 0; i < 8; ++i) {
178 env->fptags[i] = !((twd >> i) & 1);
179 }
180 env->fpip = legacy->fpip;
181 env->fpdp = legacy->fpdp;
182 env->mxcsr = legacy->mxcsr;
183 memcpy(env->fpregs, &legacy->fpregs,
184 sizeof(env->fpregs));
185
186 for (i = 0; i < CPU_NB_REGS; i++) {
187 const uint8_t *xmm = legacy->xmm_regs[i];
188
189 env->xmm_regs[i].ZMM_Q(0) = ldq_p(xmm);
190 env->xmm_regs[i].ZMM_Q(1) = ldq_p(xmm + 8);
191 }
192
193 env->xstate_bv = header->xstate_bv;
194
195 e = &x86_ext_save_areas[XSTATE_YMM_BIT];
196 if (e->size && e->offset) {
197 const XSaveAVX *avx;
198
199 avx = buf + e->offset;
200 for (i = 0; i < CPU_NB_REGS; i++) {
201 const uint8_t *ymmh = avx->ymmh[i];
202
203 env->xmm_regs[i].ZMM_Q(2) = ldq_p(ymmh);
204 env->xmm_regs[i].ZMM_Q(3) = ldq_p(ymmh + 8);
205 }
206 }
207
208 e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT];
209 if (e->size && e->offset) {
210 const XSaveBNDREG *bndreg;
211 const XSaveBNDCSR *bndcsr;
212
213 f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT];
214 assert(f->size);
215 assert(f->offset);
216
217 bndreg = buf + e->offset;
218 bndcsr = buf + f->offset;
219
220 memcpy(env->bnd_regs, &bndreg->bnd_regs,
221 sizeof(env->bnd_regs));
222 env->bndcs_regs = bndcsr->bndcsr;
223 }
224
225 e = &x86_ext_save_areas[XSTATE_OPMASK_BIT];
226 if (e->size && e->offset) {
227 const XSaveOpmask *opmask;
228 const XSaveZMM_Hi256 *zmm_hi256;
229 #ifdef TARGET_X86_64
230 const XSaveHi16_ZMM *hi16_zmm;
231 #endif
232
233 f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT];
234 assert(f->size);
235 assert(f->offset);
236
237 g = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT];
238 assert(g->size);
239 assert(g->offset);
240
241 opmask = buf + e->offset;
242 zmm_hi256 = buf + f->offset;
243 #ifdef TARGET_X86_64
244 hi16_zmm = buf + g->offset;
245 #endif
246
247 memcpy(env->opmask_regs, &opmask->opmask_regs,
248 sizeof(env->opmask_regs));
249
250 for (i = 0; i < CPU_NB_REGS; i++) {
251 const uint8_t *zmmh = zmm_hi256->zmm_hi256[i];
252
253 env->xmm_regs[i].ZMM_Q(4) = ldq_p(zmmh);
254 env->xmm_regs[i].ZMM_Q(5) = ldq_p(zmmh + 8);
255 env->xmm_regs[i].ZMM_Q(6) = ldq_p(zmmh + 16);
256 env->xmm_regs[i].ZMM_Q(7) = ldq_p(zmmh + 24);
257 }
258
259 #ifdef TARGET_X86_64
260 memcpy(&env->xmm_regs[16], &hi16_zmm->hi16_zmm,
261 16 * sizeof(env->xmm_regs[16]));
262 #endif
263 }
264
265 #ifdef TARGET_X86_64
266 e = &x86_ext_save_areas[XSTATE_PKRU_BIT];
267 if (e->size && e->offset) {
268 const XSavePKRU *pkru;
269
270 pkru = buf + e->offset;
271 memcpy(&env->pkru, pkru, sizeof(env->pkru));
272 }
273
274 e = &x86_ext_save_areas[XSTATE_XTILE_CFG_BIT];
275 if (e->size && e->offset) {
276 const XSaveXTILECFG *tilecfg = buf + e->offset;
277
278 memcpy(&env->xtilecfg, tilecfg, sizeof(env->xtilecfg));
279 }
280
281 e = &x86_ext_save_areas[XSTATE_XTILE_DATA_BIT];
282 if (e->size && e->offset && buflen >= e->size + e->offset) {
283 const XSaveXTILEDATA *tiledata = buf + e->offset;
284
285 memcpy(&env->xtiledata, tiledata, sizeof(env->xtiledata));
286 }
287
288 e = &x86_ext_save_areas[XSTATE_APX_BIT];
289 if (e->size && e->offset) {
290 const XSaveAPX *apx = buf + e->offset;
291
292 memcpy(&env->regs[CPU_NB_REGS], apx,
293 sizeof(env->regs[CPU_NB_REGS]) * (CPU_NB_EREGS - CPU_NB_REGS));
294 }
295 #endif
296 }
297
298 #define XSTATE_BV_IN_HDR offsetof(X86XSaveHeader, xstate_bv)
299 #define XCOMP_BV_IN_HDR offsetof(X86XSaveHeader, xcomp_bvo)
300
301 typedef struct X86XSaveAreaView {
302 /* 512 bytes */
303 X86LegacyXSaveArea legacy;
304 /* 64 bytes */
305 X86XSaveHeader header;
306 /* ...followed by individual xsave areas */
307 } X86XSaveAreaView;
308
309 #define XSAVE_XSTATE_BV_OFFSET offsetof(X86XSaveAreaView, header.xstate_bv)
310 #define XSAVE_XCOMP_BV_OFFSET offsetof(X86XSaveAreaView, header.xcomp_bv)
311 #define XSAVE_EXT_OFFSET (sizeof(X86LegacyXSaveArea) + \
312 sizeof(X86XSaveHeader))
313
314 /**
315 * decompact_xsave_area - Convert compacted XSAVE format to standard format
316 * @buf: Source buffer containing compacted XSAVE data
317 * @buflen: Size of source buffer
318 * @env: CPU state where the standard format buffer will be written to
319 *
320 * Accelerator backends like MSHV might return XSAVE state in compacted format
321 * (XSAVEC). The state components have to be packed contiguously without gaps.
322 * The XSAVE qemu buffers are in standard format where each component has a
323 * fixed offset.
324 *
325 * Returns: 0 on success, negative errno on failure
326 */
327 int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env)
328 {
329 uint64_t compacted_xstate_bv, compacted_xcomp_bv, compacted_layout_bv;
330 size_t xsave_offset;
331 uint64_t *xcomp_bv;
332 size_t i;
333 uint32_t eax, ebx, ecx, edx;
334 uint32_t size, dst_off;
335 bool align64, supervisor;
336 uint64_t guest_xcr0, *xstate_bv;
337
338 compacted_xstate_bv = *(uint64_t *)(buf + XSAVE_XSTATE_BV_OFFSET);
339 compacted_xcomp_bv = *(uint64_t *)(buf + XSAVE_XCOMP_BV_OFFSET);
340
341 /* This function only handles compacted format (bit 63 set) */
342 assert((compacted_xcomp_bv >> 63) & 1);
343
344 /* Low bits of XCOMP_BV describe which components are in the layout */
345 compacted_layout_bv = compacted_xcomp_bv & ~(1ULL << 63);
346
347 /* Zero out buffer, then copy legacy region (FP + SSE) and header as-is */
348 memset(env->xsave_buf, 0, env->xsave_buf_len);
349 memcpy(env->xsave_buf, buf, XSAVE_EXT_OFFSET);
350
351 /*
352 * We mask XSTATE_BV with the guest's supported XCR0 because:
353 * 1. Supervisor state (IA32_XSS) is hypervisor-managed, we don't use
354 * this state for migration.
355 * 2. Features disabled at partition creation (e.g. AMX) must be excluded
356 */
357 guest_xcr0 = ((uint64_t)env->features[FEAT_XSAVE_XCR0_HI] << 32) |
358 env->features[FEAT_XSAVE_XCR0_LO];
359 xstate_bv = (uint64_t *)(env->xsave_buf + XSAVE_XSTATE_BV_OFFSET);
360 *xstate_bv &= guest_xcr0;
361
362 /* Clear bit 63 - output is standard format, not compacted */
363 xcomp_bv = (uint64_t *)(env->xsave_buf + XSAVE_XCOMP_BV_OFFSET);
364 *xcomp_bv = *xcomp_bv & ~(1ULL << 63);
365
366 /*
367 * Process each extended state component in the compacted layout.
368 * Components 0 and 1 (FP and SSE) are in the legacy region, so we
369 * start at component 2. For each component:
370 * - Calculate its offset in the compacted source (contiguous layout)
371 * - Get its fixed offset in the standard destination from CPUID
372 * - Copy if the component has non-init state (bit set in XSTATE_BV)
373 */
374 xsave_offset = XSAVE_EXT_OFFSET;
375 for (i = 2; i < 63; i++) {
376 if (((compacted_layout_bv >> i) & 1) == 0) {
377 continue;
378 }
379
380 /* Query guest CPUID for this component's size and standard offset */
381 cpu_x86_cpuid(env, 0xD, i, &eax, &ebx, &ecx, &edx);
382
383 size = eax;
384 dst_off = ebx;
385 align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0;
386 supervisor = (ecx & ESA_FEATURE_XSS_MASK) != 0;
387
388 /* Component is in the layout but unknown to the guest CPUID model */
389 if (size == 0) {
390 /*
391 * The hypervisor might expose a component that has no
392 * representation in the guest CPUID model. We query the host to
393 * retrieve the size of the component, so we can skip over it.
394 */
395 host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
396 size = eax;
397 align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0;
398 if (size == 0) {
399 error_report("xsave component %zu: size unknown to both "
400 "guest and host CPUID", i);
401 return -EINVAL;
402 }
403
404 if (align64) {
405 xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
406 }
407
408 if (xsave_offset + size > buflen) {
409 error_report("xsave component %zu overruns source buffer: "
410 "offset=%zu size=%u buflen=%zu",
411 i, xsave_offset, size, buflen);
412 return -E2BIG;
413 }
414
415 xsave_offset += size;
416 continue;
417 }
418
419 if (align64) {
420 xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
421 }
422
423 if ((xsave_offset + size) > buflen) {
424 error_report("xsave component %zu overruns source buffer: "
425 "offset=%zu size=%u buflen=%zu",
426 i, xsave_offset, size, buflen);
427 return -E2BIG;
428 }
429
430 if ((dst_off + size) > env->xsave_buf_len) {
431 error_report("xsave component %zu overruns destination buffer: "
432 "offset=%u size=%u buflen=%zu",
433 i, dst_off, size, (size_t)env->xsave_buf_len);
434 return -E2BIG;
435 }
436
437 /*
438 * Copy components marked present in XSTATE_BV to guest model.
439 *
440 * NB: Supervisor state is skipped b/c there is no slot in the
441 * standard format XSAVE buffer (CET state is migrated via MSRs,
442 * others supervisor state isn't migrated).
443 */
444 if (((compacted_xstate_bv >> i) & 1) != 0 && !supervisor) {
445 memcpy(env->xsave_buf + dst_off, buf + xsave_offset, size);
446 }
447
448 xsave_offset += size;
449 }
450
451 return 0;
452 }
453
454 /**
455 * compact_xsave_area - Convert standard XSAVE format to compacted format
456 * @env: CPU state containing the standard format XSAVE buffer
457 * @buf: Destination buffer for compacted XSAVE data (to send to hypervisor)
458 * @buflen: Size of destination buffer
459 *
460 * Accelerator backends like MSHV might expect XSAVE state in compacted format
461 * (XSAVEC). The state components are packed contiguously without gaps.
462 * The XSAVE qemu buffers are in standard format where each component has a
463 * fixed offset.
464 *
465 * This function converts from standard to compacted format, it accepts a
466 * pre-allocated destination buffer of sufficient size, it is the
467 * responsibility of the caller to ensure the buffer is big enough.
468 *
469 * Returns: total size of compacted XSAVE data written to @buf
470 */
471 int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen)
472 {
473 uint64_t *xcomp_bv;
474 size_t i;
475 uint32_t eax, ebx, ecx, edx;
476 uint32_t size, src_off;
477 bool align64;
478 size_t compact_offset;
479 uint64_t host_xcr0_mask, guest_xcr0;
480
481 /* Zero out buffer, then copy legacy region (FP + SSE) and header as-is */
482 memset(buf, 0, buflen);
483 memcpy(buf, env->xsave_buf, XSAVE_EXT_OFFSET);
484
485 /*
486 * Set XCOMP_BV to indicate compacted format (bit 63) and which
487 * components are in the layout.
488 *
489 * We must explicitly set XCOMP_BV because x86_cpu_xsave_all_areas()
490 * produces standard format with XCOMP_BV=0 (buffer is zeroed and only
491 * XSTATE_BV is set in the header).
492 *
493 * XCOMP_BV must reflect the partition's XSAVE capability, not the
494 * guest's current XCR0 (env->xcr0). These differ b/c:
495 * - A guest's XCR0 is what the guest OS has enabled via XSETBV
496 * - The partition's XCR0 mask is the hypervisor's save/restore capability
497 *
498 * The hypervisor uses XSAVES which saves based on its capability, so the
499 * XCOMP_BV value in the buffer we send back must match that capability.
500 *
501 * We intersect the host XCR0 with the guest's supported XCR0 features
502 * (FEAT_XSAVE_XCR0_*) so that features disabled at partition creation
503 * (e.g. AMX) are excluded from the compacted layout.
504 */
505 host_cpuid(0xD, 0, &eax, &ebx, &ecx, &edx);
506 host_xcr0_mask = ((uint64_t)edx << 32) | eax;
507 guest_xcr0 = ((uint64_t)env->features[FEAT_XSAVE_XCR0_HI] << 32) |
508 env->features[FEAT_XSAVE_XCR0_LO];
509 host_xcr0_mask &= guest_xcr0;
510 xcomp_bv = buf + XSAVE_XCOMP_BV_OFFSET;
511 *xcomp_bv = host_xcr0_mask | (1ULL << 63);
512
513 /*
514 * Process each extended state component in the host's XCR0.
515 * The compacted layout must match XCOMP_BV (host capability).
516 *
517 * For each component:
518 * - Get its size and standard offset from host CPUID
519 * - Apply 64-byte alignment if required
520 * - Copy data only if guest has this component (bit set in env->xcr0)
521 * - Always advance offset to maintain correct layout
522 */
523 compact_offset = XSAVE_EXT_OFFSET;
524 for (i = 2; i < 63; i++) {
525 if (!((host_xcr0_mask >> i) & 1)) {
526 continue;
527 }
528
529 /* Query host CPUID for this component's size and standard offset */
530 host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
531 size = eax;
532 src_off = ebx;
533 align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0;
534
535 if (size == 0) {
536 /* Component in host xcr0 but unknown - shouldn't happen */
537 continue;
538 }
539
540 /* Apply 64-byte alignment if required by this component */
541 if (align64) {
542 compact_offset = QEMU_ALIGN_UP(compact_offset, 64);
543 }
544
545 /*
546 * Only copy data if guest has this component enabled in XCR0.
547 * Otherwise the component remains zeroed (init state), but we
548 * still advance the offset to maintain the correct layout.
549 */
550 if ((env->xcr0 >> i) & 1) {
551 memcpy(buf + compact_offset, env->xsave_buf + src_off, size);
552 }
553
554 compact_offset += size;
555 }
556
557 return compact_offset;
558 }