root/sys/dev/nvmm/x86/nvmm_x86.c
/*      $NetBSD: nvmm_x86.c,v 1.25 2026/07/15 01:22:21 riastradh Exp $  */

/*
 * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net
 * All rights reserved.
 *
 * This code is part of the NVMM hypervisor.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nvmm_x86.c,v 1.25 2026/07/15 01:22:21 riastradh Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/cpu.h>

#include <uvm/uvm_extern.h>

#include <x86/cputypes.h>
#include <x86/pat.h>
#include <x86/specialreg.h>

#include <dev/nvmm/nvmm.h>
#include <dev/nvmm/nvmm_internal.h>
#include <dev/nvmm/x86/nvmm_x86.h>

/*
 * Code shared between x86-SVM and x86-VMX.
 */

const struct nvmm_x64_state nvmm_x86_reset_state = {
        .segs = {
                [NVMM_X64_SEG_ES] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_CS] = {
                        .selector = 0xF000,
                        .base = 0xFFFF0000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_SS] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_DS] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_FS] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_GS] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 3,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_GDT] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 2,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_IDT] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = 2,
                                .s = 1,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_LDT] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = SDT_SYSLDT,
                                .s = 0,
                                .p = 1,
                        }
                },
                [NVMM_X64_SEG_TR] = {
                        .selector = 0x0000,
                        .base = 0x00000000,
                        .limit = 0xFFFF,
                        .attrib = {
                                .type = SDT_SYS286BSY,
                                .s = 0,
                                .p = 1,
                        }
                },
        },

        .gprs = {
                [NVMM_X64_GPR_RAX] = 0x00000000,
                [NVMM_X64_GPR_RCX] = 0x00000000,
                [NVMM_X64_GPR_RDX] = 0x00000600,
                [NVMM_X64_GPR_RBX] = 0x00000000,
                [NVMM_X64_GPR_RSP] = 0x00000000,
                [NVMM_X64_GPR_RBP] = 0x00000000,
                [NVMM_X64_GPR_RSI] = 0x00000000,
                [NVMM_X64_GPR_RDI] = 0x00000000,
                [NVMM_X64_GPR_R8] = 0x00000000,
                [NVMM_X64_GPR_R9] = 0x00000000,
                [NVMM_X64_GPR_R10] = 0x00000000,
                [NVMM_X64_GPR_R11] = 0x00000000,
                [NVMM_X64_GPR_R12] = 0x00000000,
                [NVMM_X64_GPR_R13] = 0x00000000,
                [NVMM_X64_GPR_R14] = 0x00000000,
                [NVMM_X64_GPR_R15] = 0x00000000,
                [NVMM_X64_GPR_RIP] = 0x0000FFF0,
                [NVMM_X64_GPR_RFLAGS] = 0x00000002,
        },

        .crs = {
                [NVMM_X64_CR_CR0] = 0x60000010,
                [NVMM_X64_CR_CR2] = 0x00000000,
                [NVMM_X64_CR_CR3] = 0x00000000,
                [NVMM_X64_CR_CR4] = 0x00000000,
                [NVMM_X64_CR_CR8] = 0x00000000,
                [NVMM_X64_CR_XCR0] = 0x00000001,
        },

        .drs = {
                [NVMM_X64_DR_DR0] = 0x00000000,
                [NVMM_X64_DR_DR1] = 0x00000000,
                [NVMM_X64_DR_DR2] = 0x00000000,
                [NVMM_X64_DR_DR3] = 0x00000000,
                [NVMM_X64_DR_DR6] = 0xFFFF0FF0,
                [NVMM_X64_DR_DR7] = 0x00000400,
        },

        .msrs = {
                [NVMM_X64_MSR_EFER] = 0x00000000,
                [NVMM_X64_MSR_STAR] = 0x00000000,
                [NVMM_X64_MSR_LSTAR] = 0x00000000,
                [NVMM_X64_MSR_CSTAR] = 0x00000000,
                [NVMM_X64_MSR_SFMASK] = 0x00000000,
                [NVMM_X64_MSR_KERNELGSBASE] = 0x00000000,
                [NVMM_X64_MSR_SYSENTER_CS] = 0x00000000,
                [NVMM_X64_MSR_SYSENTER_ESP] = 0x00000000,
                [NVMM_X64_MSR_SYSENTER_EIP] = 0x00000000,
                [NVMM_X64_MSR_PAT] =
                    PATENTRY(0, PAT_WB) | PATENTRY(1, PAT_WT) |
                    PATENTRY(2, PAT_UCMINUS) | PATENTRY(3, PAT_UC) |
                    PATENTRY(4, PAT_WB) | PATENTRY(5, PAT_WT) |
                    PATENTRY(6, PAT_UCMINUS) | PATENTRY(7, PAT_UC),
                [NVMM_X64_MSR_TSC] = 0,
        },

        .intr = {
                .int_shadow = 0,
                .int_window_exiting = 0,
                .nmi_window_exiting = 0,
                .evt_pending = 0,
        },

        .fpu = {
                .fx_cw = 0x0040,
                .fx_sw = 0x0000,
                .fx_tw = 0x55,
                .fx_zero = 0x55,
                .fx_mxcsr = 0x1F80,
        }
};

const struct nvmm_x86_cpuid_mask nvmm_cpuid_00000001 = {
        .eax = ~0,
        .ebx = ~0,
        .ecx =
            CPUID2_SSE3 |
            CPUID2_PCLMULQDQ |
            /* CPUID2_DTES64 excluded */
            /* CPUID2_MONITOR excluded */
            /* CPUID2_DS_CPL excluded */
            /* CPUID2_VMX excluded */
            /* CPUID2_SMX excluded */
            /* CPUID2_EST excluded */
            /* CPUID2_TM2 excluded */
            CPUID2_SSSE3 |
            /* CPUID2_CNXTID excluded */
            /* CPUID2_SDBG excluded */
            CPUID2_FMA |
            CPUID2_CX16 |
            /* CPUID2_XTPR excluded */
            /* CPUID2_PDCM excluded */
            /* CPUID2_PCID excluded, but re-included in VMX */
            /* CPUID2_DCA excluded */
            CPUID2_SSE41 |
            CPUID2_SSE42 |
            /* CPUID2_X2APIC excluded */
            CPUID2_MOVBE |
            CPUID2_POPCNT |
            /* CPUID2_DEADLINE excluded */
            CPUID2_AESNI |
            CPUID2_XSAVE |
            CPUID2_OSXSAVE |
            CPUID2_AVX |
            CPUID2_F16C |
            CPUID2_RDRAND,
            /* CPUID2_RAZ excluded */
        .edx =
            CPUID_FPU |
            CPUID_VME |
            CPUID_DE |
            CPUID_PSE |
            CPUID_TSC |
            CPUID_MSR |
            CPUID_PAE |
            /* CPUID_MCE excluded */
            CPUID_CX8 |
            CPUID_APIC |
            CPUID_SEP |
            /* CPUID_MTRR excluded */
            CPUID_PGE |
            /* CPUID_MCA excluded */
            CPUID_CMOV |
            CPUID_PAT |
            CPUID_PSE36 |
            /* CPUID_PSN excluded */
            CPUID_CLFSH |
            /* CPUID_DS excluded */
            /* CPUID_ACPI excluded */
            CPUID_MMX |
            CPUID_FXSR |
            CPUID_SSE |
            CPUID_SSE2 |
            CPUID_SS |
            CPUID_HTT |
            /* CPUID_TM excluded */
            CPUID_PBE
};

const struct nvmm_x86_cpuid_mask nvmm_cpuid_00000007 = {
        .eax = ~0,
        .ebx =
            CPUID_SEF_FSGSBASE |
            /* CPUID_SEF_TSC_ADJUST excluded */
            /* CPUID_SEF_SGX excluded */
            CPUID_SEF_BMI1 |
            /* CPUID_SEF_HLE excluded */
            CPUID_SEF_AVX2 |
            CPUID_SEF_FDPEXONLY |
            CPUID_SEF_SMEP |
            CPUID_SEF_BMI2 |
            CPUID_SEF_ERMS |
            /* CPUID_SEF_INVPCID excluded, but re-included in VMX */
            /* CPUID_SEF_RTM excluded */
            /* CPUID_SEF_QM excluded */
            CPUID_SEF_FPUCSDS |
            /* CPUID_SEF_MPX excluded */
            /* CPUID_SEF_PQE excluded */
            /* CPUID_SEF_AVX512F excluded */
            /* CPUID_SEF_AVX512DQ excluded */
            CPUID_SEF_RDSEED |
            CPUID_SEF_ADX |
            CPUID_SEF_SMAP |
            /* CPUID_SEF_AVX512_IFMA excluded */
            CPUID_SEF_CLFLUSHOPT |
            CPUID_SEF_CLWB,
            /* CPUID_SEF_PT excluded */
            /* CPUID_SEF_AVX512PF excluded */
            /* CPUID_SEF_AVX512ER excluded */
            /* CPUID_SEF_AVX512CD excluded */
            /* CPUID_SEF_SHA excluded */
            /* CPUID_SEF_AVX512BW excluded */
            /* CPUID_SEF_AVX512VL excluded */
        .ecx =
            CPUID_SEF_PREFETCHWT1 |
            /* CPUID_SEF_AVX512_VBMI excluded */
            CPUID_SEF_UMIP |
            /* CPUID_SEF_PKU excluded */
            /* CPUID_SEF_OSPKE excluded */
            /* CPUID_SEF_WAITPKG excluded */
            /* CPUID_SEF_AVX512_VBMI2 excluded */
            /* CPUID_SEF_CET_SS excluded */
            CPUID_SEF_GFNI |
            CPUID_SEF_VAES |
            CPUID_SEF_VPCLMULQDQ |
            /* CPUID_SEF_AVX512_VNNI excluded */
            /* CPUID_SEF_AVX512_BITALG excluded */
            /* CPUID_SEF_AVX512_VPOPCNTDQ excluded */
            /* CPUID_SEF_MAWAU excluded */
            /* CPUID_SEF_RDPID excluded */
            CPUID_SEF_CLDEMOTE |
            CPUID_SEF_MOVDIRI |
            CPUID_SEF_MOVDIR64B,
            /* CPUID_SEF_SGXLC excluded */
            /* CPUID_SEF_PKS excluded */
        .edx =
            /* CPUID_SEF_AVX512_4VNNIW excluded */
            /* CPUID_SEF_AVX512_4FMAPS excluded */
            CPUID_SEF_FSRM |
            /* CPUID_SEF_AVX512_VP2INTERSECT excluded */
            /* CPUID_SEF_SRBDS_CTRL excluded */
            CPUID_SEF_MD_CLEAR |
            /* CPUID_SEF_TSX_FORCE_ABORT excluded */
            CPUID_SEF_SERIALIZE
            /* CPUID_SEF_HYBRID excluded */
            /* CPUID_SEF_TSXLDTRK excluded */
            /* CPUID_SEF_CET_IBT excluded */
            /* CPUID_SEF_IBRS excluded */
            /* CPUID_SEF_STIBP excluded */
            /* CPUID_SEF_L1D_FLUSH excluded */
            /* CPUID_SEF_ARCH_CAP excluded */
            /* CPUID_SEF_CORE_CAP excluded */
            /* CPUID_SEF_SSBD excluded */
};

const struct nvmm_x86_cpuid_mask nvmm_cpuid_80000001 = {
        .eax = ~0,
        .ebx = ~0,
        .ecx =
            CPUID_LAHF |
            CPUID_CMPLEGACY |
            /* CPUID_SVM excluded */
            /* CPUID_EAPIC excluded */
            CPUID_ALTMOVCR0 |
            CPUID_ABM |
            CPUID_SSE4A |
            CPUID_MISALIGNSSE |
            CPUID_3DNOWPF |
            /* CPUID_OSVW excluded */
            /* CPUID_IBS excluded */
            CPUID_XOP |
            /* CPUID_SKINIT excluded */
            /* CPUID_WDT excluded */
            /* CPUID_LWP excluded */
            CPUID_FMA4 |
            CPUID_TCE |
            /* CPUID_NODEID excluded */
            CPUID_TBM |
            CPUID_TOPOEXT,
            /* CPUID_PCEC excluded */
            /* CPUID_PCENB excluded */
            /* CPUID_SPM excluded */
            /* CPUID_DBE excluded */
            /* CPUID_PTSC excluded */
            /* CPUID_L2IPERFC excluded */
            /* CPUID_MWAITX excluded */
        .edx =
            CPUID_FPU |
            CPUID_VME |
            CPUID_DE |
            CPUID_PSE |
            CPUID_TSC |
            CPUID_MSR |
            CPUID_PAE |
            /* CPUID_MCE excluded */
            CPUID_CX8 |
            CPUID_APIC |
            CPUID_SYSCALL |
            /* CPUID_MTRR excluded */
            CPUID_PGE |
            /* CPUID_MCA excluded */
            CPUID_CMOV |
            CPUID_PAT |
            CPUID_PSE36 |
            CPUID_XD |
            CPUID_MMXX |
            CPUID_MMX |
            CPUID_FXSR |
            CPUID_FFXSR |
            CPUID_PAGE1GB |
            /* CPUID_RDTSCP excluded */
            CPUID_EM64T |
            CPUID_3DNOW2 |
            CPUID_3DNOW
};

const struct nvmm_x86_cpuid_mask nvmm_cpuid_80000007 = {
        .eax = 0,
        .ebx = 0,
        .ecx = 0,
        .edx = CPUID_APM_ITSC
};

const struct nvmm_x86_cpuid_mask nvmm_cpuid_80000008 = {
        .eax = ~0,
        .ebx =
            CPUID_CAPEX_CLZERO |
            /* CPUID_CAPEX_IRPERF excluded */
            CPUID_CAPEX_XSAVEERPTR |
            /* CPUID_CAPEX_RDPRU excluded */
            /* CPUID_CAPEX_MCOMMIT excluded */
            CPUID_CAPEX_WBNOINVD,
        .ecx = ~0, /* TODO? */
        .edx = 0
};

bool
nvmm_x86_pat_validate(uint64_t val)
{
        uint8_t *pat = (uint8_t *)&val;
        size_t i;

        for (i = 0; i < 8; i++) {
                if (__predict_false(pat[i] & ~__BITS(2,0)))
                        return false;
                if (__predict_false(pat[i] == 2 || pat[i] == 3))
                        return false;
        }

        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.  Bits that
 *      were not previously advertised in CPUID[EAX=0x0d,ECX=0].EDX:EAX
 *      MUST NOT be set.
 */
uint32_t
nvmm_x86_xsave_size(uint64_t xcr0)
{
        uint32_t totalsize = sizeof(struct xsave_header);
        unsigned i;

        /*
         * Caller must not pass bit 63 until the architectural
         * extension mechanism it is reserved for has been defined.
         * Caller must also not pass any bits that the CPU has not
         * advertised support for.
         */
        KASSERTMSG((xcr0 & ~__BITS(62, 0)) == 0, "xcr0=0x%"PRIx64, xcr0);
        KASSERTMSG((xcr0 & ~x86_xsave_features) == 0,
            "xcr0=0x%"PRIx64" x86_xsave_features=0x%"PRIx64,
            xcr0, x86_xsave_features);

        /*
         * Bit 0 is for x87 state, and bit 1 is for SSE state, for
         * which (a) space is already included in the 512-byte legacy
         * area of XSAVE (which is included in `struct xsave_header'),
         * and (b) we can't query the CPUID[EAX=0x0d,ECX=i] leaf
         * because for i=0 and i=1 the leaf serves a different purpose.
         *
         * We stop before bit 63 because it is reserved for future
         * architectural extension which is not yet defined.
         */
        CTASSERT(sizeof(struct xsave_header) == 512 + 64);
        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;

                /*
                 * 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 totalsize;
}

/*
 * nvmm_x86_xcr0_valid(xcr0, xcr0_mask)
 *
 *      True if xcr0 is a valid content for the XCR0 register, false if
 *      not.
 *
 *      - Bits outside xcr0_mask must not be set.
 *      - Bit for x87 state must be set.
 *      - The bit for the YMM_Hi128 state (high 128-bit halves of the
 *        ymm registers) can only be set if the SSE state (xmm
 *        registers, a.k.a. low 128-bit halves of the ymm registers) is
 *        also enabled.
 *
 *      What about ZMM_Hi256?  According to even recent architecture
 *      manuals from Intel and AMD, this does _not_ seem to require
 *      that SSE or YMM_Hi128 be enabled for ZMM_Hi256 to be enabled.
 *      Not requiring this is sensible because it enables XSAVE
 *      extensions to work safely even if software doesn't know such
 *      details.  But I have not tested on real hardware whether
 *      ZMM_Hi256 actually does require YMM_Hi128 or SSE to be enabled
 *      in XCR0.
 *
 *      References:
 *
 *      - Intel 64 and IA-32 Architectures Software Developer's Manual,
 *        Volume 2 (2A, 2B, 2C, & 2D): Instruction Set Reference, A-Z,
 *        Intel, Order Number: 325383-092US, June 2026, Sec. 6.1
 *        `Instructions (W-Z)', `XSETBV---Set Extended Control
 *        Register', pp. 6-70 -- 6-71.
 *        https://cdrdv2.intel.com/v1/dl/getContent/671110
 *        https://web.archive.org/web/20260711031355/https://cdrdv2-public.intel.com/922478/325383-092-sdm-vol-2abcd.pdf
 *
 *      - AMD64 Architecture Programmer's Manual, Volume 4: 128-bit,
 *        256-bit, and 512-bit Media Instructions, Advanced Micro
 *        Devices, Publication No. 26568, Rev. 3.26, January 2026,
 *        pp. 1576--1577.
 *        https://docs.amd.com/v/u/en-US/26568_3.26_APM_Vol4
 *        https://web.archive.org/web/20260714233827/https://docs.amd.com/api/khub/documents/ioQiNhSqxlMkRaU~IyGQYQ/content?Ft-Calling-App=ft%2Fturnkey-portal&Ft-Calling-App-Version=5.3.24
 */
bool
nvmm_x86_xcr0_valid(uint64_t xcr0, uint64_t xcr0_mask)
{

        /*
         * Refuse setting any bits in XCR0 that are disabled in the
         * vCPU configuration.
         */
        if (__predict_false(xcr0 & ~xcr0_mask))
                return false;

        /*
         * Refuse clearing the x87 state component in XCR0, as the
         * physical CPU would.
         */
        if (__predict_false((xcr0 & XCR0_X87) == 0))
                return false;

        /*
         * Refuse setting the YMM_Hi128 state component in XCR0 if the
         * SSE/XMM state component is not also enabled, like the
         * physical CPU would.
         */
        if (__predict_false((xcr0 & (XCR0_YMM_Hi128|XCR0_SSE)) ==
                XCR0_YMM_Hi128))
                return false;

        /* Looks good! */
        return true;
}

/*
 * nvmm_x86_munge_xcr0(xcr0, xcr0_mask)
 *
 *      Map an arbitrary 64-bit word into a plausible value for XCR0
 *      under the given mask (which must itself be a plausible value
 *      for XCR0).  This function MUST NOT be called on CPUs without
 *      XSAVE at all.
 */
uint64_t
nvmm_x86_munge_xcr0(uint64_t xcr0, uint64_t xcr0_mask)
{
        uint64_t origxcr0 __diagused = xcr0;

        KASSERT(xcr0_mask != 0);
        KASSERTMSG(xcr0_mask & XCR0_X87, "xcr0_mask=0x%"PRIx64, xcr0_mask);
        KASSERTMSG((xcr0_mask & (XCR0_YMM_Hi128|XCR0_SSE)) != XCR0_YMM_Hi128,
            "xcr0_mask=0x%"PRIx64, xcr0_mask);

        /*
         * Clear any bits not in xcr0_mask.
         */
        xcr0 &= xcr0_mask;

        /*
         * Set the mandatory x87 bit.
         */
        xcr0 |= XCR0_X87;

        /*
         * If YMM_Hi128 (high 128-bit halves of ymmN) is enabled, then
         * SSE (xmmN, a.k.a. low 128-bit halves of ymmN) must also be
         * enabled.
         *
         * Note: The same may not apply to zmmN; see above about
         * nvmm_x86_xcr0_valid.
         */
        if (__predict_false((xcr0 & (XCR0_YMM_Hi128|XCR0_SSE)) ==
                XCR0_YMM_Hi128))
                xcr0 |= XCR0_SSE;

        KASSERTMSG(nvmm_x86_xcr0_valid(xcr0, xcr0_mask),
            "origxcr0=0x%"PRIx64" xcr0=0x%"PRIx64" xcr0_mask=0x%"PRIx64,
            origxcr0, xcr0, xcr0_mask);

        return xcr0;
}