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
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;
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 & (1u << 1)) != 0;
386
+
387
+ /* Component is in the layout but unknown to the guest CPUID model */
388
+ if (size == 0) {
389
+ /*
390
+ * The hypervisor might expose a component that has no
391
+ * representation in the guest CPUID model. We query the host to
392
+ * retrieve the size of the component, so we can skip over it.
393
+ */
394
+ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
395
+ size = eax;
396
+ align64 = (ecx & (1u << 1)) != 0;
397
+ if (size == 0) {
398
+ error_report("xsave component %zu: size unknown to both "
399
+ "guest and host CPUID", i);
400
+ return -EINVAL;
401
+ }
402
+
403
+ if (align64) {
404
+ xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
405
+ }
406
+
407
+ if (xsave_offset + size > buflen) {
408
+ error_report("xsave component %zu overruns source buffer: "
409
+ "offset=%zu size=%u buflen=%zu",
410
+ i, xsave_offset, size, buflen);
411
+ return -E2BIG;
412
+ }
413
+
414
+ xsave_offset += size;
415
+ continue;
416
+ }
417
+
418
+ if (align64) {
419
+ xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
420
+ }
421
+
422
+ if ((xsave_offset + size) > buflen) {
423
+ error_report("xsave component %zu overruns source buffer: "
424
+ "offset=%zu size=%u buflen=%zu",
425
+ i, xsave_offset, size, buflen);
426
+ return -E2BIG;
427
+ }
428
+
429
+ if ((dst_off + size) > env->xsave_buf_len) {
430
+ error_report("xsave component %zu overruns destination buffer: "
431
+ "offset=%u size=%u buflen=%zu",
432
+ i, dst_off, size, (size_t)env->xsave_buf_len);
433
+ return -E2BIG;
434
+ }
435
+
436
+ /* Copy components marked present in XSTATE_BV to guest model */
437
+ if (((compacted_xstate_bv >> i) & 1) != 0) {
438
+ memcpy(env->xsave_buf + dst_off, buf + xsave_offset, size);
439
+ }
440
+
441
+ xsave_offset += size;
442
+ }
443
+
444
+ return 0;
445
+}
446
+
447
+/**
448
+ * compact_xsave_area - Convert standard XSAVE format to compacted format
449
+ * @env: CPU state containing the standard format XSAVE buffer
450
+ * @buf: Destination buffer for compacted XSAVE data (to send to hypervisor)
451
+ * @buflen: Size of destination buffer
452
+ *
453
+ * Accelerator backends like MSHV might expect XSAVE state in compacted format
454
+ * (XSAVEC). The state components are packed contiguously without gaps.
455
+ * The XSAVE qemu buffers are in standard format where each component has a
456
+ * fixed offset.
457
+ *
458
+ * This function converts from standard to compacted format, it accepts a
459
+ * pre-allocated destination buffer of sufficient size, it is the
460
+ * responsibility of the caller to ensure the buffer is big enough.
461
+ *
462
+ * Returns: total size of compacted XSAVE data written to @buf
463
+ */
464
+int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen)
465
+{
466
+ uint64_t *xcomp_bv;
467
+ size_t i;
468
+ uint32_t eax, ebx, ecx, edx;
469
+ uint32_t size, src_off;
470
+ bool align64;
471
+ size_t compact_offset;
472
+ uint64_t host_xcr0_mask, guest_xcr0;
473
+
474
+ /* Zero out buffer, then copy legacy region (FP + SSE) and header as-is */
475
+ memset(buf, 0, buflen);
476
+ memcpy(buf, env->xsave_buf, XSAVE_EXT_OFFSET);
477
+
478
+ /*
479
+ * Set XCOMP_BV to indicate compacted format (bit 63) and which
480
+ * components are in the layout.
481
+ *
482
+ * We must explicitly set XCOMP_BV because x86_cpu_xsave_all_areas()
483
+ * produces standard format with XCOMP_BV=0 (buffer is zeroed and only
484
+ * XSTATE_BV is set in the header).
485
+ *
486
+ * XCOMP_BV must reflect the partition's XSAVE capability, not the
487
+ * guest's current XCR0 (env->xcr0). These differ b/c:
488
+ * - A guest's XCR0 is what the guest OS has enabled via XSETBV
489
+ * - The partition's XCR0 mask is the hypervisor's save/restore capability
490
+ *
491
+ * The hypervisor uses XSAVES which saves based on its capability, so the
492
+ * XCOMP_BV value in the buffer we send back must match that capability.
493
+ *
494
+ * We intersect the host XCR0 with the guest's supported XCR0 features
495
+ * (FEAT_XSAVE_XCR0_*) so that features disabled at partition creation
496
+ * (e.g. AMX) are excluded from the compacted layout.
497
+ */
498
+ host_cpuid(0xD, 0, &eax, &ebx, &ecx, &edx);
499
+ host_xcr0_mask = ((uint64_t)edx << 32) | eax;
500
+ guest_xcr0 = ((uint64_t)env->features[FEAT_XSAVE_XCR0_HI] << 32) |
501
+ env->features[FEAT_XSAVE_XCR0_LO];
502
+ host_xcr0_mask &= guest_xcr0;
503
+ xcomp_bv = buf + XSAVE_XCOMP_BV_OFFSET;
504
+ *xcomp_bv = host_xcr0_mask | (1ULL << 63);
505
+
506
+ /*
507
+ * Process each extended state component in the host's XCR0.
508
+ * The compacted layout must match XCOMP_BV (host capability).
509
+ *
510
+ * For each component:
511
+ * - Get its size and standard offset from host CPUID
512
+ * - Apply 64-byte alignment if required
513
+ * - Copy data only if guest has this component (bit set in env->xcr0)
514
+ * - Always advance offset to maintain correct layout
515
+ */
516
+ compact_offset = XSAVE_EXT_OFFSET;
517
+ for (i = 2; i < 63; i++) {
518
+ if (!((host_xcr0_mask >> i) & 1)) {
519
+ continue;
520
+ }
521
+
522
+ /* Query host CPUID for this component's size and standard offset */
523
+ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
524
+ size = eax;
525
+ src_off = ebx;
526
+ align64 = (ecx >> 1) & 1;
527
+
528
+ if (size == 0) {
529
+ /* Component in host xcr0 but unknown - shouldn't happen */
530
+ continue;
531
+ }
532
+
533
+ /* Apply 64-byte alignment if required by this component */
534
+ if (align64) {
535
+ compact_offset = QEMU_ALIGN_UP(compact_offset, 64);
536
+ }
537
+
538
+ /*
539
+ * Only copy data if guest has this component enabled in XCR0.
540
+ * Otherwise the component remains zeroed (init state), but we
541
+ * still advance the offset to maintain the correct layout.
542
+ */
543
+ if ((env->xcr0 >> i) & 1) {
544
+ memcpy(buf + compact_offset, env->xsave_buf + src_off, size);
545
+ }
546
+
547
+ compact_offset += size;
548
+ }
549
+
550
+ return compact_offset;
551
+}