[RFC PATCH 00/55] KVM: x86: Hyper-V VSM (Virtual Trust Levels) on VM planes

From: David Woodhouse

Date: Sat Oct 17 2026 - 08:39:46 EST


From: David Woodhouse <dwmw@xxxxxxxxxxxx>

Windows uses Virtualization Based Security for Credential Guard and
friends, and enables it by default in current releases. A guest with
VBS enabled runs its secure kernel in VTL1, a higher privilege level
which the hypervisor has to keep isolated from the normal kernel in
VTL0. KVM has no concept of that today, so Windows guests either lose
VBS or emulate it with nested virtualization, which is slower and
loses features.

There have been completely in-kernel implementations of VBS, which
did unnatural things all within a single kvm_vcpu (four APICs, etc.)

This is an attempt to build it more cleanly, more in userspace, based
on KVM planes (https://lwn.net/Articles/1087590/).

The behavioural reference was the in-kernel VSM implementation in the
Nitro Hypervisor, complete with its latest compatibility fixes since
it was last posted — largely turned into a bunch of kvm unit
tests. The result boots Windows Server 2016 through 2025 with
Credential Guard active, on both Intel and AMD hosts, with VTL1
protecting its memory from VTL0. With hibernate/resume tested, since
that part has always been extra fun.

For now I've structured the userspace part as a separable library
which can be pulled into QEMU or other VMMs. Partly because QEMU
doesn't want it when AI did the typing (but *might* tolerate it as an
external library), and partly because I think it might make sense to
have a more permissively licensed standard implementation for how
KVM-based VMMs implement VSM, and have some input from the Microsoft
side and some improved expectation that it'll *keep* working with
Windows and not keep breaking for every new release. But that's a
future conversation. For now I'm just preempting the question, "why
does it look like that?".

• https://git.infradead.org/?p=users/dwmw2/linux.git;a=shortlog;h=refs/heads/vsm
• https://git.infradead.org/?p=users/dwmw2/qemu.git;a=shortlog;h=refs/heads/vsm
• https://git.infradead.org/?p=users/dwmw2/kvm-unit-tests.git;a=shortlog;h=refs/heads/vsm

Pulled into my base tree on v7.1-rc7 (not reposted here):

• KVM planes itself (Jörg's latest from June)
https://lore.kernel.org/kvm/20260608144252.351443-1-joro@xxxxxxxxxx/
https://github.com/joergroedel/linux/tree/kvm-planes-v7.1
• Paolo's memory protection attributes v3
https://lore.kernel.org/kvm/20260918135030.171564-1-pbonzini@xxxxxxxxxx/
• Melody Wang's SEV-SNP #HV doorbell / Restricted Injection series
https://lore.kernel.org/all/cover.1777048608.git.huibo.wang@xxxxxxx/
• A few fixes to the above ride just above them in the git tree,
each citing the report it addresses; not part of this posting

Generic fixes and features in KVM before we get to VSM itself:

• Userspace opt-in (KVM_CAP_PLANES) and plane run-state ioctls;
MP state, guest-debug, KVM_TRANSLATE, SETUP_MCE and a filtered
KVM_ENABLE_CAP on plane vCPU fds; unwind of a failed
KVM_CREATE_PLANE
• Plane mechanics: per-plane request fan-out, kicking the twin
that is actually running, XCR0 and TSC matching shared across
twins, runtime CPUID per plane, APICv inhibited while planes
are enabled (for now).
• Per-plane memory attributes and overlays, and instruction state
on the memory-fault exit, for the VMM to build intercept
messages from
• Fixes found along the way: KVM_TRANSLATE reporting a real
write-access walk, SVM keeping TPR[3:0] across exits without a
guest CR8 write

Finally we get to the actual Hyper-V VSM bits:

• Hypercall forwarding to userspace (KVM_CAP_HYPERV_FORWARD_HCALL),
with completion or injected #UD, for the VSM hypercalls KVM
doesn't handle itself
• HvCallVtlCall/HvCallVtlReturn switching between planes in the
kernel; forced VTL entry gated on interrupt deliverability and
re-armed when a plane suspends with IRR set
• Per-plane Hyper-V state: GUEST_OS_ID/HYPERCALL MSRs, the
reference TSC page, SynIC and event routing, TLB flushes
• VTL register intercepts, including the descriptor-table load
traps Credential Guard requires
• The suspended-VP state (from Nikolas Wipper's series), refusal of
SendIpi to a higher VTL, XMM fast hypercall output
• Selftests for both halves; a hyperv_vsm suite for kvm-unit-tests
covering VTL call/return, protections, intercepts and eventing
follows separately

The four WIP-marked patches need finishing and splitting; they are
included because the series does not function without them. This is
very much an RFC.

Held back in the git tree for a follow-up posting:

• KVM_TRANSLATE2 and the page-walker flags/status work (also
Nikolas's), and the HvCallTranslateVirtualAddress handling built
on it
• TLB flush inhibit (TlbLocked)

Not done at all yet: VINA, and migration of mid-VTL-switch state
(the forced-entry and captured-register state has no GET/SET
exposure). The interaction with SEV-SNP VMPLs, the other consumer of
planes, is untested from this side.

Mushahid also has a version which boots Windows 11 on arm64, which
we'll reconcile when some of the dust settles.

I think this will take a while to reconcile and merge, even after
the base dependencies (planes, memattrs, etc.) are in. I'd like to
start settling the basic functionality so we can start preparing
to merge it in little pieces, if we can reach a general consensus.


David Woodhouse (44):
KVM: x86/mmu: Move ACC_* masks from spte.h to mmu.h
KVM: x86: Let userspace opt in to multiple planes with KVM_CAP_PLANES
KVM: selftests: Add x86 planes lifecycle test
KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL
KVM: selftests: Add Hyper-V hypercall forwarding test
KVM: Add KVM_GET/SET_PLANE_RUN_STATE vCPU ioctls
KVM: selftests: Cover plane run-state ioctls in planes_test
KVM: x86: hyper-v: Let userspace fault a forwarded hypercall
KVM: x86: Allow MP state ioctls on plane vCPU fds
KVM: x86: Filter KVM_ENABLE_CAP on plane vCPU fds
KVM: x86: initialise Hyper-V state on every plane vCPU
KVM: implement per-plane memory attributes
KVM: x86: hyper-v: bank GUEST_OS_ID and HYPERCALL MSRs per plane
KVM: x86: hyper-v: bank the reference TSC page per plane
KVM: x86: allow KVM_SET_GUEST_DEBUG on plane vCPU fds
KVM: x86: hyper-v: flush the calling plane's TLB on flush hypercalls
KVM: x86/mmu: free obsolete roots of all planes
KVM: x86: hyperv: don't arm PV EOI for a higher plane's VP assist page
KVM: x86: allow KVM_TRANSLATE on plane vCPU fds
KVM: x86: hyper-v: handle VTL call/return in the kernel
KVM: x86: load each plane twin while updating it after KVM_SET_CPUID
WIP: userspace VTL interrupt entry and register intercepts
WIP: KVM: x86: deliver events to suspended higher planes
WIP: KVM: per-plane memory overlays, richer protection-fault exits
WIP: KVM: x86: VSM event-delivery parity fixes from Windows boot
triage
KVM: planes: fan out per-plane requests to twin vCPUs
KVM: x86: inhibit APICv when VM planes are enabled
KVM: x86: hyperv: divert LLDT and LTR into register intercepts
KVM: planes: Unwind published state on KVM_CREATE_PLANE failure
KVM: planes: Kick the running twin's pCPU, not the request target's
KVM: planes: Keep current_vcpu in sync in vcpu_load()
KVM: x86: Report a real write-access walk from KVM_TRANSLATE
KVM: planes: Share XCR0 across plane twins
KVM: x86: hyperv: Refuse a synthetic IPI to a higher VTL
KVM: x86: Gate forced VTL entry on interrupt deliverability
KVM: selftests: Test a plane suspending with an undelivered interrupt
KVM: x86: hyperv: Advertise XMM fast hypercall output
KVM: SVM: Carry instruction state on reflected plane-protection faults
KVM: SVM: Trap descriptor-table loads for VTL register intercepts
KVM: SVM: Keep TPR[3:0] across exits without a guest CR8 write
KVM: selftests: Add Hyper-V reference TSC page memory attribute test
KVM: SVM: Hedge the v_tpr_entry comment
KVM: Check every plane's protections before caching a gfn->hva
KVM: planes: Annotate the lockless twin-publication protocol

Mushahid Hussain (9):
KVM: x86: Leave TSC matching to the plane 0 vCPUs
KVM: x86: hyperv: Track the reference TSC page per plane
KVM: x86: hyperv: Update the reference TSC page past protections
KVM: x86: hyperv: Publish an added reference TSC page at once
KVM: x86: hyperv: Harden the per-plane reference TSC page updates
KVM: x86: Derive runtime CPUID bits from the executing plane
KVM: selftests: Test hugepage sizing over mixed protection attributes
KVM: x86: hyperv: Do not skip a moved vCPU when completing a hypercall
KVM: x86: Allow KVM_X86_SETUP_MCE on plane vCPUs

Nikolas Wipper (1):
KVM: x86: Implement Hyper-V's vCPU suspended state

Paolo Bonzini (1):
KVM: selftests: Add VM plane infrastructure and tests

Documentation/virt/kvm/api.rst | 241 ++++-
arch/x86/hyperv/hv_apic.c | 3 +-
arch/x86/include/asm/kvm-x86-ops.h | 1 +
arch/x86/include/asm/kvm_host.h | 140 ++-
arch/x86/kvm/cpuid.c | 139 ++-
arch/x86/kvm/cpuid.h | 6 +-
arch/x86/kvm/emulate.c | 21 +
arch/x86/kvm/hyperv.c | 995 ++++++++++++++++--
arch/x86/kvm/hyperv.h | 37 +-
arch/x86/kvm/kvm_emulate.h | 10 +
arch/x86/kvm/lapic.c | 85 +-
arch/x86/kvm/mmu.h | 14 +
arch/x86/kvm/mmu/mmu.c | 131 ++-
arch/x86/kvm/mmu/paging_tmpl.h | 7 +-
arch/x86/kvm/mmu/spte.h | 14 -
arch/x86/kvm/mmu/tdp_mmu.c | 23 +-
arch/x86/kvm/svm/nested.c | 16 +
arch/x86/kvm/svm/svm.c | 143 ++-
arch/x86/kvm/svm/svm.h | 16 +-
arch/x86/kvm/vmx/main.c | 13 +-
arch/x86/kvm/vmx/nested.c | 1 +
arch/x86/kvm/vmx/vmx.c | 118 ++-
arch/x86/kvm/vmx/x86_ops.h | 1 +
arch/x86/kvm/x86.c | 668 +++++++++++-
include/hyperv/hvgdk_mini.h | 5 +-
include/hyperv/hvhdk_mini.h | 3 +-
include/linux/kvm_host.h | 149 ++-
include/uapi/linux/kvm.h | 63 +-
tools/include/uapi/linux/kvm.h | 54 +-
tools/testing/selftests/kvm/Makefile.kvm | 7 +
.../testing/selftests/kvm/include/kvm_util.h | 74 ++
.../selftests/kvm/include/x86/processor.h | 1 +
tools/testing/selftests/kvm/lib/kvm_util.c | 85 +-
.../testing/selftests/kvm/lib/x86/processor.c | 15 +
.../testing/selftests/kvm/memory_attributes.c | 6 +-
tools/testing/selftests/kvm/plane_test.c | 137 +++
.../selftests/kvm/x86/hyperv_forward_hcall.c | 144 +++
.../selftests/kvm/x86/hyperv_tsc_page_attrs.c | 129 +++
.../selftests/kvm/x86/memattr_hugepage_test.c | 132 +++
.../selftests/kvm/x86/plane_x86_test.c | 325 ++++++
tools/testing/selftests/kvm/x86/planes_test.c | 142 +++
.../selftests/kvm/x86/planes_vtl_entry_test.c | 188 ++++
virt/kvm/kvm_main.c | 699 +++++++++++-
virt/kvm/pfncache.c | 3 +-
44 files changed, 4956 insertions(+), 248 deletions(-)
create mode 100644 tools/testing/selftests/kvm/plane_test.c
create mode 100644 tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
create mode 100644 tools/testing/selftests/kvm/x86/hyperv_tsc_page_attrs.c
create mode 100644 tools/testing/selftests/kvm/x86/memattr_hugepage_test.c
create mode 100644 tools/testing/selftests/kvm/x86/plane_x86_test.c
create mode 100644 tools/testing/selftests/kvm/x86/planes_test.c
create mode 100644 tools/testing/selftests/kvm/x86/planes_vtl_entry_test.c

--
2.50.1