13b20eb23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
246a97191SGreg Kroah-Hartman /*
346a97191SGreg Kroah-Hartman * Copyright (c) 2009, Microsoft Corporation.
446a97191SGreg Kroah-Hartman *
546a97191SGreg Kroah-Hartman * Authors:
646a97191SGreg Kroah-Hartman * Haiyang Zhang <haiyangz@microsoft.com>
746a97191SGreg Kroah-Hartman * Hank Janssen <hjanssen@microsoft.com>
846a97191SGreg Kroah-Hartman */
946a97191SGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1046a97191SGreg Kroah-Hartman
11faff4406STianyu Lan #include <linux/io.h>
1246a97191SGreg Kroah-Hartman #include <linux/kernel.h>
1346a97191SGreg Kroah-Hartman #include <linux/mm.h>
1446a97191SGreg Kroah-Hartman #include <linux/slab.h>
1546a97191SGreg Kroah-Hartman #include <linux/vmalloc.h>
1646a97191SGreg Kroah-Hartman #include <linux/hyperv.h>
17248e742aSMichael Kelley #include <linux/random.h>
184061ed9eSK. Y. Srinivasan #include <linux/clockchips.h>
19b635ccc1SAndrea Parri (Microsoft) #include <linux/delay.h>
20d608715dSMichael Kelley #include <linux/interrupt.h>
21fd1fea68SMichael Kelley #include <clocksource/hyperv_timer.h>
224061ed9eSK. Y. Srinivasan #include <asm/mshyperv.h>
23193061eaSTianyu Lan #include <linux/set_memory.h>
2446a97191SGreg Kroah-Hartman #include "hyperv_vmbus.h"
2546a97191SGreg Kroah-Hartman
2646a97191SGreg Kroah-Hartman /* The one and only */
27a3cadf38SK. Y. Srinivasan struct hv_context hv_context;
2846a97191SGreg Kroah-Hartman
29248e742aSMichael Kelley /*
3046a97191SGreg Kroah-Hartman * hv_init - Main initialization routine.
3146a97191SGreg Kroah-Hartman *
3246a97191SGreg Kroah-Hartman * This routine must be called before any other routines in here are called
3346a97191SGreg Kroah-Hartman */
hv_init(void)3446a97191SGreg Kroah-Hartman int hv_init(void)
3546a97191SGreg Kroah-Hartman {
3637cdd991SStephen Hemminger hv_context.cpu_context = alloc_percpu(struct hv_per_cpu_context);
3737cdd991SStephen Hemminger if (!hv_context.cpu_context)
3837cdd991SStephen Hemminger return -ENOMEM;
3946a97191SGreg Kroah-Hartman return 0;
4046a97191SGreg Kroah-Hartman }
4146a97191SGreg Kroah-Hartman
4246a97191SGreg Kroah-Hartman /*
4346a97191SGreg Kroah-Hartman * hv_post_message - Post a message using the hypervisor message IPC.
4446a97191SGreg Kroah-Hartman *
4546a97191SGreg Kroah-Hartman * This involves a hypercall.
4646a97191SGreg Kroah-Hartman */
hv_post_message(union hv_connection_id connection_id,enum hv_message_type message_type,void * payload,size_t payload_size)47415f0a02SDan Carpenter int hv_post_message(union hv_connection_id connection_id,
4846a97191SGreg Kroah-Hartman enum hv_message_type message_type,
4946a97191SGreg Kroah-Hartman void *payload, size_t payload_size)
5046a97191SGreg Kroah-Hartman {
5146a97191SGreg Kroah-Hartman struct hv_input_post_message *aligned_msg;
529a6b1a17SDexuan Cui unsigned long flags;
53a108393dSJake Oshins u64 status;
5446a97191SGreg Kroah-Hartman
5546a97191SGreg Kroah-Hartman if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
5646a97191SGreg Kroah-Hartman return -EMSGSIZE;
5746a97191SGreg Kroah-Hartman
589a6b1a17SDexuan Cui local_irq_save(flags);
599a6b1a17SDexuan Cui
6023378295SDexuan Cui /*
6123378295SDexuan Cui * A TDX VM with the paravisor must use the decrypted post_msg_page: see
6223378295SDexuan Cui * the comment in struct hv_per_cpu_context. A SNP VM with the paravisor
6323378295SDexuan Cui * can use the encrypted hyperv_pcpu_input_arg because it copies the
6423378295SDexuan Cui * input into the GHCB page, which has been decrypted by the paravisor.
6523378295SDexuan Cui */
6623378295SDexuan Cui if (hv_isolation_type_tdx() && ms_hyperv.paravisor_present)
6723378295SDexuan Cui aligned_msg = this_cpu_ptr(hv_context.cpu_context)->post_msg_page;
6823378295SDexuan Cui else
699a6b1a17SDexuan Cui aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);
7023378295SDexuan Cui
7146a97191SGreg Kroah-Hartman aligned_msg->connectionid = connection_id;
72b29ef354SK. Y. Srinivasan aligned_msg->reserved = 0;
7346a97191SGreg Kroah-Hartman aligned_msg->message_type = message_type;
7446a97191SGreg Kroah-Hartman aligned_msg->payload_size = payload_size;
7546a97191SGreg Kroah-Hartman memcpy((void *)aligned_msg->payload, payload, payload_size);
7646a97191SGreg Kroah-Hartman
7723378295SDexuan Cui if (ms_hyperv.paravisor_present) {
7823378295SDexuan Cui if (hv_isolation_type_tdx())
7923378295SDexuan Cui status = hv_tdx_hypercall(HVCALL_POST_MESSAGE,
8023378295SDexuan Cui virt_to_phys(aligned_msg), 0);
8123378295SDexuan Cui else if (hv_isolation_type_snp())
8220c89a55STianyu Lan status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE,
8323378295SDexuan Cui aligned_msg, NULL,
8420c89a55STianyu Lan sizeof(*aligned_msg));
8520c89a55STianyu Lan else
8623378295SDexuan Cui status = HV_STATUS_INVALID_PARAMETER;
8723378295SDexuan Cui } else {
8820c89a55STianyu Lan status = hv_do_hypercall(HVCALL_POST_MESSAGE,
8920c89a55STianyu Lan aligned_msg, NULL);
9023378295SDexuan Cui }
9146a97191SGreg Kroah-Hartman
929a6b1a17SDexuan Cui local_irq_restore(flags);
9313b9abfcSMichael Kelley
94753ed9c9SJoseph Salisbury return hv_result(status);
9546a97191SGreg Kroah-Hartman }
9646a97191SGreg Kroah-Hartman
hv_synic_alloc(void)972608fb65SJason Wang int hv_synic_alloc(void)
982608fb65SJason Wang {
99193061eaSTianyu Lan int cpu, ret = -ENOMEM;
100f25a7eceSMichael Kelley struct hv_per_cpu_context *hv_cpu;
101f25a7eceSMichael Kelley
102f25a7eceSMichael Kelley /*
103f25a7eceSMichael Kelley * First, zero all per-cpu memory areas so hv_synic_free() can
104f25a7eceSMichael Kelley * detect what memory has been allocated and cleanup properly
105f25a7eceSMichael Kelley * after any failures.
106f25a7eceSMichael Kelley */
107f25a7eceSMichael Kelley for_each_present_cpu(cpu) {
108f25a7eceSMichael Kelley hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
109f25a7eceSMichael Kelley memset(hv_cpu, 0, sizeof(*hv_cpu));
110f25a7eceSMichael Kelley }
1112608fb65SJason Wang
1126396bb22SKees Cook hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask),
113597ff72fSJia-Ju Bai GFP_KERNEL);
1149f01ec53SK. Y. Srinivasan if (hv_context.hv_numa_map == NULL) {
1159f01ec53SK. Y. Srinivasan pr_err("Unable to allocate NUMA map\n");
1169f01ec53SK. Y. Srinivasan goto err;
1179f01ec53SK. Y. Srinivasan }
1189f01ec53SK. Y. Srinivasan
119421b8f20SVitaly Kuznetsov for_each_present_cpu(cpu) {
120f25a7eceSMichael Kelley hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);
1212608fb65SJason Wang
12237cdd991SStephen Hemminger tasklet_init(&hv_cpu->msg_dpc,
12337cdd991SStephen Hemminger vmbus_on_msg_dpc, (unsigned long) hv_cpu);
124d81274aaSK. Y. Srinivasan
12523378295SDexuan Cui if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
12623378295SDexuan Cui hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
12723378295SDexuan Cui if (hv_cpu->post_msg_page == NULL) {
12823378295SDexuan Cui pr_err("Unable to allocate post msg page\n");
12923378295SDexuan Cui goto err;
13023378295SDexuan Cui }
13123378295SDexuan Cui
13223378295SDexuan Cui ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1);
13323378295SDexuan Cui if (ret) {
13423378295SDexuan Cui pr_err("Failed to decrypt post msg page: %d\n", ret);
13523378295SDexuan Cui /* Just leak the page, as it's unsafe to free the page. */
13623378295SDexuan Cui hv_cpu->post_msg_page = NULL;
13723378295SDexuan Cui goto err;
13823378295SDexuan Cui }
13923378295SDexuan Cui
14023378295SDexuan Cui memset(hv_cpu->post_msg_page, 0, PAGE_SIZE);
14123378295SDexuan Cui }
14223378295SDexuan Cui
143faff4406STianyu Lan /*
144faff4406STianyu Lan * Synic message and event pages are allocated by paravisor.
145faff4406STianyu Lan * Skip these pages allocation here.
146faff4406STianyu Lan */
147d3a9d7e4SDexuan Cui if (!ms_hyperv.paravisor_present && !hv_root_partition) {
14837cdd991SStephen Hemminger hv_cpu->synic_message_page =
1492608fb65SJason Wang (void *)get_zeroed_page(GFP_ATOMIC);
15037cdd991SStephen Hemminger if (hv_cpu->synic_message_page == NULL) {
1512608fb65SJason Wang pr_err("Unable to allocate SYNIC message page\n");
1522608fb65SJason Wang goto err;
1532608fb65SJason Wang }
1542608fb65SJason Wang
155faff4406STianyu Lan hv_cpu->synic_event_page =
156faff4406STianyu Lan (void *)get_zeroed_page(GFP_ATOMIC);
15737cdd991SStephen Hemminger if (hv_cpu->synic_event_page == NULL) {
1582608fb65SJason Wang pr_err("Unable to allocate SYNIC event page\n");
15968f2f2bcSDexuan Cui
16068f2f2bcSDexuan Cui free_page((unsigned long)hv_cpu->synic_message_page);
16168f2f2bcSDexuan Cui hv_cpu->synic_message_page = NULL;
1622608fb65SJason Wang goto err;
1632608fb65SJason Wang }
164faff4406STianyu Lan }
165193061eaSTianyu Lan
16668f2f2bcSDexuan Cui if (!ms_hyperv.paravisor_present &&
167*e3131f1cSDexuan Cui (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
168193061eaSTianyu Lan ret = set_memory_decrypted((unsigned long)
169193061eaSTianyu Lan hv_cpu->synic_message_page, 1);
170193061eaSTianyu Lan if (ret) {
171193061eaSTianyu Lan pr_err("Failed to decrypt SYNIC msg page: %d\n", ret);
172193061eaSTianyu Lan hv_cpu->synic_message_page = NULL;
173193061eaSTianyu Lan
174193061eaSTianyu Lan /*
175193061eaSTianyu Lan * Free the event page here so that hv_synic_free()
176193061eaSTianyu Lan * won't later try to re-encrypt it.
177193061eaSTianyu Lan */
178193061eaSTianyu Lan free_page((unsigned long)hv_cpu->synic_event_page);
179193061eaSTianyu Lan hv_cpu->synic_event_page = NULL;
180193061eaSTianyu Lan goto err;
181193061eaSTianyu Lan }
182193061eaSTianyu Lan
183193061eaSTianyu Lan ret = set_memory_decrypted((unsigned long)
184193061eaSTianyu Lan hv_cpu->synic_event_page, 1);
185193061eaSTianyu Lan if (ret) {
186193061eaSTianyu Lan pr_err("Failed to decrypt SYNIC event page: %d\n", ret);
187193061eaSTianyu Lan hv_cpu->synic_event_page = NULL;
188193061eaSTianyu Lan goto err;
189193061eaSTianyu Lan }
190193061eaSTianyu Lan
191193061eaSTianyu Lan memset(hv_cpu->synic_message_page, 0, PAGE_SIZE);
192193061eaSTianyu Lan memset(hv_cpu->synic_event_page, 0, PAGE_SIZE);
193193061eaSTianyu Lan }
1942608fb65SJason Wang }
1952608fb65SJason Wang
1962608fb65SJason Wang return 0;
197193061eaSTianyu Lan
1982608fb65SJason Wang err:
19957208632SMichael Kelley /*
20057208632SMichael Kelley * Any memory allocations that succeeded will be freed when
20157208632SMichael Kelley * the caller cleans up by calling hv_synic_free()
20257208632SMichael Kelley */
203193061eaSTianyu Lan return ret;
2042608fb65SJason Wang }
2052608fb65SJason Wang
2062608fb65SJason Wang
hv_synic_free(void)2072608fb65SJason Wang void hv_synic_free(void)
2082608fb65SJason Wang {
209193061eaSTianyu Lan int cpu, ret;
2102608fb65SJason Wang
21137cdd991SStephen Hemminger for_each_present_cpu(cpu) {
21237cdd991SStephen Hemminger struct hv_per_cpu_context *hv_cpu
21337cdd991SStephen Hemminger = per_cpu_ptr(hv_context.cpu_context, cpu);
21437cdd991SStephen Hemminger
215193061eaSTianyu Lan /* It's better to leak the page if the encryption fails. */
21623378295SDexuan Cui if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) {
21723378295SDexuan Cui if (hv_cpu->post_msg_page) {
21823378295SDexuan Cui ret = set_memory_encrypted((unsigned long)
21923378295SDexuan Cui hv_cpu->post_msg_page, 1);
22023378295SDexuan Cui if (ret) {
22123378295SDexuan Cui pr_err("Failed to encrypt post msg page: %d\n", ret);
22223378295SDexuan Cui hv_cpu->post_msg_page = NULL;
22323378295SDexuan Cui }
22423378295SDexuan Cui }
22523378295SDexuan Cui }
22623378295SDexuan Cui
22768f2f2bcSDexuan Cui if (!ms_hyperv.paravisor_present &&
228*e3131f1cSDexuan Cui (hv_isolation_type_snp() || hv_isolation_type_tdx())) {
229193061eaSTianyu Lan if (hv_cpu->synic_message_page) {
230193061eaSTianyu Lan ret = set_memory_encrypted((unsigned long)
231193061eaSTianyu Lan hv_cpu->synic_message_page, 1);
232193061eaSTianyu Lan if (ret) {
233193061eaSTianyu Lan pr_err("Failed to encrypt SYNIC msg page: %d\n", ret);
234193061eaSTianyu Lan hv_cpu->synic_message_page = NULL;
235193061eaSTianyu Lan }
236193061eaSTianyu Lan }
237193061eaSTianyu Lan
238193061eaSTianyu Lan if (hv_cpu->synic_event_page) {
239193061eaSTianyu Lan ret = set_memory_encrypted((unsigned long)
240193061eaSTianyu Lan hv_cpu->synic_event_page, 1);
241193061eaSTianyu Lan if (ret) {
242193061eaSTianyu Lan pr_err("Failed to encrypt SYNIC event page: %d\n", ret);
243193061eaSTianyu Lan hv_cpu->synic_event_page = NULL;
244193061eaSTianyu Lan }
245193061eaSTianyu Lan }
246193061eaSTianyu Lan }
247193061eaSTianyu Lan
24823378295SDexuan Cui free_page((unsigned long)hv_cpu->post_msg_page);
24937cdd991SStephen Hemminger free_page((unsigned long)hv_cpu->synic_event_page);
25037cdd991SStephen Hemminger free_page((unsigned long)hv_cpu->synic_message_page);
25137cdd991SStephen Hemminger }
25237cdd991SStephen Hemminger
2539f01ec53SK. Y. Srinivasan kfree(hv_context.hv_numa_map);
2542608fb65SJason Wang }
2552608fb65SJason Wang
25646a97191SGreg Kroah-Hartman /*
25768cb8117SJoe Perches * hv_synic_init - Initialize the Synthetic Interrupt Controller.
25846a97191SGreg Kroah-Hartman *
25946a97191SGreg Kroah-Hartman * If it is already initialized by another entity (ie x2v shim), we need to
26046a97191SGreg Kroah-Hartman * retrieve the initialized message and event pages. Otherwise, we create and
26146a97191SGreg Kroah-Hartman * initialize the message and event pages.
26246a97191SGreg Kroah-Hartman */
hv_synic_enable_regs(unsigned int cpu)263dba61cdaSDexuan Cui void hv_synic_enable_regs(unsigned int cpu)
26446a97191SGreg Kroah-Hartman {
26537cdd991SStephen Hemminger struct hv_per_cpu_context *hv_cpu
26637cdd991SStephen Hemminger = per_cpu_ptr(hv_context.cpu_context, cpu);
26746a97191SGreg Kroah-Hartman union hv_synic_simp simp;
26846a97191SGreg Kroah-Hartman union hv_synic_siefp siefp;
26946a97191SGreg Kroah-Hartman union hv_synic_sint shared_sint;
27046a97191SGreg Kroah-Hartman union hv_synic_scontrol sctrl;
27146a97191SGreg Kroah-Hartman
27246a97191SGreg Kroah-Hartman /* Setup the Synic's message page */
273f3c5e63cSMichael Kelley simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP);
27446a97191SGreg Kroah-Hartman simp.simp_enabled = 1;
275faff4406STianyu Lan
276d3a9d7e4SDexuan Cui if (ms_hyperv.paravisor_present || hv_root_partition) {
2776afd9dc1SMichael Kelley /* Mask out vTOM bit. ioremap_cache() maps decrypted */
2786afd9dc1SMichael Kelley u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) &
2796afd9dc1SMichael Kelley ~ms_hyperv.shared_gpa_boundary;
280faff4406STianyu Lan hv_cpu->synic_message_page
2816afd9dc1SMichael Kelley = (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
282faff4406STianyu Lan if (!hv_cpu->synic_message_page)
2836afd9dc1SMichael Kelley pr_err("Fail to map synic message page.\n");
284faff4406STianyu Lan } else {
28537cdd991SStephen Hemminger simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
286ef514d3eSBoqun Feng >> HV_HYP_PAGE_SHIFT;
287faff4406STianyu Lan }
28846a97191SGreg Kroah-Hartman
289f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SIMP, simp.as_uint64);
29046a97191SGreg Kroah-Hartman
29146a97191SGreg Kroah-Hartman /* Setup the Synic's event page */
292f3c5e63cSMichael Kelley siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP);
29346a97191SGreg Kroah-Hartman siefp.siefp_enabled = 1;
294faff4406STianyu Lan
295d3a9d7e4SDexuan Cui if (ms_hyperv.paravisor_present || hv_root_partition) {
2966afd9dc1SMichael Kelley /* Mask out vTOM bit. ioremap_cache() maps decrypted */
2976afd9dc1SMichael Kelley u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) &
2986afd9dc1SMichael Kelley ~ms_hyperv.shared_gpa_boundary;
2996afd9dc1SMichael Kelley hv_cpu->synic_event_page
3006afd9dc1SMichael Kelley = (void *)ioremap_cache(base, HV_HYP_PAGE_SIZE);
301faff4406STianyu Lan if (!hv_cpu->synic_event_page)
3026afd9dc1SMichael Kelley pr_err("Fail to map synic event page.\n");
303faff4406STianyu Lan } else {
30437cdd991SStephen Hemminger siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
305ef514d3eSBoqun Feng >> HV_HYP_PAGE_SHIFT;
306faff4406STianyu Lan }
30746a97191SGreg Kroah-Hartman
308f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
30946a97191SGreg Kroah-Hartman
31046a97191SGreg Kroah-Hartman /* Setup the shared SINT. */
311d608715dSMichael Kelley if (vmbus_irq != -1)
312d608715dSMichael Kelley enable_percpu_irq(vmbus_irq, 0);
313f3c5e63cSMichael Kelley shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
314f3c5e63cSMichael Kelley VMBUS_MESSAGE_SINT);
31546a97191SGreg Kroah-Hartman
316d608715dSMichael Kelley shared_sint.vector = vmbus_interrupt;
31746a97191SGreg Kroah-Hartman shared_sint.masked = false;
318946f4b86SMichael Kelley
319946f4b86SMichael Kelley /*
320946f4b86SMichael Kelley * On architectures where Hyper-V doesn't support AEOI (e.g., ARM64),
321946f4b86SMichael Kelley * it doesn't provide a recommendation flag and AEOI must be disabled.
322946f4b86SMichael Kelley */
323946f4b86SMichael Kelley #ifdef HV_DEPRECATING_AEOI_RECOMMENDED
324946f4b86SMichael Kelley shared_sint.auto_eoi =
325946f4b86SMichael Kelley !(ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED);
326946f4b86SMichael Kelley #else
327946f4b86SMichael Kelley shared_sint.auto_eoi = 0;
328946f4b86SMichael Kelley #endif
329f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT,
330f3c5e63cSMichael Kelley shared_sint.as_uint64);
33146a97191SGreg Kroah-Hartman
33246a97191SGreg Kroah-Hartman /* Enable the global synic bit */
333f3c5e63cSMichael Kelley sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
33446a97191SGreg Kroah-Hartman sctrl.enable = 1;
33546a97191SGreg Kroah-Hartman
336f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
337dba61cdaSDexuan Cui }
338dba61cdaSDexuan Cui
hv_synic_init(unsigned int cpu)339dba61cdaSDexuan Cui int hv_synic_init(unsigned int cpu)
340dba61cdaSDexuan Cui {
341dba61cdaSDexuan Cui hv_synic_enable_regs(cpu);
34246a97191SGreg Kroah-Hartman
3434df4cb9eSMichael Kelley hv_stimer_legacy_init(cpu, VMBUS_MESSAGE_SINT);
344fd1fea68SMichael Kelley
34576d36ab7SVitaly Kuznetsov return 0;
34646a97191SGreg Kroah-Hartman }
34746a97191SGreg Kroah-Hartman
34846a97191SGreg Kroah-Hartman /*
34946a97191SGreg Kroah-Hartman * hv_synic_cleanup - Cleanup routine for hv_synic_init().
35046a97191SGreg Kroah-Hartman */
hv_synic_disable_regs(unsigned int cpu)351dba61cdaSDexuan Cui void hv_synic_disable_regs(unsigned int cpu)
35246a97191SGreg Kroah-Hartman {
353faff4406STianyu Lan struct hv_per_cpu_context *hv_cpu
354faff4406STianyu Lan = per_cpu_ptr(hv_context.cpu_context, cpu);
35546a97191SGreg Kroah-Hartman union hv_synic_sint shared_sint;
35646a97191SGreg Kroah-Hartman union hv_synic_simp simp;
35746a97191SGreg Kroah-Hartman union hv_synic_siefp siefp;
358e72e7ac5SVitaly Kuznetsov union hv_synic_scontrol sctrl;
359dba61cdaSDexuan Cui
360f3c5e63cSMichael Kelley shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
361f3c5e63cSMichael Kelley VMBUS_MESSAGE_SINT);
362dba61cdaSDexuan Cui
363dba61cdaSDexuan Cui shared_sint.masked = 1;
364dba61cdaSDexuan Cui
365dba61cdaSDexuan Cui /* Need to correctly cleanup in the case of SMP!!! */
366dba61cdaSDexuan Cui /* Disable the interrupt */
367f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SINT0 + VMBUS_MESSAGE_SINT,
368f3c5e63cSMichael Kelley shared_sint.as_uint64);
369dba61cdaSDexuan Cui
370f3c5e63cSMichael Kelley simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP);
371faff4406STianyu Lan /*
372faff4406STianyu Lan * In Isolation VM, sim and sief pages are allocated by
373faff4406STianyu Lan * paravisor. These pages also will be used by kdump
374faff4406STianyu Lan * kernel. So just reset enable bit here and keep page
375faff4406STianyu Lan * addresses.
376faff4406STianyu Lan */
377dba61cdaSDexuan Cui simp.simp_enabled = 0;
378d3a9d7e4SDexuan Cui if (ms_hyperv.paravisor_present || hv_root_partition) {
3796afd9dc1SMichael Kelley iounmap(hv_cpu->synic_message_page);
3807fec185aSJinank Jain hv_cpu->synic_message_page = NULL;
3817fec185aSJinank Jain } else {
382dba61cdaSDexuan Cui simp.base_simp_gpa = 0;
3837fec185aSJinank Jain }
384dba61cdaSDexuan Cui
385f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SIMP, simp.as_uint64);
386dba61cdaSDexuan Cui
387f3c5e63cSMichael Kelley siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP);
388dba61cdaSDexuan Cui siefp.siefp_enabled = 0;
389faff4406STianyu Lan
390d3a9d7e4SDexuan Cui if (ms_hyperv.paravisor_present || hv_root_partition) {
3916afd9dc1SMichael Kelley iounmap(hv_cpu->synic_event_page);
3927fec185aSJinank Jain hv_cpu->synic_event_page = NULL;
3937fec185aSJinank Jain } else {
394dba61cdaSDexuan Cui siefp.base_siefp_gpa = 0;
3957fec185aSJinank Jain }
396dba61cdaSDexuan Cui
397f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
398dba61cdaSDexuan Cui
399dba61cdaSDexuan Cui /* Disable the global synic bit */
400f3c5e63cSMichael Kelley sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
401dba61cdaSDexuan Cui sctrl.enable = 0;
402f3c5e63cSMichael Kelley hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
403d608715dSMichael Kelley
404d608715dSMichael Kelley if (vmbus_irq != -1)
405d608715dSMichael Kelley disable_percpu_irq(vmbus_irq);
406dba61cdaSDexuan Cui }
407dba61cdaSDexuan Cui
408b635ccc1SAndrea Parri (Microsoft) #define HV_MAX_TRIES 3
409b635ccc1SAndrea Parri (Microsoft) /*
410b635ccc1SAndrea Parri (Microsoft) * Scan the event flags page of 'this' CPU looking for any bit that is set. If we find one
411b635ccc1SAndrea Parri (Microsoft) * bit set, then wait for a few milliseconds. Repeat these steps for a maximum of 3 times.
412b635ccc1SAndrea Parri (Microsoft) * Return 'true', if there is still any set bit after this operation; 'false', otherwise.
413b635ccc1SAndrea Parri (Microsoft) *
414b635ccc1SAndrea Parri (Microsoft) * If a bit is set, that means there is a pending channel interrupt. The expectation is
415b635ccc1SAndrea Parri (Microsoft) * that the normal interrupt handling mechanism will find and process the channel interrupt
416b635ccc1SAndrea Parri (Microsoft) * "very soon", and in the process clear the bit.
417b635ccc1SAndrea Parri (Microsoft) */
hv_synic_event_pending(void)418b635ccc1SAndrea Parri (Microsoft) static bool hv_synic_event_pending(void)
419b635ccc1SAndrea Parri (Microsoft) {
420b635ccc1SAndrea Parri (Microsoft) struct hv_per_cpu_context *hv_cpu = this_cpu_ptr(hv_context.cpu_context);
421b635ccc1SAndrea Parri (Microsoft) union hv_synic_event_flags *event =
422b635ccc1SAndrea Parri (Microsoft) (union hv_synic_event_flags *)hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT;
423b635ccc1SAndrea Parri (Microsoft) unsigned long *recv_int_page = event->flags; /* assumes VMBus version >= VERSION_WIN8 */
424b635ccc1SAndrea Parri (Microsoft) bool pending;
425b635ccc1SAndrea Parri (Microsoft) u32 relid;
426b635ccc1SAndrea Parri (Microsoft) int tries = 0;
427b635ccc1SAndrea Parri (Microsoft)
428b635ccc1SAndrea Parri (Microsoft) retry:
429b635ccc1SAndrea Parri (Microsoft) pending = false;
430b635ccc1SAndrea Parri (Microsoft) for_each_set_bit(relid, recv_int_page, HV_EVENT_FLAGS_COUNT) {
431b635ccc1SAndrea Parri (Microsoft) /* Special case - VMBus channel protocol messages */
432b635ccc1SAndrea Parri (Microsoft) if (relid == 0)
433b635ccc1SAndrea Parri (Microsoft) continue;
434b635ccc1SAndrea Parri (Microsoft) pending = true;
435b635ccc1SAndrea Parri (Microsoft) break;
436b635ccc1SAndrea Parri (Microsoft) }
437b635ccc1SAndrea Parri (Microsoft) if (pending && tries++ < HV_MAX_TRIES) {
438b635ccc1SAndrea Parri (Microsoft) usleep_range(10000, 20000);
439b635ccc1SAndrea Parri (Microsoft) goto retry;
440b635ccc1SAndrea Parri (Microsoft) }
441b635ccc1SAndrea Parri (Microsoft) return pending;
442b635ccc1SAndrea Parri (Microsoft) }
443f3c5e63cSMichael Kelley
hv_synic_cleanup(unsigned int cpu)444dba61cdaSDexuan Cui int hv_synic_cleanup(unsigned int cpu)
445dba61cdaSDexuan Cui {
446523b9408SVitaly Kuznetsov struct vmbus_channel *channel, *sc;
447523b9408SVitaly Kuznetsov bool channel_found = false;
44846a97191SGreg Kroah-Hartman
449b635ccc1SAndrea Parri (Microsoft) if (vmbus_connection.conn_state != CONNECTED)
450b635ccc1SAndrea Parri (Microsoft) goto always_cleanup;
451b635ccc1SAndrea Parri (Microsoft)
452523b9408SVitaly Kuznetsov /*
4538a857c55SAndrea Parri (Microsoft) * Hyper-V does not provide a way to change the connect CPU once
45492e4dc8bSChris Co * it is set; we must prevent the connect CPU from going offline
45592e4dc8bSChris Co * while the VM is running normally. But in the panic or kexec()
45692e4dc8bSChris Co * path where the vmbus is already disconnected, the CPU must be
45792e4dc8bSChris Co * allowed to shut down.
4588a857c55SAndrea Parri (Microsoft) */
459b635ccc1SAndrea Parri (Microsoft) if (cpu == VMBUS_CONNECT_CPU)
4608a857c55SAndrea Parri (Microsoft) return -EBUSY;
4618a857c55SAndrea Parri (Microsoft)
4628a857c55SAndrea Parri (Microsoft) /*
463523b9408SVitaly Kuznetsov * Search for channels which are bound to the CPU we're about to
464d570aec0SAndrea Parri (Microsoft) * cleanup. In case we find one and vmbus is still connected, we
465d570aec0SAndrea Parri (Microsoft) * fail; this will effectively prevent CPU offlining.
466d570aec0SAndrea Parri (Microsoft) *
467d570aec0SAndrea Parri (Microsoft) * TODO: Re-bind the channels to different CPUs.
468523b9408SVitaly Kuznetsov */
469523b9408SVitaly Kuznetsov mutex_lock(&vmbus_connection.channel_mutex);
470523b9408SVitaly Kuznetsov list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
471523b9408SVitaly Kuznetsov if (channel->target_cpu == cpu) {
472523b9408SVitaly Kuznetsov channel_found = true;
473523b9408SVitaly Kuznetsov break;
474523b9408SVitaly Kuznetsov }
475523b9408SVitaly Kuznetsov list_for_each_entry(sc, &channel->sc_list, sc_list) {
476523b9408SVitaly Kuznetsov if (sc->target_cpu == cpu) {
477523b9408SVitaly Kuznetsov channel_found = true;
478523b9408SVitaly Kuznetsov break;
479523b9408SVitaly Kuznetsov }
480523b9408SVitaly Kuznetsov }
481523b9408SVitaly Kuznetsov if (channel_found)
482523b9408SVitaly Kuznetsov break;
483523b9408SVitaly Kuznetsov }
484523b9408SVitaly Kuznetsov mutex_unlock(&vmbus_connection.channel_mutex);
485523b9408SVitaly Kuznetsov
486b635ccc1SAndrea Parri (Microsoft) if (channel_found)
487523b9408SVitaly Kuznetsov return -EBUSY;
488523b9408SVitaly Kuznetsov
489b635ccc1SAndrea Parri (Microsoft) /*
490b635ccc1SAndrea Parri (Microsoft) * channel_found == false means that any channels that were previously
491b635ccc1SAndrea Parri (Microsoft) * assigned to the CPU have been reassigned elsewhere with a call of
492b635ccc1SAndrea Parri (Microsoft) * vmbus_send_modifychannel(). Scan the event flags page looking for
493b635ccc1SAndrea Parri (Microsoft) * bits that are set and waiting with a timeout for vmbus_chan_sched()
494b635ccc1SAndrea Parri (Microsoft) * to process such bits. If bits are still set after this operation
495b635ccc1SAndrea Parri (Microsoft) * and VMBus is connected, fail the CPU offlining operation.
496b635ccc1SAndrea Parri (Microsoft) */
497b635ccc1SAndrea Parri (Microsoft) if (vmbus_proto_version >= VERSION_WIN10_V4_1 && hv_synic_event_pending())
498b635ccc1SAndrea Parri (Microsoft) return -EBUSY;
499b635ccc1SAndrea Parri (Microsoft)
500b635ccc1SAndrea Parri (Microsoft) always_cleanup:
5014df4cb9eSMichael Kelley hv_stimer_legacy_cleanup(cpu);
502e086748cSVitaly Kuznetsov
503dba61cdaSDexuan Cui hv_synic_disable_regs(cpu);
50476d36ab7SVitaly Kuznetsov
50576d36ab7SVitaly Kuznetsov return 0;
50646a97191SGreg Kroah-Hartman }
507