* [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace
@ 2026-11-11 20:58 David Woodhouse
2026-11-11 20:58 ` [PATCH 1/4] KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL David Woodhouse
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: David Woodhouse @ 2026-11-11 20:58 UTC (permalink / raw)
To: kvm
Cc: linux-kernel, linux-hyperv, linux-kselftest, Paolo Bonzini,
Sean Christopherson, Vitaly Kuznetsov, Marcelo Tosatti,
Joerg Roedel, Tom Lendacky, James Bottomley, Thara Gopinath,
Wei Liu, Michael Kelley, Nicolas Saenz Julienne, Nikolas Wipper,
Mushahid Hussain, Sriram Nambakam, David Woodhouse
From: David Woodhouse <dwmw@amazon.co.uk>
KVM implements a fixed set of Hyper-V hypercalls and fails every other
code with HV_STATUS_INVALID_HYPERCALL_CODE. A VMM that wants to
implement more of the TLFS, such as the VSM hypercalls Windows uses for
Virtualization Based Security, never sees them.
This series adds KVM_CAP_HYPERV_FORWARD_HCALL. As with KVM_CAP_XEN_HVM
and KVM_CAP_EXIT_HYPERCALL, KVM_CHECK_EXTENSION returns a bitmask of
features and KVM_ENABLE_CAP takes the subset to enable, rejecting any
bit the kernel does not report:
- KVM_HYPERV_HCALL_FORWARD_UNKNOWN (bit 0) sends the hypercalls KVM
does not implement, and extended hypercalls in fast form, out
through the existing KVM_EXIT_HYPERV_HCALL exit.
- KVM_HYPERV_HCALL_RAISE_UD (bit 1) lets userspace complete such an
exit by raising #UD at the hypercall instruction, which the TLFS
specifies for some invalid uses, instead of returning a status.
Userspace asks for that with KVM_HYPERV_HCALL_UD in a new 'flags'
field after the hypercall parameters; KVM zeroes it on every exit,
and refuses bits it does not know. The field fits in the existing
union, so struct kvm_run does not grow.
Further mask bits will follow for HvCallVtlCall and HvCallVtlReturn on
VMs with planes, so that KVM can handle VTL transitions in the kernel.
Patch 3 reports HV_X64_HYPERCALL_XMM_OUTPUT_AVAILABLE, since a VMM
handling a forwarded fast hypercall can write the XMM output itself.
Patch 4 adds a selftest.
This is orthogonal to KVM_CAP_HCALL_FAULT_EXIT in the memory attributes
series, which Vitaly asked about on v3 [2]. That one is about a memory
fault *during* the processing of a hypercall KVM does implement, which
it turns into KVM_EXIT_MEMORY_FAULT. This one is about the hypercalls
KVM does not implement at all. A forwarded hypercall goes out to
userspace before KVM touches any guest memory on its behalf, so the two
never meet, and a VMM can use either without the other.
This is the first part of the VSM work posted as an RFC [1] that does
not depend on planes or memory attributes.
The series is based on v7.4-rc2 and also applies cleanly to
kvm-x86/next (cbe54fc3462b). It has been compile-tested on x86_64 with
and without CONFIG_KVM_HYPERV.
Tested on v7.4-rc2 with this series and the KVM_TRANSLATE fix posted
alongside it, on an Intel Xeon 6 system (itself a KVM guest, so VMX
there is nested) and on a bare-metal AMD EPYC (Zen 4) system. The new
hyperv_forward_hcall selftest passes on both, as do hyperv_clock,
hyperv_cpuid, hyperv_extended_hypercalls, hyperv_features, hyperv_ipi
and hyperv_tlb_flush. hyperv_evmcs passes on Intel and hyperv_svm_test
on AMD, each skipping on the other vendor.
[1] https://lore.kernel.org/kvm/20261017122501.610248-1-dwmw2@infradead.org/
[2] https://lore.kernel.org/kvm/kupv77y5yrc.fsf@vkuznets-thinkpadx1carbongen12.rmtcz.csb/
David Woodhouse (4):
KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL
KVM: x86: hyper-v: Let userspace fault a forwarded hypercall
KVM: x86: hyper-v: Advertise XMM fast hypercall output
KVM: selftests: Add Hyper-V hypercall forwarding test
Documentation/virt/kvm/api.rst | 65 +++++
arch/x86/include/asm/kvm_host.h | 2 +
arch/x86/kvm/hyperv.c | 38 ++-
arch/x86/kvm/hyperv.h | 9 +
arch/x86/kvm/x86.c | 14 +
include/uapi/linux/kvm.h | 7 +
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/x86/hyperv_forward_hcall.c | 268 ++++++++++++++++++
8 files changed, 402 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
--
2.50.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/4] KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL
2026-11-11 20:58 [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace David Woodhouse
@ 2026-11-11 20:58 ` David Woodhouse
2026-11-11 20:58 ` [PATCH 2/4] KVM: x86: hyper-v: Let userspace fault a forwarded hypercall David Woodhouse
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: David Woodhouse @ 2026-11-11 20:58 UTC (permalink / raw)
To: kvm
Cc: linux-kernel, linux-hyperv, linux-kselftest, Paolo Bonzini,
Sean Christopherson, Vitaly Kuznetsov, Marcelo Tosatti,
Joerg Roedel, Tom Lendacky, James Bottomley, Thara Gopinath,
Wei Liu, Michael Kelley, Nicolas Saenz Julienne, Nikolas Wipper,
Mushahid Hussain, Sriram Nambakam, David Woodhouse
From: David Woodhouse <dwmw@amazon.co.uk>
A VMM that implements Hyper-V hypercalls KVM does not know about has
no way to receive them. kvm_hv_hypercall() fails an unknown code with
HV_STATUS_INVALID_HYPERCALL_CODE, and only a fixed set (the synthetic
debugger, memory-based extended hypercalls, HvCallPostMessage and the
HvCallSignalEvent fallback) ever exits to userspace.
Add a VM capability whose KVM_CHECK_EXTENSION value is a bitmask of the
hypercall handling features the kernel implements, as KVM_CAP_XEN_HVM
and KVM_CAP_EXIT_HYPERCALL do. KVM_ENABLE_CAP takes the subset to
enable and rejects any bit the kernel does not report, so a VMM can
probe for exactly the behaviour it relies on.
The first feature, KVM_HYPERV_HCALL_FORWARD_UNKNOWN, sends a hypercall
KVM would have failed as unknown, and an extended hypercall in fast
form, out through the existing KVM_EXIT_HYPERV_HCALL. That exit already
carries the control value and both parameter registers, and XMM state
is reachable through the ordinary vCPU state ioctls, so no new exit
format is needed.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
Documentation/virt/kvm/api.rst | 49 +++++++++++++++++++++++++++++++++
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/kvm/hyperv.c | 7 ++++-
arch/x86/kvm/hyperv.h | 8 ++++++
arch/x86/kvm/x86.c | 14 ++++++++++
include/uapi/linux/kvm.h | 4 +++
6 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e95c9cda085d..092f6f9ad8a0 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -7312,6 +7312,17 @@ Hyper-V SynIC state change. Notification is used to remap SynIC
event/message pages and to enable/disable SynIC messages/events processing
in userspace.
+ - KVM_EXIT_HYPERV_HCALL -- a hypercall for userspace to handle.
+
+'input' holds the hypercall control value and 'params' the two parameter
+registers. Userspace sets 'result' to the hypercall result value before
+resuming the vCPU; KVM then writes it to the guest and advances past the
+hypercall instruction. HvCallPostMessage, HvCallSignalEvent for a
+connection with no registered eventfd, the synthetic debugger hypercalls
+and memory-based extended hypercalls exit this way;
+KVM_CAP_HYPERV_FORWARD_HCALL (see section 7.48) adds the hypercalls KVM
+does not implement.
+
- KVM_EXIT_HYPERV_SYNDBG -- synchronously notify user-space about
Hyper-V Synthetic debugger state change. Notification is used to either update
@@ -9181,6 +9192,44 @@ enabled, cmma can't be enabled anymore and pfmfi and the storage key
interpretation are disabled. If cmma has already been enabled or the
hpage_2g module parameter is not set to 1, -EINVAL is returned.
+7.48 KVM_CAP_HYPERV_FORWARD_HCALL
+---------------------------------
+
+:Architectures: x86
+:Target: VM
+:Parameters: args[0] is a bitmask of KVM_HYPERV_HCALL_* features to enable
+:Returns: 0 on success, -EINVAL if args[0] contains an unsupported bit
+
+KVM_CHECK_EXTENSION for this capability returns a bitmask of the Hyper-V
+hypercall handling features that KVM implements. KVM_ENABLE_CAP takes
+the subset that userspace wants active and replaces the previously
+enabled set; it can be called at any time. A bit that KVM_CHECK_EXTENSION
+does not report is rejected with -EINVAL, so a VMM can enable exactly the
+features it was written for and learn which ones the kernel lacks.
+
+The defined bits are::
+
+ #define KVM_HYPERV_HCALL_FORWARD_UNKNOWN (1ULL << 0)
+
+KVM_HYPERV_HCALL_FORWARD_UNKNOWN: a hypercall that KVM would otherwise
+fail with HV_STATUS_INVALID_HYPERCALL_CODE exits to userspace with
+KVM_EXIT_HYPERV_HCALL instead, and an extended hypercall in fast form
+exits the same way instead of failing with HV_STATUS_INVALID_PARAMETER.
+The VMM can then implement hypercalls that KVM does not. The synthetic
+debugger hypercalls (HvCallResetDebugSession, HvCallPostDebugData,
+HvCallRetrieveDebugData) are an exception: while the synthetic debugger
+is not enabled they still fail in KVM, with HV_STATUS_INVALID_PARAMETER
+for the fast form of HvCallPostDebugData and HvCallRetrieveDebugData
+and with HV_STATUS_INVALID_HYPERCALL_CODE otherwise.
+
+For a fast hypercall, 'params' carries RDX and R8 from a 64-bit caller,
+or the EBX:ECX and EDI:ESI pairs from a 32-bit caller. XMM input and
+output registers of a fast hypercall are read and written with the
+ordinary vCPU state ioctls (KVM_GET_XSAVE/KVM_SET_XSAVE or
+KVM_GET_FPU/KVM_SET_FPU) before the vCPU is resumed. Hypercalls that
+KVM implements itself, and the CPUID-enforcement checks KVM applies
+before dispatching a hypercall, are unaffected.
+
8. Other capabilities.
======================
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 1ee4b127004d..fce46ae26137 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1072,6 +1072,8 @@ struct kvm_hv {
struct mutex hv_lock;
u64 hv_guest_os_id;
u64 hv_hypercall;
+ /* KVM_HYPERV_HCALL_* features enabled by KVM_CAP_HYPERV_FORWARD_HCALL */
+ u64 hcall_features;
u64 hv_tsc_page;
enum hv_tsc_page_status hv_tsc_page_status;
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 5d2e3617c952..e72539f9acc9 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2692,12 +2692,17 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
goto hypercall_userspace_exit;
}
case HV_EXT_CALL_QUERY_CAPABILITIES ... HV_EXT_CALL_MAX:
- if (unlikely(hc.fast)) {
+ if (unlikely(hc.fast) &&
+ !kvm_hv_hcall_feature(vcpu->kvm,
+ KVM_HYPERV_HCALL_FORWARD_UNKNOWN)) {
ret = HV_STATUS_INVALID_PARAMETER;
break;
}
goto hypercall_userspace_exit;
default:
+ if (kvm_hv_hcall_feature(vcpu->kvm,
+ KVM_HYPERV_HCALL_FORWARD_UNKNOWN))
+ goto hypercall_userspace_exit;
ret = HV_STATUS_INVALID_HYPERCALL_CODE;
break;
}
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 622a6553e9ac..20d40261c75b 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -205,6 +205,14 @@ static inline bool kvm_hv_hypercall_enabled(struct kvm_vcpu *vcpu)
return vcpu->arch.hyperv_enabled && to_kvm_hv(vcpu->kvm)->hv_guest_os_id;
}
+/* Features reported and accepted by KVM_CAP_HYPERV_FORWARD_HCALL. */
+#define KVM_HYPERV_HCALL_SUPPORTED KVM_HYPERV_HCALL_FORWARD_UNKNOWN
+
+static inline bool kvm_hv_hcall_feature(struct kvm *kvm, u64 feature)
+{
+ return READ_ONCE(to_kvm_hv(kvm)->hcall_features) & feature;
+}
+
int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
void kvm_hv_irq_routing_update(struct kvm *kvm);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 26e7beaff8a0..d8de7742a0da 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2305,6 +2305,11 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_EXIT_HYPERCALL:
r = KVM_EXIT_HYPERCALL_VALID_MASK;
break;
+#ifdef CONFIG_KVM_HYPERV
+ case KVM_CAP_HYPERV_FORWARD_HCALL:
+ r = KVM_HYPERV_HCALL_SUPPORTED;
+ break;
+#endif
case KVM_CAP_SET_GUEST_DEBUG2:
return KVM_GUESTDBG_VALID_MASK;
#ifdef CONFIG_KVM_XEN
@@ -4144,6 +4149,15 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
kvm->arch.hypercall_exit_enabled = cap->args[0];
r = 0;
break;
+#ifdef CONFIG_KVM_HYPERV
+ case KVM_CAP_HYPERV_FORWARD_HCALL:
+ r = -EINVAL;
+ if (cap->args[0] & ~KVM_HYPERV_HCALL_SUPPORTED)
+ break;
+ WRITE_ONCE(to_kvm_hv(kvm)->hcall_features, cap->args[0]);
+ r = 0;
+ break;
+#endif
case KVM_CAP_EXIT_ON_EMULATION_FAILURE:
r = -EINVAL;
if (cap->args[0] & ~1)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 8dff2fc1972e..7194c213a742 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -126,6 +126,9 @@ struct kvm_hyperv_exit {
} u;
};
+/* KVM_CAP_HYPERV_FORWARD_HCALL feature bits */
+#define KVM_HYPERV_HCALL_FORWARD_UNKNOWN (1ULL << 0)
+
struct kvm_xen_exit {
#define KVM_EXIT_XEN_HCALL 1
__u32 type;
@@ -1000,6 +1003,7 @@ struct kvm_enable_cap {
#define KVM_CAP_PPC_COMPAT_CAPS 250
#define KVM_CAP_ARM_PMU_V3_STRICT 251
#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 252
+#define KVM_CAP_HYPERV_FORWARD_HCALL 253
struct kvm_irq_routing_irqchip {
__u32 irqchip;
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/4] KVM: x86: hyper-v: Let userspace fault a forwarded hypercall
2026-11-11 20:58 [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace David Woodhouse
2026-11-11 20:58 ` [PATCH 1/4] KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL David Woodhouse
@ 2026-11-11 20:58 ` David Woodhouse
2026-11-11 20:58 ` [PATCH 3/4] KVM: x86: hyper-v: Advertise XMM fast hypercall output David Woodhouse
2026-11-11 20:58 ` [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test David Woodhouse
3 siblings, 0 replies; 6+ messages in thread
From: David Woodhouse @ 2026-11-11 20:58 UTC (permalink / raw)
To: kvm
Cc: linux-kernel, linux-hyperv, linux-kselftest, Paolo Bonzini,
Sean Christopherson, Vitaly Kuznetsov, Marcelo Tosatti,
Joerg Roedel, Tom Lendacky, James Bottomley, Thara Gopinath,
Wei Liu, Michael Kelley, Nicolas Saenz Julienne, Nikolas Wipper,
Mushahid Hussain, Sriram Nambakam, David Woodhouse
From: David Woodhouse <dwmw@amazon.co.uk>
The TLFS specifies that some hypercalls raise #UD at the hypercall
instruction when they are invalid in the caller's state, rather than
completing with an error status. A VMM implementing such a hypercall
behind KVM_EXIT_HYPERV_HCALL can only return a status, which writes
RAX, advances RIP and completes the call.
Give the hcall member of struct kvm_hyperv_exit a 'flags' field after
the parameters, and add KVM_HYPERV_HCALL_RAISE_UD to
KVM_CAP_HYPERV_FORWARD_HCALL. With it enabled, a completion with
KVM_HYPERV_HCALL_UD set in 'flags' queues a #UD at the unadvanced RIP
and ignores 'result'. It applies to every KVM_EXIT_HYPERV_HCALL
completion, not only to forwarded hypercalls, and 'result' stays a
plain 64-bit value.
KVM zeroes 'flags' when it reports the exit, so a VMM that has never
heard of the field leaves it alone. A completion with any other bit set,
or with KVM_HYPERV_HCALL_UD set while RAISE_UD is not enabled, fails
KVM_RUN with -EINVAL and leaves the completion pending for userspace to
correct. The new field fits within the existing union, whose largest
member is still syndbg, so struct kvm_run does not change size.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
Documentation/virt/kvm/api.rst | 16 ++++++++++++++++
arch/x86/kvm/hyperv.c | 30 +++++++++++++++++++++++++++++-
arch/x86/kvm/hyperv.h | 3 ++-
include/uapi/linux/kvm.h | 3 +++
4 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 092f6f9ad8a0..25ed1027fb08 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -7286,6 +7286,8 @@ EOI was received.
__u64 input;
__u64 result;
__u64 params[2];
+ #define KVM_HYPERV_HCALL_UD (1ULL << 0)
+ __u64 flags;
} hcall;
struct {
__u32 msr;
@@ -7323,6 +7325,12 @@ and memory-based extended hypercalls exit this way;
KVM_CAP_HYPERV_FORWARD_HCALL (see section 7.48) adds the hypercalls KVM
does not implement.
+KVM sets 'flags' to zero when it reports the exit. With that
+capability's KVM_HYPERV_HCALL_RAISE_UD feature enabled, userspace may set
+KVM_HYPERV_HCALL_UD there to raise #UD at the hypercall instruction
+instead of completing the hypercall. KVM_RUN fails with EINVAL, leaving
+the exit pending, if 'flags' holds any other bit.
+
- KVM_EXIT_HYPERV_SYNDBG -- synchronously notify user-space about
Hyper-V Synthetic debugger state change. Notification is used to either update
@@ -9210,6 +9218,7 @@ features it was written for and learn which ones the kernel lacks.
The defined bits are::
#define KVM_HYPERV_HCALL_FORWARD_UNKNOWN (1ULL << 0)
+ #define KVM_HYPERV_HCALL_RAISE_UD (1ULL << 1)
KVM_HYPERV_HCALL_FORWARD_UNKNOWN: a hypercall that KVM would otherwise
fail with HV_STATUS_INVALID_HYPERCALL_CODE exits to userspace with
@@ -9230,6 +9239,13 @@ KVM_GET_FPU/KVM_SET_FPU) before the vCPU is resumed. Hypercalls that
KVM implements itself, and the CPUID-enforcement checks KVM applies
before dispatching a hypercall, are unaffected.
+KVM_HYPERV_HCALL_RAISE_UD: userspace may complete a KVM_EXIT_HYPERV_HCALL
+exit with KVM_HYPERV_HCALL_UD set in 'flags', and KVM then raises #UD at
+the hypercall instruction instead of completing the hypercall, ignoring
+'result'. This is for hypercalls that the TLFS specifies to fault
+rather than fail with a status. When enabled, it applies to every
+KVM_EXIT_HYPERV_HCALL completion, forwarded or not.
+
8. Other capabilities.
======================
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index e72539f9acc9..2a2bc54775fe 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2430,7 +2430,34 @@ static int kvm_hv_hypercall_complete(struct kvm_vcpu *vcpu, u64 result)
static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
{
- return kvm_hv_hypercall_complete(vcpu, vcpu->run->hyperv.u.hcall.result);
+ u64 result = vcpu->run->hyperv.u.hcall.result;
+ u64 flags = vcpu->run->hyperv.u.hcall.flags;
+ u64 valid = 0;
+
+ if (kvm_hv_hcall_feature(vcpu->kvm, KVM_HYPERV_HCALL_RAISE_UD))
+ valid |= KVM_HYPERV_HCALL_UD;
+
+ /*
+ * Fail KVM_RUN on flags KVM does not know (or that were not enabled),
+ * and leave the completion pending so that userspace can fix them.
+ */
+ if (flags & ~valid) {
+ vcpu->arch.complete_userspace_io =
+ kvm_hv_hypercall_complete_userspace;
+ return -EINVAL;
+ }
+
+ /*
+ * The VMM judged the hypercall to fault rather than return a
+ * status: leave RIP at the hypercall instruction and raise #UD
+ * there.
+ */
+ if (flags & KVM_HYPERV_HCALL_UD) {
+ kvm_queue_exception(vcpu, UD_VECTOR);
+ return 1;
+ }
+
+ return kvm_hv_hypercall_complete(vcpu, result);
}
static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
@@ -2716,6 +2743,7 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
vcpu->run->hyperv.u.hcall.input = hc.param;
vcpu->run->hyperv.u.hcall.params[0] = hc.ingpa;
vcpu->run->hyperv.u.hcall.params[1] = hc.outgpa;
+ vcpu->run->hyperv.u.hcall.flags = 0;
vcpu->arch.complete_userspace_io = kvm_hv_hypercall_complete_userspace;
return 0;
}
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 20d40261c75b..7c6a21f02d71 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -206,7 +206,8 @@ static inline bool kvm_hv_hypercall_enabled(struct kvm_vcpu *vcpu)
}
/* Features reported and accepted by KVM_CAP_HYPERV_FORWARD_HCALL. */
-#define KVM_HYPERV_HCALL_SUPPORTED KVM_HYPERV_HCALL_FORWARD_UNKNOWN
+#define KVM_HYPERV_HCALL_SUPPORTED (KVM_HYPERV_HCALL_FORWARD_UNKNOWN | \
+ KVM_HYPERV_HCALL_RAISE_UD)
static inline bool kvm_hv_hcall_feature(struct kvm *kvm, u64 feature)
{
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 7194c213a742..09b8b8c84380 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -113,6 +113,8 @@ struct kvm_hyperv_exit {
__u64 input;
__u64 result;
__u64 params[2];
+#define KVM_HYPERV_HCALL_UD (1ULL << 0)
+ __u64 flags;
} hcall;
struct {
__u32 msr;
@@ -128,6 +130,7 @@ struct kvm_hyperv_exit {
/* KVM_CAP_HYPERV_FORWARD_HCALL feature bits */
#define KVM_HYPERV_HCALL_FORWARD_UNKNOWN (1ULL << 0)
+#define KVM_HYPERV_HCALL_RAISE_UD (1ULL << 1)
struct kvm_xen_exit {
#define KVM_EXIT_XEN_HCALL 1
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/4] KVM: x86: hyper-v: Advertise XMM fast hypercall output
2026-11-11 20:58 [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace David Woodhouse
2026-11-11 20:58 ` [PATCH 1/4] KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL David Woodhouse
2026-11-11 20:58 ` [PATCH 2/4] KVM: x86: hyper-v: Let userspace fault a forwarded hypercall David Woodhouse
@ 2026-11-11 20:58 ` David Woodhouse
2026-11-11 20:58 ` [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test David Woodhouse
3 siblings, 0 replies; 6+ messages in thread
From: David Woodhouse @ 2026-11-11 20:58 UTC (permalink / raw)
To: kvm
Cc: linux-kernel, linux-hyperv, linux-kselftest, Paolo Bonzini,
Sean Christopherson, Vitaly Kuznetsov, Marcelo Tosatti,
Joerg Roedel, Tom Lendacky, James Bottomley, Thara Gopinath,
Wei Liu, Michael Kelley, Nicolas Saenz Julienne, Nikolas Wipper,
Mushahid Hussain, Sriram Nambakam, David Woodhouse
From: David Woodhouse <dwmw@amazon.co.uk>
A fast hypercall may return its output in XMM registers when the
hypervisor reports HV_X64_HYPERCALL_XMM_OUTPUT_AVAILABLE, and Windows
only uses those forms when the bit is set. KVM does not report it in
KVM_GET_SUPPORTED_HV_CPUID, although nothing stops a VMM handling a
forwarded fast hypercall from writing its output through the vCPU FPU
state before resuming the vCPU.
Report the bit. None of the hypercalls KVM implements itself returns
output, so it only matters for hypercalls the VMM handles, where the
VMM writes the output registers.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/x86/kvm/hyperv.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 2a2bc54775fe..0e1d40cf37a9 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2892,6 +2892,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
ent->ebx |= HV_ENABLE_EXTENDED_HYPERCALLS;
ent->edx |= HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE;
+ ent->edx |= HV_X64_HYPERCALL_XMM_OUTPUT_AVAILABLE;
ent->edx |= HV_FEATURE_FREQUENCY_MSRS_AVAILABLE;
ent->edx |= HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE;
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test
2026-11-11 20:58 [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace David Woodhouse
` (2 preceding siblings ...)
2026-11-11 20:58 ` [PATCH 3/4] KVM: x86: hyper-v: Advertise XMM fast hypercall output David Woodhouse
@ 2026-11-11 20:58 ` David Woodhouse
2026-11-11 21:49 ` sashiko-bot
3 siblings, 1 reply; 6+ messages in thread
From: David Woodhouse @ 2026-11-11 20:58 UTC (permalink / raw)
To: kvm
Cc: linux-kernel, linux-hyperv, linux-kselftest, Paolo Bonzini,
Sean Christopherson, Vitaly Kuznetsov, Marcelo Tosatti,
Joerg Roedel, Tom Lendacky, James Bottomley, Thara Gopinath,
Wei Liu, Michael Kelley, Nicolas Saenz Julienne, Nikolas Wipper,
Mushahid Hussain, Sriram Nambakam, David Woodhouse
From: David Woodhouse <dwmw@amazon.co.uk>
Cover KVM_CAP_HYPERV_FORWARD_HCALL:
- the feature mask reports forwarding and RAISE_UD, and rejects bits
it does not report;
- with nothing enabled, an unknown hypercall fails in KVM;
- with forwarding enabled, it exits to userspace in memory-based and
fast form with the control value and parameters, and the guest
sees the status userspace supplies (and, for the fast form, the XMM
output userspace writes through KVM_SET_FPU);
- the result reaches the guest as the full 64-bit value userspace
wrote, bit 63 included;
- KVM_HYPERV_HCALL_UD in 'flags' fails KVM_RUN with EINVAL until
RAISE_UD is enabled, and then raises #UD at the hypercall;
- KVM reports every exit with 'flags' cleared, even after a completion
that set them;
- disabling everything restores the in-kernel failure.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/x86/hyperv_forward_hcall.c | 268 ++++++++++++++++++
2 files changed, 269 insertions(+)
create mode 100644 tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 17a4256e9081..cc01edd07064 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -89,6 +89,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_cpuid
TEST_GEN_PROGS_x86 += x86/hyperv_evmcs
TEST_GEN_PROGS_x86 += x86/hyperv_extended_hypercalls
TEST_GEN_PROGS_x86 += x86/hyperv_features
+TEST_GEN_PROGS_x86 += x86/hyperv_forward_hcall
TEST_GEN_PROGS_x86 += x86/hyperv_ipi
TEST_GEN_PROGS_x86 += x86/hyperv_svm_test
TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush
diff --git a/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c b/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
new file mode 100644
index 000000000000..5afc99ed533c
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test KVM_CAP_HYPERV_FORWARD_HCALL: the feature mask KVM reports and
+ * accepts, Hyper-V hypercalls that KVM does not implement exiting to
+ * userspace in memory-based and fast form, with userspace supplying the
+ * result and the XMM output, and a userspace completion raising #UD
+ * through the exit's flags.
+ */
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "hyperv.h"
+
+/* A hypercall code KVM does not implement (HvCallGetVpRegisters). */
+#define HVCALL_TEST_CODE 0x50
+
+#define FAST_PARAM0 0x1122334455667788ULL
+#define FAST_PARAM1 0x99aabbccddeeff00ULL
+#define XMM_IN_MARKER 0x5157ca11c0ffee00ULL
+#define XMM_OUT_MARKER 0x0de1ba5eba5eba11ULL
+#define USERSPACE_STATUS 0x55AA
+#define FULL_RESULT (BIT_ULL(63) | USERSPACE_STATUS)
+
+enum {
+ STAGE_DISABLED = 1,
+ STAGE_MEMORY,
+ STAGE_FAST,
+ STAGE_RESULT_FULL,
+ STAGE_RAISE_UD,
+ STAGE_FLAGS_CLEARED,
+ STAGE_REDISABLED,
+};
+
+static void guest_code(gpa_t pgs_gpa)
+{
+ __sse128_u in, out;
+ u64 status;
+ u8 vector;
+
+ wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
+ wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);
+
+ /* Nothing enabled: KVM fails the call itself. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status & 0xffff, HV_STATUS_INVALID_HYPERCALL_CODE);
+ GUEST_SYNC(STAGE_DISABLED);
+
+ /* Forwarding enabled: userspace supplies the status. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status, USERSPACE_STATUS);
+ GUEST_SYNC(STAGE_MEMORY);
+
+ /*
+ * Fast form: the register parameters and XMM0 reach userspace, and
+ * the XMM1 value userspace writes reaches the guest.
+ */
+ in.as_u64[0] = XMM_IN_MARKER;
+ in.as_u64[1] = XMM_IN_MARKER + 1;
+ out.as_u64[0] = 0;
+ out.as_u64[1] = 0;
+ write_sse_reg(0, &in.vec);
+ write_sse_reg(1, &out.vec);
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE | HV_HYPERCALL_FAST_BIT,
+ FAST_PARAM0, FAST_PARAM1, &status);
+ read_sse_reg(1, &out.vec);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status, USERSPACE_STATUS);
+ GUEST_ASSERT_EQ(out.as_u64[0], XMM_OUT_MARKER);
+ GUEST_ASSERT_EQ(out.as_u64[1], XMM_OUT_MARKER + 1);
+ GUEST_SYNC(STAGE_FAST);
+
+ /* The result reaches the guest as all 64 bits userspace wrote. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status, FULL_RESULT);
+ GUEST_SYNC(STAGE_RESULT_FULL);
+
+ /* With KVM_HYPERV_HCALL_RAISE_UD, the UD flag faults the call. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT_EQ(vector, UD_VECTOR);
+ GUEST_SYNC(STAGE_RAISE_UD);
+
+ /* The next exit starts with the flags cleared again. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status, USERSPACE_STATUS);
+ GUEST_SYNC(STAGE_FLAGS_CLEARED);
+
+ /* Everything disabled again. */
+ vector = __hyperv_hypercall(HVCALL_TEST_CODE, pgs_gpa + PAGE_SIZE,
+ pgs_gpa + 2 * PAGE_SIZE, &status);
+ GUEST_ASSERT(!vector);
+ GUEST_ASSERT_EQ(status & 0xffff, HV_STATUS_INVALID_HYPERCALL_CODE);
+ GUEST_SYNC(STAGE_REDISABLED);
+
+ GUEST_DONE();
+}
+
+static void check_hcall_exit(struct kvm_vcpu *vcpu, u64 expect_control,
+ u64 expect_in, u64 expect_out)
+{
+ struct kvm_run *run = vcpu->run;
+
+ TEST_ASSERT_EQ(run->exit_reason, KVM_EXIT_HYPERV);
+ TEST_ASSERT_EQ(run->hyperv.type, KVM_EXIT_HYPERV_HCALL);
+ TEST_ASSERT_EQ(run->hyperv.u.hcall.input, expect_control);
+ TEST_ASSERT_EQ(run->hyperv.u.hcall.params[0], expect_in);
+ TEST_ASSERT_EQ(run->hyperv.u.hcall.params[1], expect_out);
+ TEST_ASSERT_EQ(run->hyperv.u.hcall.flags, 0);
+}
+
+/*
+ * Without KVM_HYPERV_HCALL_RAISE_UD, KVM_HYPERV_HCALL_UD is an unknown
+ * flag: KVM_RUN must fail with EINVAL and leave the exit pending.
+ */
+static void check_flags_refused(struct kvm_vcpu *vcpu)
+{
+ int r;
+
+ vcpu->run->hyperv.u.hcall.flags = KVM_HYPERV_HCALL_UD;
+ r = __vcpu_run(vcpu);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_RUN with an unenabled hcall flag: r = %d, errno = %d",
+ r, errno);
+ vcpu->run->hyperv.u.hcall.flags = 0;
+}
+
+static void handle_fast_hcall(struct kvm_vcpu *vcpu)
+{
+ struct kvm_fpu fpu;
+
+ check_hcall_exit(vcpu, HVCALL_TEST_CODE | HV_HYPERCALL_FAST_BIT,
+ FAST_PARAM0, FAST_PARAM1);
+
+ vcpu_fpu_get(vcpu, &fpu);
+ TEST_ASSERT_EQ(*(u64 *)&fpu.xmm[0][0], XMM_IN_MARKER);
+ TEST_ASSERT_EQ(*(u64 *)&fpu.xmm[0][8], XMM_IN_MARKER + 1);
+ *(u64 *)&fpu.xmm[1][0] = XMM_OUT_MARKER;
+ *(u64 *)&fpu.xmm[1][8] = XMM_OUT_MARKER + 1;
+ vcpu_fpu_set(vcpu, &fpu);
+
+ vcpu->run->hyperv.u.hcall.result = USERSPACE_STATUS;
+}
+
+static void test_feature_mask(struct kvm_vm *vm)
+{
+ u64 supported = kvm_check_cap(KVM_CAP_HYPERV_FORWARD_HCALL);
+ int r;
+
+ TEST_ASSERT(supported & KVM_HYPERV_HCALL_FORWARD_UNKNOWN,
+ "KVM_HYPERV_HCALL_FORWARD_UNKNOWN not reported");
+ TEST_ASSERT(supported & KVM_HYPERV_HCALL_RAISE_UD,
+ "KVM_HYPERV_HCALL_RAISE_UD not reported");
+
+ r = __vm_enable_cap(vm, KVM_CAP_HYPERV_FORWARD_HCALL, BIT_ULL(63));
+ TEST_ASSERT(r && errno == EINVAL,
+ "Enabling an undefined feature should fail with EINVAL");
+
+ /* Enabling nothing is allowed. */
+ vm_enable_cap(vm, KVM_CAP_HYPERV_FORWARD_HCALL, 0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ gva_t hcall_page;
+ gpa_t hcall_gpa;
+ struct ucall uc;
+ int stage = STAGE_DISABLED;
+
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_FORWARD_HCALL));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ vcpu_set_hv_cpuid(vcpu);
+
+ test_feature_mask(vm);
+
+ /* Hypercall page plus an input and an output page. */
+ hcall_page = vm_alloc_pages(vm, 3);
+ memset(addr_gva2hva(vm, hcall_page), 0x0, 3 * getpagesize());
+ hcall_gpa = addr_gva2gpa(vm, hcall_page);
+
+ vcpu_args_set(vcpu, 1, hcall_gpa);
+
+ for (;;) {
+ vcpu_run(vcpu);
+
+ if (vcpu->run->exit_reason == KVM_EXIT_HYPERV) {
+ switch (stage) {
+ case STAGE_MEMORY:
+ check_hcall_exit(vcpu, HVCALL_TEST_CODE,
+ hcall_gpa + getpagesize(),
+ hcall_gpa + 2 * getpagesize());
+ vcpu->run->hyperv.u.hcall.result = USERSPACE_STATUS;
+ break;
+ case STAGE_FAST:
+ handle_fast_hcall(vcpu);
+ break;
+ case STAGE_RESULT_FULL:
+ check_hcall_exit(vcpu, HVCALL_TEST_CODE,
+ hcall_gpa + getpagesize(),
+ hcall_gpa + 2 * getpagesize());
+ check_flags_refused(vcpu);
+ vcpu->run->hyperv.u.hcall.result = FULL_RESULT;
+ break;
+ case STAGE_RAISE_UD:
+ check_hcall_exit(vcpu, HVCALL_TEST_CODE,
+ hcall_gpa + getpagesize(),
+ hcall_gpa + 2 * getpagesize());
+ vcpu->run->hyperv.u.hcall.result = USERSPACE_STATUS;
+ vcpu->run->hyperv.u.hcall.flags = KVM_HYPERV_HCALL_UD;
+ break;
+ case STAGE_FLAGS_CLEARED:
+ /* check_hcall_exit() asserts the stale UD is gone. */
+ check_hcall_exit(vcpu, HVCALL_TEST_CODE,
+ hcall_gpa + getpagesize(),
+ hcall_gpa + 2 * getpagesize());
+ vcpu->run->hyperv.u.hcall.result = USERSPACE_STATUS;
+ break;
+ default:
+ TEST_FAIL("Unexpected hypercall exit in stage %d", stage);
+ }
+ continue;
+ }
+
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC:
+ TEST_ASSERT_EQ(uc.args[1], stage);
+ switch (stage) {
+ case STAGE_DISABLED:
+ vm_enable_cap(vm, KVM_CAP_HYPERV_FORWARD_HCALL,
+ KVM_HYPERV_HCALL_FORWARD_UNKNOWN);
+ break;
+ case STAGE_RESULT_FULL:
+ vm_enable_cap(vm, KVM_CAP_HYPERV_FORWARD_HCALL,
+ KVM_HYPERV_HCALL_FORWARD_UNKNOWN |
+ KVM_HYPERV_HCALL_RAISE_UD);
+ break;
+ case STAGE_FLAGS_CLEARED:
+ vm_enable_cap(vm, KVM_CAP_HYPERV_FORWARD_HCALL, 0);
+ break;
+ }
+ stage++;
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ case UCALL_DONE:
+ goto done;
+ default:
+ TEST_FAIL("Unhandled ucall: %ld", uc.cmd);
+ }
+ }
+
+done:
+ TEST_ASSERT_EQ(stage, STAGE_REDISABLED + 1);
+ kvm_vm_free(vm);
+ return 0;
+}
--
2.50.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test
2026-11-11 20:58 ` [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test David Woodhouse
@ 2026-11-11 21:49 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-11 21:49 UTC (permalink / raw)
To: David Woodhouse, kvm; +Cc: linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] KVM: selftests: missing volatile and XMM constraints in
guest_code()
--
commit 10757ad97814a547a0e0ce57f22b5e41e1c14071
Author: David Woodhouse <dwmw@amazon.co.uk>
KVM: selftests: Add Hyper-V hypercall forwarding test
This commit adds a kselftest for KVM_CAP_HYPERV_FORWARD_HCALL covering feature
mask validation, memory-based and fast hypercall exits to userspace, XMM
register output, 64-bit result handling, and KVM_HYPERV_HCALL_UD fault
injection.
> diff --git a/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c b/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
> new file mode 100644
> index 0000000000000..5afc99ed533c8
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86/hyperv_forward_hcall.c
> @@ -0,0 +1,268 @@
[ ... ]
> +static void guest_code(gpa_t pgs_gpa)
> +{
> + __sse128_u in, out;
> + u64 status;
> + u8 vector;
[ ... ]
> + /*
> + * Fast form: the register parameters and XMM0 reach userspace, and
> + * the XMM1 value userspace writes reaches the guest.
> + */
> + in.as_u64[0] = XMM_IN_MARKER;
> + in.as_u64[1] = XMM_IN_MARKER + 1;
> + out.as_u64[0] = 0;
> + out.as_u64[1] = 0;
> + write_sse_reg(0, &in.vec);
> + write_sse_reg(1, &out.vec);
> + vector = __hyperv_hypercall(HVCALL_TEST_CODE | HV_HYPERCALL_FAST_BIT,
> + FAST_PARAM0, FAST_PARAM1, &status);
> + read_sse_reg(1, &out.vec);
> + GUEST_ASSERT(!vector);
> + GUEST_ASSERT_EQ(status, USERSPACE_STATUS);
> + GUEST_ASSERT_EQ(out.as_u64[0], XMM_OUT_MARKER);
> + GUEST_ASSERT_EQ(out.as_u64[1], XMM_OUT_MARKER + 1);
> + GUEST_SYNC(STAGE_FAST);
[Severity: Low]
Can compiler instruction scheduling or XMM register allocation reorder
read_sse_reg() or clobber xmm0 across __hyperv_hypercall()?
x86 KVM selftests are built with -O2 -march=x86-64-v2 (with SSE enabled), and
read_sse_reg() and write_sse_reg() omit both volatile and XMM register
constraints:
tools/testing/selftests/kvm/include/x86/processor.h:read_sse_reg() {
switch (reg) {
...
case 1:
asm("movdqa %%xmm1, %0" : "=m"(*data));
break;
...
}
tools/testing/selftests/kvm/include/x86/processor.h:write_sse_reg() {
switch (reg) {
case 0:
asm("movdqa %0, %%xmm0" : : "m"(*data));
break;
...
}
Because read_sse_reg() has an output operand, no input operands, and no
volatile qualifier, the compiler treats it as a movable computation with no
side effects, and the memory clobber in __hyperv_hypercall() does not prevent
hoisting read_sse_reg(1, &out.vec) ahead of the hypercall since out is a
non-escaping local variable. In addition, because neither write_sse_reg() nor
__hyperv_hypercall() declares XMM register operands or clobbers, the compiler
can use xmm0 when zero-initializing out after write_sse_reg(0, &in.vec),
causing spurious test assertion failures in handle_fast_hcall() or
guest_code().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261111205836.4186584-1-dwmw2@infradead.org?part=4
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-11 21:49 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 20:58 [PATCH 0/4] KVM: x86: hyper-v: Forward unimplemented hypercalls to userspace David Woodhouse
2026-11-11 20:58 ` [PATCH 1/4] KVM: x86: hyper-v: Add KVM_CAP_HYPERV_FORWARD_HCALL David Woodhouse
2026-11-11 20:58 ` [PATCH 2/4] KVM: x86: hyper-v: Let userspace fault a forwarded hypercall David Woodhouse
2026-11-11 20:58 ` [PATCH 3/4] KVM: x86: hyper-v: Advertise XMM fast hypercall output David Woodhouse
2026-11-11 20:58 ` [PATCH 4/4] KVM: selftests: Add Hyper-V hypercall forwarding test David Woodhouse
2026-11-11 21:49 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox