From 98e86378fb4bccc1ed020cdc4d68d032f89b6756 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 13 Nov 2026 13:02:32 -0800 Subject: [PATCH 1/2] Drivers: hv: balloon: refuse to bind on a Xen PV dom0 nested under Hyper-V The Hyper-V-on-Xen series translates GPADLs, SynIC pages, hypercall arguments and netvsc buffers to the machine (L1-physical) frames the L0 host expects, but hv_balloon was never taught about nested mode. It hands L0 page_to_pfn() ranges straight from dom0's pseudo-physical address space, in balloon responses, free-page reports and hot-add requests, and L0 reads each of those numbers as a Xen machine frame. On a PV dom0 nested on Hyper-V the device autoloads from the VMBus offer, Xen proxies POST_MESSAGE and SIGNAL_EVENT, and the dynamic memory protocol runs end to end. A DM_BALLOON_REQUEST from the host then has L0 reclaim whichever machine frames dom0's PFNs happen to name: Xen's own memory, another domain's, or unrelated dom0 memory, with no error anywhere. A hot-add does the reverse and calls add_memory() on a host range that dom0's physmap does not back, so dom0 oopses on first touch. Translating the ranges would not make this correct either. A PV dom0 does not own the machine memory it runs on, Xen does, and the memory an L1 VM returns to or takes from L0 has to be accounted for in Xen's heap, not handed over behind its back by one of its domains. Dynamic memory for the L1 VM is a job for Xen itself. Refuse the probe when nested on Xen, so the offer stays unbound and dom0 does not speak the protocol at all. Nothing changes when not nested: hyperv_nested_on_xen is a build-time false without CONFIG_XEN_PV. Signed-off-by: Ariadne Conill --- drivers/hv/hv_balloon.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 39c1b793a8934f..e3cb22d87d237d 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1932,6 +1932,22 @@ static int balloon_probe(struct hv_device *dev, { int ret; + /* + * A Xen PV dom0 nested under Hyper-V does not own the machine memory + * it runs on; Xen does. Every page range this driver would hand the + * L0 host (balloon, free-page report, hot-add) is a dom0 + * pseudo-physical range, which L0 reads as a range of Xen machine + * frames: a balloon request would have the host reclaim Xen's own or + * another domain's memory, and a hot-add would add a range dom0's + * physmap does not back. Dynamic memory for the L1 VM is Xen's to + * manage, so leave the device unbound. + */ + if (hyperv_nested_on_xen) { + dev_info(&dev->device, + "dynamic memory is not supported on a Xen PV dom0 nested under Hyper-V\n"); + return -ENODEV; + } + allow_hibernation = hv_is_hibernation_supported(); if (allow_hibernation) hot_add = false; From 7ef9c692751f0a3f67896249a48b7965b6fb450a Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 13 Nov 2026 13:02:33 -0800 Subject: [PATCH 2/2] x86/xen/hyperv: free the vPCI irq domain's fwnode when the VIRQ cannot be bound hyperv_xen_vpci_root_domain() frees the named fwnode when creating the irq domain fails, but not when cpuhp_setup_state() fails afterwards: irq_domain_remove() does not free the fwnode its domain was created with, so that path leaked it. Free it there too. Signed-off-by: Ariadne Conill --- arch/x86/xen/hyperv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/xen/hyperv.c b/arch/x86/xen/hyperv.c index a0d9f82ebdcd25..0c1f104d0c3291 100644 --- a/arch/x86/xen/hyperv.c +++ b/arch/x86/xen/hyperv.c @@ -424,6 +424,7 @@ struct irq_domain *hyperv_xen_vpci_root_domain(void) pr_err("Xen: can't bind the Hyper-V vPCI VIRQ: %d\n", ret); irq_domain_remove(xen_hv_vpci_domain); xen_hv_vpci_domain = NULL; + irq_domain_free_fwnode(fn); return NULL; }