diff -r 69e5242c35ee sys/dev/nvmm/x86/nvmm_x86.c --- a/sys/dev/nvmm/x86/nvmm_x86.c Sat Jul 11 23:11:22 2026 +0000 +++ b/sys/dev/nvmm/x86/nvmm_x86.c Mon Jul 13 16:42:04 2026 +0000 @@ -467,23 +467,52 @@ nvmm_x86_pat_validate(uint64_t val) return true; } +/* + * nvmm_x86_xsave_size(xcr0) + * + * Returns the maximum XSAVE area size in bytes needed to + * represent all user state components corresponding to bits set + * in xcr0. Bit 63, which is reserved for future architecture + * extension at the time of writing, MUST NOT be set. + */ uint32_t nvmm_x86_xsave_size(uint64_t xcr0) { - uint32_t size = sizeof(struct xsave_header); + uint32_t totalsize = sizeof(struct xsave_header); unsigned i; - KASSERT((xcr0 & ~__BITS(XSAVE_MAX_COMPONENT, 0)) == 0); + /* + * Caller must not pass bit 63 until the architectural + * extension mechanism it is reserved for has been defined. + */ + KASSERT((xcr0 & ~__BITS(62, 0)) == 0); CTASSERT(sizeof(struct xsave_header) == 512 + 64); - for (i = 0; i < XSAVE_MAX_COMPONENT; i++) { + for (i = 2; i < 63; i++) { + uint32_t descs[4]; + uint32_t size, offset; + + /* + * Skip state components that are not of interest to + * the caller. + */ if ((xcr0 & __BIT(i)) == 0) continue; - KASSERT(x86_xsave_sizes[i] <= - UINT32_MAX - x86_xsave_offsets[i]); - if (size < x86_xsave_offsets[i] + x86_xsave_sizes[i]) - size = x86_xsave_offsets[i] + x86_xsave_sizes[i]; + + /* + * Can't use x86_xsave_offsets[i] + x86_xsave_sizes[i] + * because the NetBSD kernel only queries those for the + * user state components it knows about, but in + * principle this should support any user state + * component the guest wants to use even if NetBSD + * doesn't know how. + */ + x86_cpuid2(0x0d, i, descs); + size = descs[0]; /* CPUID[EAX=0x0d,ECX=i].EAX */ + offset = descs[1]; /* CPUID[EAX=0x0d,ECX=i].EBX */ + KASSERT(size <= UINT32_MAX - offset); + totalsize = MAX(totalsize, offset + size); } - return size; + return totalsize; } diff -r 69e5242c35ee sys/dev/nvmm/x86/nvmm_x86_vmx.c --- a/sys/dev/nvmm/x86/nvmm_x86_vmx.c Sat Jul 11 23:11:22 2026 +0000 +++ b/sys/dev/nvmm/x86/nvmm_x86_vmx.c Mon Jul 13 16:42:04 2026 +0000 @@ -828,7 +828,10 @@ struct vmx_cpudata { struct nvmm_vcpu_conf_tpr tpr; uint64_t xcr0_mask; - /* Guest XSAVE state. */ + /* + * Guest XSAVE state. Must be the last member because it may + * be extended variably by the CPU. + */ struct xsave_header gfpu __aligned(64); }; @@ -2091,14 +2094,32 @@ vmx_vcpu_guest_fpu_enter(struct nvmm_cpu { struct vmx_cpudata *cpudata = vcpu->cpudata; + /* + * Save any extended CPU state (such as FPU/vector registers) + * that might be in use by userland in this thread to memory, + * and block interrupts whose handlers might use the XSAVE area + * -- this raises the IPL to IPL_VM. + * + * After this point, we are free to use extended CPU state. + */ fpu_kern_enter(); - /* TODO: should we use *XSAVE64 here? */ - fpu_area_restore(&cpudata->gfpu, cpudata->xcr0_mask, false); - + + /* + * Set XCR0 to list all the XSAVE user state components the + * guest has requested so we can restore any registers from + * memory. + */ if (cpudata->xcr0_mask != 0) { cpudata->hxcr0 = rdxcr(0); wrxcr(0, cpudata->gxcr0); } + + /* + * Load any FPU registers that the guest might have been using + * from memory. + */ + /* TODO: should we use *XSAVE64 here? */ + fpu_area_restore(&cpudata->gfpu, cpudata->xcr0_mask, false); } static void @@ -2106,13 +2127,30 @@ vmx_vcpu_guest_fpu_leave(struct nvmm_cpu { struct vmx_cpudata *cpudata = vcpu->cpudata; + /* + * Save any extended CPU state that might be in use by the + * guest to memory. + */ + /* TODO: should we use *XSAVE64 here? */ + fpu_area_save(&cpudata->gfpu, cpudata->xcr0_mask, false); + + /* + * Set XCR0 back to list the XSAVE user state components that + * the NetBSD host uses. + */ if (cpudata->xcr0_mask != 0) { cpudata->gxcr0 = rdxcr(0); wrxcr(0, cpudata->hxcr0); } - /* TODO: should we use *XSAVE64 here? */ - fpu_area_save(&cpudata->gfpu, cpudata->xcr0_mask, false); + /* + * Restore any extended CPU state that might be in use by + * userland in this thread from memory, and allow interrupts + * whose handlers might use the XSAVE area -- this restores the + * IPL. + * + * After this point, we must not use extended CPU state. + */ fpu_kern_leave(); } @@ -3670,9 +3708,11 @@ vmx_init(void) * physical CPU. Hypervisor software can use * nvmm_vcpu_configure(NVMM_VCPU_CONF_XCR0_MASK) to restrict * the available features on a per-vCPU basis, e.g. in order to - * limit guests to compatible features for migration. + * limit guests to compatible features for migration. We mask + * off bit 63 which is reserved for future extension which we + * don't understand. */ - vmx_xcr0_mask = x86_xsave_features; + vmx_xcr0_mask = x86_xsave_features & __BITS(62,0); /* Init the max basic CPUID leaf. */ vmx_cpuid_max_basic = uimin(cpuid_level, VMX_CPUID_MAX_BASIC);