[PATCH v4 9/9] mshv: set up own SynIC registers on a nested root partition

From: Wei Hu

Date: Mon Aug 31 2026 - 07:33:04 EST


From: Wei Hu <weh@xxxxxxxxxxxxx>

Upstream mshv_synic_cpu_init()/_exit() skip programming the SynIC
SIMP/SIEFP/SCONTROL registers when VMBus is active (hv_vmbus_exists()),
assuming VMBus's hv_hyp_synic_enable_regs() already provisioned them.

That assumption is wrong for a nested root partition. There, VMBus
programs the nested SynIC MSRs (HV_X64_MSR_NESTED_SIMP, ...) through
hv_set_msr()'s nested remap, while mshv_synic reads and writes the
non-nested SynIC MSRs. As a result MSHV maps the wrong message page and
never receives the async hypercall completion, hanging guest creation.

Gate the "VMBus owns the SynIC registers" optimization on !hv_nested so
a nested root partition programs its own non-nested SynIC registers.
There is no behavior change for a non-nested root.

Map hypervisor-provided SIMP, SIEFP, and root SIRBP pages with the shared
GPA boundary removed and MEMREMAP_DEC, matching drivers/hv/hv.c for
confidential hosts. The L1VH SIRBP remains locally allocated.

Signed-off-by: Wei Hu <weh@xxxxxxxxxxxxx>
---
drivers/hv/mshv_synic.c | 38 +++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
index 0fdbae1e053c..8470cf958f21 100644
--- a/drivers/hv/mshv_synic.c
+++ b/drivers/hv/mshv_synic.c
@@ -458,6 +458,25 @@ void mshv_isr(void)
}
}

+static bool mshv_synic_vmbus_owns_registers(void)
+{
+ /* Nested VMBus programs nested MSRs, while MSHV uses non-nested MSRs. */
+ return hv_vmbus_exists() && !hv_nested;
+}
+
+static void *mshv_synic_map_shared_page(u64 pfn)
+{
+ u64 base;
+
+ if (!pfn)
+ return NULL;
+
+ /* Match Hyper-V's established confidential SynIC mapping convention. */
+ base = (pfn << HV_HYP_PAGE_SHIFT) &
+ ~ms_hyperv.shared_gpa_boundary;
+ return memremap(base, HV_HYP_PAGE_SIZE, MEMREMAP_WB | MEMREMAP_DEC);
+}
+
static int mshv_synic_cpu_init(unsigned int cpu)
{
union hv_synic_simp simp;
@@ -468,11 +487,7 @@ static int mshv_synic_cpu_init(unsigned int cpu)
struct hv_message_page *msg_page;
struct hv_synic_event_flags_page *event_flags_page;
struct hv_synic_event_ring_page *event_ring_page;
- /*
- * VMBus owns SIMP/SIEFP/SCONTROL when it is active.
- * See hv_hyp_synic_enable_regs() for that initialization.
- */
- bool vmbus_active = hv_vmbus_exists();
+ bool vmbus_active = mshv_synic_vmbus_owns_registers();

/*
* Map the SYNIC message page. When VMBus is not active the
@@ -484,8 +499,7 @@ static int mshv_synic_cpu_init(unsigned int cpu)
simp.simp_enabled = true;
hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
}
- msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT,
- HV_HYP_PAGE_SIZE, MEMREMAP_WB);
+ msg_page = mshv_synic_map_shared_page(simp.base_simp_gpa);
WRITE_ONCE(spages->hyp_synic_message_page, msg_page);

if (!msg_page)
@@ -500,8 +514,7 @@ static int mshv_synic_cpu_init(unsigned int cpu)
siefp.siefp_enabled = true;
hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
}
- event_flags_page = memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT,
- HV_HYP_PAGE_SIZE, MEMREMAP_WB);
+ event_flags_page = mshv_synic_map_shared_page(siefp.base_siefp_gpa);
WRITE_ONCE(spages->synic_event_flags_page, event_flags_page);

if (!event_flags_page)
@@ -511,8 +524,8 @@ static int mshv_synic_cpu_init(unsigned int cpu)
sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);

if (hv_root_partition()) {
- event_ring_page = memremap(sirbp.base_sirbp_gpa << HV_HYP_PAGE_SHIFT,
- HV_HYP_PAGE_SIZE, MEMREMAP_WB);
+ event_ring_page =
+ mshv_synic_map_shared_page(sirbp.base_sirbp_gpa);

if (!event_ring_page)
goto cleanup_siefp;
@@ -595,8 +608,7 @@ static int mshv_synic_cpu_exit(unsigned int cpu)
struct hv_message_page *msg_page;
struct hv_synic_event_flags_page *event_flags_page;
struct hv_synic_event_ring_page *event_ring_page;
- /* VMBus owns SIMP/SIEFP/SCONTROL when it is active */
- bool vmbus_active = hv_vmbus_exists();
+ bool vmbus_active = mshv_synic_vmbus_owns_registers();

msg_page = READ_ONCE(spages->hyp_synic_message_page);
event_flags_page = READ_ONCE(spages->synic_event_flags_page);
--
2.43.0