xref: /openbmc/linux/drivers/hv/hv.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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