diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index ae60fd542292..ef4b1b03395d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -272,6 +272,9 @@ void hv_synic_free(void) /* * hv_hyp_synic_enable_regs - Initialize the Synthetic Interrupt Controller * with the hypervisor. + * + * Note: When MSHV is present, mshv_synic_cpu_init() intializes further + * registers later. */ void hv_hyp_synic_enable_regs(unsigned int cpu) { diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c index e2288a726fec..2db3b0192eac 100644 --- a/drivers/hv/mshv_synic.c +++ b/drivers/hv/mshv_synic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -456,46 +457,75 @@ static int mshv_synic_cpu_init(unsigned int cpu) union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; union hv_synic_sint sint; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_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(); - /* Setup the Synic's message page */ + /* + * Map the SYNIC message page. When VMBus is not active the + * hypervisor pre-provisions the SIMP GPA but may not set + * simp_enabled — enable it here. + */ simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = true; + if (!vmbus_active) { + 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); if (!(*msg_page)) - return -EFAULT; + goto cleanup_simp; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - - /* Setup the Synic's event flags page */ + /* + * Map the event flags page. Same as SIMP: enable when + * VMBus is not active, already enabled by VMBus otherwise. + */ siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = true; - *event_flags_page = memremap(siefp.base_siefp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); + if (!vmbus_active) { + 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); if (!(*event_flags_page)) - goto cleanup; - - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + goto cleanup_siefp; /* Setup the Synic's event ring page */ 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); + + if (!(*event_ring_page)) + goto cleanup_siefp; + } else { + /* + * On L1VH the hypervisor does not provide a SIRBP page. + * Allocate one and program its GPA into the MSR. + */ + *event_ring_page = (struct hv_synic_event_ring_page *) + get_zeroed_page(GFP_KERNEL); + + if (!(*event_ring_page)) + goto cleanup_siefp; + + sirbp.base_sirbp_gpa = virt_to_phys(*event_ring_page) + >> HV_HYP_PAGE_SHIFT; + } + sirbp.sirbp_enabled = true; - *event_ring_page = memremap(sirbp.base_sirbp_gpa << PAGE_SHIFT, - PAGE_SIZE, MEMREMAP_WB); - - if (!(*event_ring_page)) - goto cleanup; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); if (mshv_sint_irq != -1) @@ -518,28 +548,30 @@ static int mshv_synic_cpu_init(unsigned int cpu) hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX, sint.as_uint64); - /* Enable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 1; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it already enabled SCONTROL. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 1; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; -cleanup: - if (*event_ring_page) { - sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - } - if (*event_flags_page) { +cleanup_siefp: + if (*event_flags_page) + memunmap(*event_flags_page); + if (!vmbus_active) { siefp.siefp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); - memunmap(*event_flags_page); } - if (*msg_page) { +cleanup_simp: + if (*msg_page) + memunmap(*msg_page); + if (!vmbus_active) { simp.simp_enabled = false; hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); - memunmap(*msg_page); } return -EFAULT; @@ -548,16 +580,15 @@ static int mshv_synic_cpu_init(unsigned int cpu) static int mshv_synic_cpu_exit(unsigned int cpu) { union hv_synic_sint sint; - union hv_synic_simp simp; - union hv_synic_siefp siefp; union hv_synic_sirbp sirbp; - union hv_synic_scontrol sctrl; struct hv_synic_pages *spages = this_cpu_ptr(synic_pages); struct hv_message_page **msg_page = &spages->hyp_synic_message_page; struct hv_synic_event_flags_page **event_flags_page = &spages->synic_event_flags_page; struct hv_synic_event_ring_page **event_ring_page = &spages->synic_event_ring_page; + /* VMBus owns SIMP/SIEFP/SCONTROL when it is active */ + bool vmbus_active = hv_vmbus_exists(); /* Disable the interrupt */ sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX); @@ -574,28 +605,47 @@ static int mshv_synic_cpu_exit(unsigned int cpu) if (mshv_sint_irq != -1) disable_percpu_irq(mshv_sint_irq); - /* Disable Synic's event ring page */ + /* Disable SYNIC event ring page owned by MSHV */ sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP); sirbp.sirbp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); - memunmap(*event_ring_page); - /* Disable Synic's event flags page */ - siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); - siefp.siefp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + if (hv_root_partition()) { + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + memunmap(*event_ring_page); + } else { + sirbp.base_sirbp_gpa = 0; + hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64); + free_page((unsigned long)*event_ring_page); + } + + /* + * Release our mappings of the message and event flags pages. + * When VMBus is not active, we enabled SIMP/SIEFP — disable + * them. Otherwise VMBus owns the MSRs — leave them. + */ memunmap(*event_flags_page); + if (!vmbus_active) { + union hv_synic_simp simp; + union hv_synic_siefp siefp; - /* Disable Synic's message page */ - simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); - simp.simp_enabled = false; - hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP); + siefp.siefp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64); + + simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP); + simp.simp_enabled = false; + hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64); + } memunmap(*msg_page); - /* Disable global synic bit */ - sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); - sctrl.enable = 0; - hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + /* When VMBus is active it owns SCONTROL — leave it. */ + if (!vmbus_active) { + union hv_synic_scontrol sctrl; + + sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL); + sctrl.enable = 0; + hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64); + } return 0; }