diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 81b6c3b0f21a..936c5f310df6 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -278,9 +278,8 @@ void hv_hyp_synic_enable_regs(unsigned int cpu) union hv_synic_simp simp; union hv_synic_siefp siefp; union hv_synic_sint shared_sint; - union hv_synic_scontrol sctrl; - /* Setup the Synic's message page */ + /* Setup the Synic's message page with the hypervisor. */ simp.as_uint64 = hv_get_msr(HV_MSR_SIMP); simp.simp_enabled = 1; @@ -299,7 +298,7 @@ void hv_hyp_synic_enable_regs(unsigned int cpu) hv_set_msr(HV_MSR_SIMP, simp.as_uint64); - /* Setup the Synic's event page */ + /* Setup the Synic's event page with the hypervisor. */ siefp.as_uint64 = hv_get_msr(HV_MSR_SIEFP); siefp.siefp_enabled = 1; @@ -328,6 +327,11 @@ void hv_hyp_synic_enable_regs(unsigned int cpu) shared_sint.masked = false; shared_sint.auto_eoi = hv_recommend_using_aeoi(); hv_set_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); +} + +static void hv_hyp_synic_enable_interrupts(void) +{ + union hv_synic_scontrol sctrl; /* Enable the global synic bit */ sctrl.as_uint64 = hv_get_msr(HV_MSR_SCONTROL); @@ -336,9 +340,59 @@ void hv_hyp_synic_enable_regs(unsigned int cpu) hv_set_msr(HV_MSR_SCONTROL, sctrl.as_uint64); } +static void hv_para_synic_enable_regs(unsigned int cpu) +{ + union hv_synic_simp simp; + union hv_synic_siefp siefp; + struct hv_per_cpu_context *hv_cpu + = per_cpu_ptr(hv_context.cpu_context, cpu); + + /* Setup the Synic's message page with the paravisor. */ + simp.as_uint64 = hv_para_get_synic_register(HV_MSR_SIMP); + simp.simp_enabled = 1; + simp.base_simp_gpa = virt_to_phys(hv_cpu->para_synic_message_page) + >> HV_HYP_PAGE_SHIFT; + hv_para_set_synic_register(HV_MSR_SIMP, simp.as_uint64); + + /* Setup the Synic's event page with the paravisor. */ + siefp.as_uint64 = hv_para_get_synic_register(HV_MSR_SIEFP); + siefp.siefp_enabled = 1; + siefp.base_siefp_gpa = virt_to_phys(hv_cpu->para_synic_event_page) + >> HV_HYP_PAGE_SHIFT; + hv_para_set_synic_register(HV_MSR_SIEFP, siefp.as_uint64); +} + +static void hv_para_synic_enable_interrupts(void) +{ + union hv_synic_scontrol sctrl; + + /* Enable the global synic bit */ + sctrl.as_uint64 = hv_para_get_synic_register(HV_MSR_SCONTROL); + sctrl.enable = 1; + hv_para_set_synic_register(HV_MSR_SCONTROL, sctrl.as_uint64); +} + int hv_synic_init(unsigned int cpu) { + if (vmbus_is_confidential()) + hv_para_synic_enable_regs(cpu); + + /* + * The SINT is set in hv_hyp_synic_enable_regs() by calling + * hv_set_msr(). hv_set_msr() in turn has special case code for the + * SINT MSRs that write to the hypervisor version of the MSR *and* + * the paravisor version of the MSR (but *without* the proxy bit when + * VMBus is confidential). + * + * Then enable interrupts via the paravisor if VMBus is confidential, + * and otherwise via the hypervisor. + */ + hv_hyp_synic_enable_regs(cpu); + if (vmbus_is_confidential()) + hv_para_synic_enable_interrupts(); + else + hv_hyp_synic_enable_interrupts(); hv_stimer_legacy_init(cpu, VMBUS_MESSAGE_SINT); @@ -352,7 +406,6 @@ void hv_hyp_synic_disable_regs(unsigned int cpu) union hv_synic_sint shared_sint; union hv_synic_simp simp; union hv_synic_siefp siefp; - union hv_synic_scontrol sctrl; shared_sint.as_uint64 = hv_get_msr(HV_MSR_SINT0 + VMBUS_MESSAGE_SINT); @@ -365,7 +418,7 @@ void hv_hyp_synic_disable_regs(unsigned int cpu) simp.as_uint64 = hv_get_msr(HV_MSR_SIMP); /* - * In Isolation VM, sim and sief pages are allocated by + * In Isolation VM, simp and sief pages are allocated by * paravisor. These pages also will be used by kdump * kernel. So just reset enable bit here and keep page * addresses. @@ -395,14 +448,42 @@ void hv_hyp_synic_disable_regs(unsigned int cpu) } hv_set_msr(HV_MSR_SIEFP, siefp.as_uint64); +} + +static void hv_hyp_synic_disable_interrupts(void) +{ + union hv_synic_scontrol sctrl; /* Disable the global synic bit */ sctrl.as_uint64 = hv_get_msr(HV_MSR_SCONTROL); sctrl.enable = 0; hv_set_msr(HV_MSR_SCONTROL, sctrl.as_uint64); +} - if (vmbus_irq != -1) - disable_percpu_irq(vmbus_irq); +static void hv_para_synic_disable_regs(unsigned int cpu) +{ + union hv_synic_simp simp; + union hv_synic_siefp siefp; + + /* Disable SynIC's message page in the paravisor. */ + simp.as_uint64 = hv_para_get_synic_register(HV_MSR_SIMP); + simp.simp_enabled = 0; + hv_para_set_synic_register(HV_MSR_SIMP, simp.as_uint64); + + /* Disable SynIC's event page in the paravisor. */ + siefp.as_uint64 = hv_para_get_synic_register(HV_MSR_SIEFP); + siefp.siefp_enabled = 0; + hv_para_set_synic_register(HV_MSR_SIEFP, siefp.as_uint64); +} + +static void hv_para_synic_disable_interrupts(void) +{ + union hv_synic_scontrol sctrl; + + /* Disable the global synic bit */ + sctrl.as_uint64 = hv_para_get_synic_register(HV_MSR_SCONTROL); + sctrl.enable = 0; + hv_para_set_synic_register(HV_MSR_SCONTROL, sctrl.as_uint64); } #define HV_MAX_TRIES 3 @@ -415,16 +496,18 @@ void hv_hyp_synic_disable_regs(unsigned int cpu) * that the normal interrupt handling mechanism will find and process the channel interrupt * "very soon", and in the process clear the bit. */ -static bool hv_synic_event_pending(void) +static bool __hv_synic_event_pending(union hv_synic_event_flags *event, int sint) { - struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context); - union hv_synic_event_flags *event = - (union hv_synic_event_flags *)hv_cpu->hyp_synic_event_page + VMBUS_MESSAGE_SINT; - unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */ + unsigned long *recv_int_page; bool pending; u32 relid; int tries = 0; + if (!event) + return false; + + event += sint; + recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */ retry: pending = false; for_each_set_bit(relid, recv_int_page, HV_EVENT_FLAGS_COUNT) { @@ -441,6 +524,17 @@ retry: return pending; } +static bool hv_synic_event_pending(void) +{ + struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context); + union hv_synic_event_flags *hyp_synic_event_page = hv_cpu->hyp_synic_event_page; + union hv_synic_event_flags *para_synic_event_page = hv_cpu->para_synic_event_page; + + return + __hv_synic_event_pending(hyp_synic_event_page, VMBUS_MESSAGE_SINT) || + __hv_synic_event_pending(para_synic_event_page, VMBUS_MESSAGE_SINT); +} + static int hv_pick_new_cpu(struct vmbus_channel *channel) { int ret = -EBUSY; @@ -533,7 +627,27 @@ int hv_synic_cleanup(unsigned int cpu) always_cleanup: hv_stimer_legacy_cleanup(cpu); + /* + * First, disable the event and message pages + * used for communicating with the host, and then + * disable the host interrupts if VMBus is not + * confidential. + */ hv_hyp_synic_disable_regs(cpu); + if (!vmbus_is_confidential()) + hv_hyp_synic_disable_interrupts(); + + /* + * Perform the same steps for the Confidential VMBus. + * The sequencing provides the guarantee that no data + * may be posted for processing before disabling interrupts. + */ + if (vmbus_is_confidential()) { + hv_para_synic_disable_regs(cpu); + hv_para_synic_disable_interrupts(); + } + if (vmbus_irq != -1) + disable_percpu_irq(vmbus_irq); return ret; }