The confidential VMBus runs with the paravisor SynIC and requires configuring it with the paravisor.
Add the functions for configuring the paravisor SynIC Signed-off-by: Roman Kisel <rom...@linux.microsoft.com> --- drivers/hv/hv.c | 180 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 169 insertions(+), 11 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 2b561825089a..c9649ab3439e 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -203,7 +203,7 @@ int hv_synic_alloc(void) decrypt, "hypervisor SynIC event"); if (ret) goto err; - } + } if (vmbus_is_confidential()) { ret = hv_alloc_page(cpu, &hv_cpu->para_synic_message_page, @@ -267,7 +267,6 @@ 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 */ simp.as_uint64 = hv_get_msr(HV_MSR_SIMP); @@ -288,7 +287,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; @@ -316,6 +315,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); @@ -324,13 +328,90 @@ void hv_hyp_synic_enable_regs(unsigned int cpu) hv_set_msr(HV_MSR_SCONTROL, sctrl.as_uint64); } +/* + * The paravisor might not support proxying SynIC, and this + * function may fail. + */ +static int hv_para_synic_enable_regs(unsigned int cpu) +{ + int err; + 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. */ + err = hv_para_get_synic_register(HV_MSR_SIMP, &simp.as_uint64); + if (err) + return err; + simp.simp_enabled = 1; + simp.base_simp_gpa = virt_to_phys(hv_cpu->para_synic_message_page) + >> HV_HYP_PAGE_SHIFT; + err = hv_para_set_synic_register(HV_MSR_SIMP, simp.as_uint64); + if (err) + return err; + + /* Setup the Synic's event page with the paravisor. */ + err = hv_para_get_synic_register(HV_MSR_SIEFP, &siefp.as_uint64); + if (err) + return err; + siefp.siefp_enabled = 1; + siefp.base_siefp_gpa = virt_to_phys(hv_cpu->para_synic_event_page) + >> HV_HYP_PAGE_SHIFT; + return hv_para_set_synic_register(HV_MSR_SIEFP, siefp.as_uint64); +} + +static int hv_para_synic_enable_interrupts(void) +{ + union hv_synic_scontrol sctrl; + int err; + + /* Enable the global synic bit */ + err = hv_para_get_synic_register(HV_MSR_SCONTROL, &sctrl.as_uint64); + if (err) + return err; + sctrl.enable = 1; + + return hv_para_set_synic_register(HV_MSR_SCONTROL, sctrl.as_uint64); +} + int hv_synic_init(unsigned int cpu) { + int err; + + /* + * The paravisor may not support the confidential VMBus, + * check on that first. + */ + if (vmbus_is_confidential()) { + err = hv_para_synic_enable_regs(cpu); + if (err) + goto fail; + } + hv_hyp_synic_enable_regs(cpu); + if (vmbus_is_confidential()) { + err = hv_para_synic_enable_interrupts(); + if (err) + goto fail; + } else + hv_hyp_synic_enable_interrupts(); hv_stimer_legacy_init(cpu, VMBUS_MESSAGE_SINT); return 0; + +fail: + /* + * The failure may only come from enabling the paravisor SynIC. + * That in turn means that the confidential VMBus cannot be used + * which is not an error: the setup will be re-tried with the + * non-confidential VMBus. + * + * We also don't bother attempting to reset the paravisor registers + * as something isn't working there anyway. + */ + return err; } void hv_hyp_synic_disable_regs(unsigned int cpu) @@ -340,7 +421,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); @@ -378,14 +458,71 @@ 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) +{ + /* + * When a get/set register error is encountered, the function + * returns as the paravisor may not support these registers. + */ + int err; + 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); + + /* Disable SynIC's message page in the paravisor. */ + err = hv_para_get_synic_register(HV_MSR_SIMP, &simp.as_uint64); + if (err) + return; + simp.simp_enabled = 0; + + if (hv_cpu->para_synic_message_page) { + free_page((u64)(hv_cpu->para_synic_message_page)); + hv_cpu->para_synic_message_page = NULL; + } + + err = hv_para_set_synic_register(HV_MSR_SIMP, simp.as_uint64); + if (err) + return; + + /* Disable SynIC's event page in the paravisor. */ + err = hv_para_get_synic_register(HV_MSR_SIEFP, &siefp.as_uint64); + if (err) + return; + siefp.siefp_enabled = 0; + + if (hv_cpu->para_synic_event_page) { + free_page((u64)(hv_cpu->para_synic_event_page)); + hv_cpu->para_synic_event_page = NULL; + } + + hv_para_set_synic_register(HV_MSR_SIEFP, siefp.as_uint64); +} + +static void hv_para_synic_disable_interrupts(void) +{ + union hv_synic_scontrol sctrl; + int err; + + /* Disable the global synic bit */ + err = hv_para_get_synic_register(HV_MSR_SCONTROL, &sctrl.as_uint64); + if (err) + return; + sctrl.enable = 0; + hv_para_set_synic_register(HV_MSR_SCONTROL, sctrl.as_uint64); } #define HV_MAX_TRIES 3 @@ -398,16 +535,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) { @@ -424,6 +563,17 @@ static bool hv_synic_event_pending(void) 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; @@ -517,6 +667,14 @@ int hv_synic_cleanup(unsigned int cpu) hv_stimer_legacy_cleanup(cpu); hv_hyp_synic_disable_regs(cpu); + if (vmbus_is_confidential()) { + hv_para_synic_disable_regs(cpu); + hv_para_synic_disable_interrupts(); + } else { + hv_hyp_synic_disable_interrupts(); + } + if (vmbus_irq != -1) + disable_percpu_irq(vmbus_irq); return ret; } -- 2.43.0