xref: /linux/drivers/hv/mshv_root_main.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
1*621191d7SNuno Das Neves // SPDX-License-Identifier: GPL-2.0-only
2*621191d7SNuno Das Neves /*
3*621191d7SNuno Das Neves  * Copyright (c) 2024, Microsoft Corporation.
4*621191d7SNuno Das Neves  *
5*621191d7SNuno Das Neves  * The main part of the mshv_root module, providing APIs to create
6*621191d7SNuno Das Neves  * and manage guest partitions.
7*621191d7SNuno Das Neves  *
8*621191d7SNuno Das Neves  * Authors: Microsoft Linux virtualization team
9*621191d7SNuno Das Neves  */
10*621191d7SNuno Das Neves 
11*621191d7SNuno Das Neves #include <linux/kernel.h>
12*621191d7SNuno Das Neves #include <linux/module.h>
13*621191d7SNuno Das Neves #include <linux/fs.h>
14*621191d7SNuno Das Neves #include <linux/miscdevice.h>
15*621191d7SNuno Das Neves #include <linux/slab.h>
16*621191d7SNuno Das Neves #include <linux/file.h>
17*621191d7SNuno Das Neves #include <linux/anon_inodes.h>
18*621191d7SNuno Das Neves #include <linux/mm.h>
19*621191d7SNuno Das Neves #include <linux/io.h>
20*621191d7SNuno Das Neves #include <linux/cpuhotplug.h>
21*621191d7SNuno Das Neves #include <linux/random.h>
22*621191d7SNuno Das Neves #include <asm/mshyperv.h>
23*621191d7SNuno Das Neves #include <linux/hyperv.h>
24*621191d7SNuno Das Neves #include <linux/notifier.h>
25*621191d7SNuno Das Neves #include <linux/reboot.h>
26*621191d7SNuno Das Neves #include <linux/kexec.h>
27*621191d7SNuno Das Neves #include <linux/page-flags.h>
28*621191d7SNuno Das Neves #include <linux/crash_dump.h>
29*621191d7SNuno Das Neves #include <linux/panic_notifier.h>
30*621191d7SNuno Das Neves #include <linux/vmalloc.h>
31*621191d7SNuno Das Neves 
32*621191d7SNuno Das Neves #include "mshv_eventfd.h"
33*621191d7SNuno Das Neves #include "mshv.h"
34*621191d7SNuno Das Neves #include "mshv_root.h"
35*621191d7SNuno Das Neves 
36*621191d7SNuno Das Neves MODULE_AUTHOR("Microsoft");
37*621191d7SNuno Das Neves MODULE_LICENSE("GPL");
38*621191d7SNuno Das Neves MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM interface /dev/mshv");
39*621191d7SNuno Das Neves 
40*621191d7SNuno Das Neves /* TODO move this to mshyperv.h when needed outside driver */
41*621191d7SNuno Das Neves static inline bool hv_parent_partition(void)
42*621191d7SNuno Das Neves {
43*621191d7SNuno Das Neves 	return hv_root_partition();
44*621191d7SNuno Das Neves }
45*621191d7SNuno Das Neves 
46*621191d7SNuno Das Neves /* TODO move this to another file when debugfs code is added */
47*621191d7SNuno Das Neves enum hv_stats_vp_counters {			/* HV_THREAD_COUNTER */
48*621191d7SNuno Das Neves #if defined(CONFIG_X86)
49*621191d7SNuno Das Neves 	VpRootDispatchThreadBlocked			= 201,
50*621191d7SNuno Das Neves #elif defined(CONFIG_ARM64)
51*621191d7SNuno Das Neves 	VpRootDispatchThreadBlocked			= 94,
52*621191d7SNuno Das Neves #endif
53*621191d7SNuno Das Neves 	VpStatsMaxCounter
54*621191d7SNuno Das Neves };
55*621191d7SNuno Das Neves 
56*621191d7SNuno Das Neves struct hv_stats_page {
57*621191d7SNuno Das Neves 	union {
58*621191d7SNuno Das Neves 		u64 vp_cntrs[VpStatsMaxCounter];		/* VP counters */
59*621191d7SNuno Das Neves 		u8 data[HV_HYP_PAGE_SIZE];
60*621191d7SNuno Das Neves 	};
61*621191d7SNuno Das Neves } __packed;
62*621191d7SNuno Das Neves 
63*621191d7SNuno Das Neves struct mshv_root mshv_root;
64*621191d7SNuno Das Neves 
65*621191d7SNuno Das Neves enum hv_scheduler_type hv_scheduler_type;
66*621191d7SNuno Das Neves 
67*621191d7SNuno Das Neves /* Once we implement the fast extended hypercall ABI they can go away. */
68*621191d7SNuno Das Neves static void * __percpu *root_scheduler_input;
69*621191d7SNuno Das Neves static void * __percpu *root_scheduler_output;
70*621191d7SNuno Das Neves 
71*621191d7SNuno Das Neves static long mshv_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg);
72*621191d7SNuno Das Neves static int mshv_dev_open(struct inode *inode, struct file *filp);
73*621191d7SNuno Das Neves static int mshv_dev_release(struct inode *inode, struct file *filp);
74*621191d7SNuno Das Neves static int mshv_vp_release(struct inode *inode, struct file *filp);
75*621191d7SNuno Das Neves static long mshv_vp_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg);
76*621191d7SNuno Das Neves static int mshv_partition_release(struct inode *inode, struct file *filp);
77*621191d7SNuno Das Neves static long mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg);
78*621191d7SNuno Das Neves static int mshv_vp_mmap(struct file *file, struct vm_area_struct *vma);
79*621191d7SNuno Das Neves static vm_fault_t mshv_vp_fault(struct vm_fault *vmf);
80*621191d7SNuno Das Neves static int mshv_init_async_handler(struct mshv_partition *partition);
81*621191d7SNuno Das Neves static void mshv_async_hvcall_handler(void *data, u64 *status);
82*621191d7SNuno Das Neves 
83*621191d7SNuno Das Neves static const union hv_input_vtl input_vtl_zero;
84*621191d7SNuno Das Neves static const union hv_input_vtl input_vtl_normal = {
85*621191d7SNuno Das Neves 	.target_vtl = HV_NORMAL_VTL,
86*621191d7SNuno Das Neves 	.use_target_vtl = 1,
87*621191d7SNuno Das Neves };
88*621191d7SNuno Das Neves 
89*621191d7SNuno Das Neves static const struct vm_operations_struct mshv_vp_vm_ops = {
90*621191d7SNuno Das Neves 	.fault = mshv_vp_fault,
91*621191d7SNuno Das Neves };
92*621191d7SNuno Das Neves 
93*621191d7SNuno Das Neves static const struct file_operations mshv_vp_fops = {
94*621191d7SNuno Das Neves 	.owner = THIS_MODULE,
95*621191d7SNuno Das Neves 	.release = mshv_vp_release,
96*621191d7SNuno Das Neves 	.unlocked_ioctl = mshv_vp_ioctl,
97*621191d7SNuno Das Neves 	.llseek = noop_llseek,
98*621191d7SNuno Das Neves 	.mmap = mshv_vp_mmap,
99*621191d7SNuno Das Neves };
100*621191d7SNuno Das Neves 
101*621191d7SNuno Das Neves static const struct file_operations mshv_partition_fops = {
102*621191d7SNuno Das Neves 	.owner = THIS_MODULE,
103*621191d7SNuno Das Neves 	.release = mshv_partition_release,
104*621191d7SNuno Das Neves 	.unlocked_ioctl = mshv_partition_ioctl,
105*621191d7SNuno Das Neves 	.llseek = noop_llseek,
106*621191d7SNuno Das Neves };
107*621191d7SNuno Das Neves 
108*621191d7SNuno Das Neves static const struct file_operations mshv_dev_fops = {
109*621191d7SNuno Das Neves 	.owner = THIS_MODULE,
110*621191d7SNuno Das Neves 	.open = mshv_dev_open,
111*621191d7SNuno Das Neves 	.release = mshv_dev_release,
112*621191d7SNuno Das Neves 	.unlocked_ioctl = mshv_dev_ioctl,
113*621191d7SNuno Das Neves 	.llseek = noop_llseek,
114*621191d7SNuno Das Neves };
115*621191d7SNuno Das Neves 
116*621191d7SNuno Das Neves static struct miscdevice mshv_dev = {
117*621191d7SNuno Das Neves 	.minor = MISC_DYNAMIC_MINOR,
118*621191d7SNuno Das Neves 	.name = "mshv",
119*621191d7SNuno Das Neves 	.fops = &mshv_dev_fops,
120*621191d7SNuno Das Neves 	.mode = 0600,
121*621191d7SNuno Das Neves };
122*621191d7SNuno Das Neves 
123*621191d7SNuno Das Neves /*
124*621191d7SNuno Das Neves  * Only allow hypercalls that have a u64 partition id as the first member of
125*621191d7SNuno Das Neves  * the input structure.
126*621191d7SNuno Das Neves  * These are sorted by value.
127*621191d7SNuno Das Neves  */
128*621191d7SNuno Das Neves static u16 mshv_passthru_hvcalls[] = {
129*621191d7SNuno Das Neves 	HVCALL_GET_PARTITION_PROPERTY,
130*621191d7SNuno Das Neves 	HVCALL_SET_PARTITION_PROPERTY,
131*621191d7SNuno Das Neves 	HVCALL_INSTALL_INTERCEPT,
132*621191d7SNuno Das Neves 	HVCALL_GET_VP_REGISTERS,
133*621191d7SNuno Das Neves 	HVCALL_SET_VP_REGISTERS,
134*621191d7SNuno Das Neves 	HVCALL_TRANSLATE_VIRTUAL_ADDRESS,
135*621191d7SNuno Das Neves 	HVCALL_CLEAR_VIRTUAL_INTERRUPT,
136*621191d7SNuno Das Neves 	HVCALL_REGISTER_INTERCEPT_RESULT,
137*621191d7SNuno Das Neves 	HVCALL_ASSERT_VIRTUAL_INTERRUPT,
138*621191d7SNuno Das Neves 	HVCALL_GET_GPA_PAGES_ACCESS_STATES,
139*621191d7SNuno Das Neves 	HVCALL_SIGNAL_EVENT_DIRECT,
140*621191d7SNuno Das Neves 	HVCALL_POST_MESSAGE_DIRECT,
141*621191d7SNuno Das Neves 	HVCALL_GET_VP_CPUID_VALUES,
142*621191d7SNuno Das Neves };
143*621191d7SNuno Das Neves 
144*621191d7SNuno Das Neves static bool mshv_hvcall_is_async(u16 code)
145*621191d7SNuno Das Neves {
146*621191d7SNuno Das Neves 	switch (code) {
147*621191d7SNuno Das Neves 	case HVCALL_SET_PARTITION_PROPERTY:
148*621191d7SNuno Das Neves 		return true;
149*621191d7SNuno Das Neves 	default:
150*621191d7SNuno Das Neves 		break;
151*621191d7SNuno Das Neves 	}
152*621191d7SNuno Das Neves 	return false;
153*621191d7SNuno Das Neves }
154*621191d7SNuno Das Neves 
155*621191d7SNuno Das Neves static int mshv_ioctl_passthru_hvcall(struct mshv_partition *partition,
156*621191d7SNuno Das Neves 				      bool partition_locked,
157*621191d7SNuno Das Neves 				      void __user *user_args)
158*621191d7SNuno Das Neves {
159*621191d7SNuno Das Neves 	u64 status;
160*621191d7SNuno Das Neves 	int ret = 0, i;
161*621191d7SNuno Das Neves 	bool is_async;
162*621191d7SNuno Das Neves 	struct mshv_root_hvcall args;
163*621191d7SNuno Das Neves 	struct page *page;
164*621191d7SNuno Das Neves 	unsigned int pages_order;
165*621191d7SNuno Das Neves 	void *input_pg = NULL;
166*621191d7SNuno Das Neves 	void *output_pg = NULL;
167*621191d7SNuno Das Neves 
168*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
169*621191d7SNuno Das Neves 		return -EFAULT;
170*621191d7SNuno Das Neves 
171*621191d7SNuno Das Neves 	if (args.status || !args.in_ptr || args.in_sz < sizeof(u64) ||
172*621191d7SNuno Das Neves 	    mshv_field_nonzero(args, rsvd) || args.in_sz > HV_HYP_PAGE_SIZE)
173*621191d7SNuno Das Neves 		return -EINVAL;
174*621191d7SNuno Das Neves 
175*621191d7SNuno Das Neves 	if (args.out_ptr && (!args.out_sz || args.out_sz > HV_HYP_PAGE_SIZE))
176*621191d7SNuno Das Neves 		return -EINVAL;
177*621191d7SNuno Das Neves 
178*621191d7SNuno Das Neves 	for (i = 0; i < ARRAY_SIZE(mshv_passthru_hvcalls); ++i)
179*621191d7SNuno Das Neves 		if (args.code == mshv_passthru_hvcalls[i])
180*621191d7SNuno Das Neves 			break;
181*621191d7SNuno Das Neves 
182*621191d7SNuno Das Neves 	if (i >= ARRAY_SIZE(mshv_passthru_hvcalls))
183*621191d7SNuno Das Neves 		return -EINVAL;
184*621191d7SNuno Das Neves 
185*621191d7SNuno Das Neves 	is_async = mshv_hvcall_is_async(args.code);
186*621191d7SNuno Das Neves 	if (is_async) {
187*621191d7SNuno Das Neves 		/* async hypercalls can only be called from partition fd */
188*621191d7SNuno Das Neves 		if (!partition_locked)
189*621191d7SNuno Das Neves 			return -EINVAL;
190*621191d7SNuno Das Neves 		ret = mshv_init_async_handler(partition);
191*621191d7SNuno Das Neves 		if (ret)
192*621191d7SNuno Das Neves 			return ret;
193*621191d7SNuno Das Neves 	}
194*621191d7SNuno Das Neves 
195*621191d7SNuno Das Neves 	pages_order = args.out_ptr ? 1 : 0;
196*621191d7SNuno Das Neves 	page = alloc_pages(GFP_KERNEL, pages_order);
197*621191d7SNuno Das Neves 	if (!page)
198*621191d7SNuno Das Neves 		return -ENOMEM;
199*621191d7SNuno Das Neves 	input_pg = page_address(page);
200*621191d7SNuno Das Neves 
201*621191d7SNuno Das Neves 	if (args.out_ptr)
202*621191d7SNuno Das Neves 		output_pg = (char *)input_pg + PAGE_SIZE;
203*621191d7SNuno Das Neves 	else
204*621191d7SNuno Das Neves 		output_pg = NULL;
205*621191d7SNuno Das Neves 
206*621191d7SNuno Das Neves 	if (copy_from_user(input_pg, (void __user *)args.in_ptr,
207*621191d7SNuno Das Neves 			   args.in_sz)) {
208*621191d7SNuno Das Neves 		ret = -EFAULT;
209*621191d7SNuno Das Neves 		goto free_pages_out;
210*621191d7SNuno Das Neves 	}
211*621191d7SNuno Das Neves 
212*621191d7SNuno Das Neves 	/*
213*621191d7SNuno Das Neves 	 * NOTE: This only works because all the allowed hypercalls' input
214*621191d7SNuno Das Neves 	 * structs begin with a u64 partition_id field.
215*621191d7SNuno Das Neves 	 */
216*621191d7SNuno Das Neves 	*(u64 *)input_pg = partition->pt_id;
217*621191d7SNuno Das Neves 
218*621191d7SNuno Das Neves 	if (args.reps)
219*621191d7SNuno Das Neves 		status = hv_do_rep_hypercall(args.code, args.reps, 0,
220*621191d7SNuno Das Neves 					     input_pg, output_pg);
221*621191d7SNuno Das Neves 	else
222*621191d7SNuno Das Neves 		status = hv_do_hypercall(args.code, input_pg, output_pg);
223*621191d7SNuno Das Neves 
224*621191d7SNuno Das Neves 	if (hv_result(status) == HV_STATUS_CALL_PENDING) {
225*621191d7SNuno Das Neves 		if (is_async) {
226*621191d7SNuno Das Neves 			mshv_async_hvcall_handler(partition, &status);
227*621191d7SNuno Das Neves 		} else { /* Paranoia check. This shouldn't happen! */
228*621191d7SNuno Das Neves 			ret = -EBADFD;
229*621191d7SNuno Das Neves 			goto free_pages_out;
230*621191d7SNuno Das Neves 		}
231*621191d7SNuno Das Neves 	}
232*621191d7SNuno Das Neves 
233*621191d7SNuno Das Neves 	if (hv_result(status) == HV_STATUS_INSUFFICIENT_MEMORY) {
234*621191d7SNuno Das Neves 		ret = hv_call_deposit_pages(NUMA_NO_NODE, partition->pt_id, 1);
235*621191d7SNuno Das Neves 		if (!ret)
236*621191d7SNuno Das Neves 			ret = -EAGAIN;
237*621191d7SNuno Das Neves 	} else if (!hv_result_success(status)) {
238*621191d7SNuno Das Neves 		ret = hv_result_to_errno(status);
239*621191d7SNuno Das Neves 	}
240*621191d7SNuno Das Neves 
241*621191d7SNuno Das Neves 	/*
242*621191d7SNuno Das Neves 	 * Always return the status and output data regardless of result.
243*621191d7SNuno Das Neves 	 * The VMM may need it to determine how to proceed. E.g. the status may
244*621191d7SNuno Das Neves 	 * contain the number of reps completed if a rep hypercall partially
245*621191d7SNuno Das Neves 	 * succeeded.
246*621191d7SNuno Das Neves 	 */
247*621191d7SNuno Das Neves 	args.status = hv_result(status);
248*621191d7SNuno Das Neves 	args.reps = args.reps ? hv_repcomp(status) : 0;
249*621191d7SNuno Das Neves 	if (copy_to_user(user_args, &args, sizeof(args)))
250*621191d7SNuno Das Neves 		ret = -EFAULT;
251*621191d7SNuno Das Neves 
252*621191d7SNuno Das Neves 	if (output_pg &&
253*621191d7SNuno Das Neves 	    copy_to_user((void __user *)args.out_ptr, output_pg, args.out_sz))
254*621191d7SNuno Das Neves 		ret = -EFAULT;
255*621191d7SNuno Das Neves 
256*621191d7SNuno Das Neves free_pages_out:
257*621191d7SNuno Das Neves 	free_pages((unsigned long)input_pg, pages_order);
258*621191d7SNuno Das Neves 
259*621191d7SNuno Das Neves 	return ret;
260*621191d7SNuno Das Neves }
261*621191d7SNuno Das Neves 
262*621191d7SNuno Das Neves static inline bool is_ghcb_mapping_available(void)
263*621191d7SNuno Das Neves {
264*621191d7SNuno Das Neves #if IS_ENABLED(CONFIG_X86_64)
265*621191d7SNuno Das Neves 	return ms_hyperv.ext_features & HV_VP_GHCB_ROOT_MAPPING_AVAILABLE;
266*621191d7SNuno Das Neves #else
267*621191d7SNuno Das Neves 	return 0;
268*621191d7SNuno Das Neves #endif
269*621191d7SNuno Das Neves }
270*621191d7SNuno Das Neves 
271*621191d7SNuno Das Neves static int mshv_get_vp_registers(u32 vp_index, u64 partition_id, u16 count,
272*621191d7SNuno Das Neves 				 struct hv_register_assoc *registers)
273*621191d7SNuno Das Neves {
274*621191d7SNuno Das Neves 	return hv_call_get_vp_registers(vp_index, partition_id,
275*621191d7SNuno Das Neves 					count, input_vtl_zero, registers);
276*621191d7SNuno Das Neves }
277*621191d7SNuno Das Neves 
278*621191d7SNuno Das Neves static int mshv_set_vp_registers(u32 vp_index, u64 partition_id, u16 count,
279*621191d7SNuno Das Neves 				 struct hv_register_assoc *registers)
280*621191d7SNuno Das Neves {
281*621191d7SNuno Das Neves 	return hv_call_set_vp_registers(vp_index, partition_id,
282*621191d7SNuno Das Neves 					count, input_vtl_zero, registers);
283*621191d7SNuno Das Neves }
284*621191d7SNuno Das Neves 
285*621191d7SNuno Das Neves /*
286*621191d7SNuno Das Neves  * Explicit guest vCPU suspend is asynchronous by nature (as it is requested by
287*621191d7SNuno Das Neves  * dom0 vCPU for guest vCPU) and thus it can race with "intercept" suspend,
288*621191d7SNuno Das Neves  * done by the hypervisor.
289*621191d7SNuno Das Neves  * "Intercept" suspend leads to asynchronous message delivery to dom0 which
290*621191d7SNuno Das Neves  * should be awaited to keep the VP loop consistent (i.e. no message pending
291*621191d7SNuno Das Neves  * upon VP resume).
292*621191d7SNuno Das Neves  * VP intercept suspend can't be done when the VP is explicitly suspended
293*621191d7SNuno Das Neves  * already, and thus can be only two possible race scenarios:
294*621191d7SNuno Das Neves  *   1. implicit suspend bit set -> explicit suspend bit set -> message sent
295*621191d7SNuno Das Neves  *   2. implicit suspend bit set -> message sent -> explicit suspend bit set
296*621191d7SNuno Das Neves  * Checking for implicit suspend bit set after explicit suspend request has
297*621191d7SNuno Das Neves  * succeeded in either case allows us to reliably identify, if there is a
298*621191d7SNuno Das Neves  * message to receive and deliver to VMM.
299*621191d7SNuno Das Neves  */
300*621191d7SNuno Das Neves static int
301*621191d7SNuno Das Neves mshv_suspend_vp(const struct mshv_vp *vp, bool *message_in_flight)
302*621191d7SNuno Das Neves {
303*621191d7SNuno Das Neves 	struct hv_register_assoc explicit_suspend = {
304*621191d7SNuno Das Neves 		.name = HV_REGISTER_EXPLICIT_SUSPEND
305*621191d7SNuno Das Neves 	};
306*621191d7SNuno Das Neves 	struct hv_register_assoc intercept_suspend = {
307*621191d7SNuno Das Neves 		.name = HV_REGISTER_INTERCEPT_SUSPEND
308*621191d7SNuno Das Neves 	};
309*621191d7SNuno Das Neves 	union hv_explicit_suspend_register *es =
310*621191d7SNuno Das Neves 		&explicit_suspend.value.explicit_suspend;
311*621191d7SNuno Das Neves 	union hv_intercept_suspend_register *is =
312*621191d7SNuno Das Neves 		&intercept_suspend.value.intercept_suspend;
313*621191d7SNuno Das Neves 	int ret;
314*621191d7SNuno Das Neves 
315*621191d7SNuno Das Neves 	es->suspended = 1;
316*621191d7SNuno Das Neves 
317*621191d7SNuno Das Neves 	ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
318*621191d7SNuno Das Neves 				    1, &explicit_suspend);
319*621191d7SNuno Das Neves 	if (ret) {
320*621191d7SNuno Das Neves 		vp_err(vp, "Failed to explicitly suspend vCPU\n");
321*621191d7SNuno Das Neves 		return ret;
322*621191d7SNuno Das Neves 	}
323*621191d7SNuno Das Neves 
324*621191d7SNuno Das Neves 	ret = mshv_get_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
325*621191d7SNuno Das Neves 				    1, &intercept_suspend);
326*621191d7SNuno Das Neves 	if (ret) {
327*621191d7SNuno Das Neves 		vp_err(vp, "Failed to get intercept suspend state\n");
328*621191d7SNuno Das Neves 		return ret;
329*621191d7SNuno Das Neves 	}
330*621191d7SNuno Das Neves 
331*621191d7SNuno Das Neves 	*message_in_flight = is->suspended;
332*621191d7SNuno Das Neves 
333*621191d7SNuno Das Neves 	return 0;
334*621191d7SNuno Das Neves }
335*621191d7SNuno Das Neves 
336*621191d7SNuno Das Neves /*
337*621191d7SNuno Das Neves  * This function is used when VPs are scheduled by the hypervisor's
338*621191d7SNuno Das Neves  * scheduler.
339*621191d7SNuno Das Neves  *
340*621191d7SNuno Das Neves  * Caller has to make sure the registers contain cleared
341*621191d7SNuno Das Neves  * HV_REGISTER_INTERCEPT_SUSPEND and HV_REGISTER_EXPLICIT_SUSPEND registers
342*621191d7SNuno Das Neves  * exactly in this order (the hypervisor clears them sequentially) to avoid
343*621191d7SNuno Das Neves  * potential invalid clearing a newly arrived HV_REGISTER_INTERCEPT_SUSPEND
344*621191d7SNuno Das Neves  * after VP is released from HV_REGISTER_EXPLICIT_SUSPEND in case of the
345*621191d7SNuno Das Neves  * opposite order.
346*621191d7SNuno Das Neves  */
347*621191d7SNuno Das Neves static long mshv_run_vp_with_hyp_scheduler(struct mshv_vp *vp)
348*621191d7SNuno Das Neves {
349*621191d7SNuno Das Neves 	long ret;
350*621191d7SNuno Das Neves 	struct hv_register_assoc suspend_regs[2] = {
351*621191d7SNuno Das Neves 			{ .name = HV_REGISTER_INTERCEPT_SUSPEND },
352*621191d7SNuno Das Neves 			{ .name = HV_REGISTER_EXPLICIT_SUSPEND }
353*621191d7SNuno Das Neves 	};
354*621191d7SNuno Das Neves 	size_t count = ARRAY_SIZE(suspend_regs);
355*621191d7SNuno Das Neves 
356*621191d7SNuno Das Neves 	/* Resume VP execution */
357*621191d7SNuno Das Neves 	ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
358*621191d7SNuno Das Neves 				    count, suspend_regs);
359*621191d7SNuno Das Neves 	if (ret) {
360*621191d7SNuno Das Neves 		vp_err(vp, "Failed to resume vp execution. %lx\n", ret);
361*621191d7SNuno Das Neves 		return ret;
362*621191d7SNuno Das Neves 	}
363*621191d7SNuno Das Neves 
364*621191d7SNuno Das Neves 	ret = wait_event_interruptible(vp->run.vp_suspend_queue,
365*621191d7SNuno Das Neves 				       vp->run.kicked_by_hv == 1);
366*621191d7SNuno Das Neves 	if (ret) {
367*621191d7SNuno Das Neves 		bool message_in_flight;
368*621191d7SNuno Das Neves 
369*621191d7SNuno Das Neves 		/*
370*621191d7SNuno Das Neves 		 * Otherwise the waiting was interrupted by a signal: suspend
371*621191d7SNuno Das Neves 		 * the vCPU explicitly and copy message in flight (if any).
372*621191d7SNuno Das Neves 		 */
373*621191d7SNuno Das Neves 		ret = mshv_suspend_vp(vp, &message_in_flight);
374*621191d7SNuno Das Neves 		if (ret)
375*621191d7SNuno Das Neves 			return ret;
376*621191d7SNuno Das Neves 
377*621191d7SNuno Das Neves 		/* Return if no message in flight */
378*621191d7SNuno Das Neves 		if (!message_in_flight)
379*621191d7SNuno Das Neves 			return -EINTR;
380*621191d7SNuno Das Neves 
381*621191d7SNuno Das Neves 		/* Wait for the message in flight. */
382*621191d7SNuno Das Neves 		wait_event(vp->run.vp_suspend_queue, vp->run.kicked_by_hv == 1);
383*621191d7SNuno Das Neves 	}
384*621191d7SNuno Das Neves 
385*621191d7SNuno Das Neves 	/*
386*621191d7SNuno Das Neves 	 * Reset the flag to make the wait_event call above work
387*621191d7SNuno Das Neves 	 * next time.
388*621191d7SNuno Das Neves 	 */
389*621191d7SNuno Das Neves 	vp->run.kicked_by_hv = 0;
390*621191d7SNuno Das Neves 
391*621191d7SNuno Das Neves 	return 0;
392*621191d7SNuno Das Neves }
393*621191d7SNuno Das Neves 
394*621191d7SNuno Das Neves static int
395*621191d7SNuno Das Neves mshv_vp_dispatch(struct mshv_vp *vp, u32 flags,
396*621191d7SNuno Das Neves 		 struct hv_output_dispatch_vp *res)
397*621191d7SNuno Das Neves {
398*621191d7SNuno Das Neves 	struct hv_input_dispatch_vp *input;
399*621191d7SNuno Das Neves 	struct hv_output_dispatch_vp *output;
400*621191d7SNuno Das Neves 	u64 status;
401*621191d7SNuno Das Neves 
402*621191d7SNuno Das Neves 	preempt_disable();
403*621191d7SNuno Das Neves 	input = *this_cpu_ptr(root_scheduler_input);
404*621191d7SNuno Das Neves 	output = *this_cpu_ptr(root_scheduler_output);
405*621191d7SNuno Das Neves 
406*621191d7SNuno Das Neves 	memset(input, 0, sizeof(*input));
407*621191d7SNuno Das Neves 	memset(output, 0, sizeof(*output));
408*621191d7SNuno Das Neves 
409*621191d7SNuno Das Neves 	input->partition_id = vp->vp_partition->pt_id;
410*621191d7SNuno Das Neves 	input->vp_index = vp->vp_index;
411*621191d7SNuno Das Neves 	input->time_slice = 0; /* Run forever until something happens */
412*621191d7SNuno Das Neves 	input->spec_ctrl = 0; /* TODO: set sensible flags */
413*621191d7SNuno Das Neves 	input->flags = flags;
414*621191d7SNuno Das Neves 
415*621191d7SNuno Das Neves 	vp->run.flags.root_sched_dispatched = 1;
416*621191d7SNuno Das Neves 	status = hv_do_hypercall(HVCALL_DISPATCH_VP, input, output);
417*621191d7SNuno Das Neves 	vp->run.flags.root_sched_dispatched = 0;
418*621191d7SNuno Das Neves 
419*621191d7SNuno Das Neves 	*res = *output;
420*621191d7SNuno Das Neves 	preempt_enable();
421*621191d7SNuno Das Neves 
422*621191d7SNuno Das Neves 	if (!hv_result_success(status))
423*621191d7SNuno Das Neves 		vp_err(vp, "%s: status %s\n", __func__,
424*621191d7SNuno Das Neves 		       hv_result_to_string(status));
425*621191d7SNuno Das Neves 
426*621191d7SNuno Das Neves 	return hv_result_to_errno(status);
427*621191d7SNuno Das Neves }
428*621191d7SNuno Das Neves 
429*621191d7SNuno Das Neves static int
430*621191d7SNuno Das Neves mshv_vp_clear_explicit_suspend(struct mshv_vp *vp)
431*621191d7SNuno Das Neves {
432*621191d7SNuno Das Neves 	struct hv_register_assoc explicit_suspend = {
433*621191d7SNuno Das Neves 		.name = HV_REGISTER_EXPLICIT_SUSPEND,
434*621191d7SNuno Das Neves 		.value.explicit_suspend.suspended = 0,
435*621191d7SNuno Das Neves 	};
436*621191d7SNuno Das Neves 	int ret;
437*621191d7SNuno Das Neves 
438*621191d7SNuno Das Neves 	ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
439*621191d7SNuno Das Neves 				    1, &explicit_suspend);
440*621191d7SNuno Das Neves 
441*621191d7SNuno Das Neves 	if (ret)
442*621191d7SNuno Das Neves 		vp_err(vp, "Failed to unsuspend\n");
443*621191d7SNuno Das Neves 
444*621191d7SNuno Das Neves 	return ret;
445*621191d7SNuno Das Neves }
446*621191d7SNuno Das Neves 
447*621191d7SNuno Das Neves #if IS_ENABLED(CONFIG_X86_64)
448*621191d7SNuno Das Neves static u64 mshv_vp_interrupt_pending(struct mshv_vp *vp)
449*621191d7SNuno Das Neves {
450*621191d7SNuno Das Neves 	if (!vp->vp_register_page)
451*621191d7SNuno Das Neves 		return 0;
452*621191d7SNuno Das Neves 	return vp->vp_register_page->interrupt_vectors.as_uint64;
453*621191d7SNuno Das Neves }
454*621191d7SNuno Das Neves #else
455*621191d7SNuno Das Neves static u64 mshv_vp_interrupt_pending(struct mshv_vp *vp)
456*621191d7SNuno Das Neves {
457*621191d7SNuno Das Neves 	return 0;
458*621191d7SNuno Das Neves }
459*621191d7SNuno Das Neves #endif
460*621191d7SNuno Das Neves 
461*621191d7SNuno Das Neves static bool mshv_vp_dispatch_thread_blocked(struct mshv_vp *vp)
462*621191d7SNuno Das Neves {
463*621191d7SNuno Das Neves 	struct hv_stats_page **stats = vp->vp_stats_pages;
464*621191d7SNuno Das Neves 	u64 *self_vp_cntrs = stats[HV_STATS_AREA_SELF]->vp_cntrs;
465*621191d7SNuno Das Neves 	u64 *parent_vp_cntrs = stats[HV_STATS_AREA_PARENT]->vp_cntrs;
466*621191d7SNuno Das Neves 
467*621191d7SNuno Das Neves 	if (self_vp_cntrs[VpRootDispatchThreadBlocked])
468*621191d7SNuno Das Neves 		return self_vp_cntrs[VpRootDispatchThreadBlocked];
469*621191d7SNuno Das Neves 	return parent_vp_cntrs[VpRootDispatchThreadBlocked];
470*621191d7SNuno Das Neves }
471*621191d7SNuno Das Neves 
472*621191d7SNuno Das Neves static int
473*621191d7SNuno Das Neves mshv_vp_wait_for_hv_kick(struct mshv_vp *vp)
474*621191d7SNuno Das Neves {
475*621191d7SNuno Das Neves 	int ret;
476*621191d7SNuno Das Neves 
477*621191d7SNuno Das Neves 	ret = wait_event_interruptible(vp->run.vp_suspend_queue,
478*621191d7SNuno Das Neves 				       (vp->run.kicked_by_hv == 1 &&
479*621191d7SNuno Das Neves 					!mshv_vp_dispatch_thread_blocked(vp)) ||
480*621191d7SNuno Das Neves 				       mshv_vp_interrupt_pending(vp));
481*621191d7SNuno Das Neves 	if (ret)
482*621191d7SNuno Das Neves 		return -EINTR;
483*621191d7SNuno Das Neves 
484*621191d7SNuno Das Neves 	vp->run.flags.root_sched_blocked = 0;
485*621191d7SNuno Das Neves 	vp->run.kicked_by_hv = 0;
486*621191d7SNuno Das Neves 
487*621191d7SNuno Das Neves 	return 0;
488*621191d7SNuno Das Neves }
489*621191d7SNuno Das Neves 
490*621191d7SNuno Das Neves static int mshv_pre_guest_mode_work(struct mshv_vp *vp)
491*621191d7SNuno Das Neves {
492*621191d7SNuno Das Neves 	const ulong work_flags = _TIF_NOTIFY_SIGNAL | _TIF_SIGPENDING |
493*621191d7SNuno Das Neves 				 _TIF_NEED_RESCHED  | _TIF_NOTIFY_RESUME;
494*621191d7SNuno Das Neves 	ulong th_flags;
495*621191d7SNuno Das Neves 
496*621191d7SNuno Das Neves 	th_flags = read_thread_flags();
497*621191d7SNuno Das Neves 	while (th_flags & work_flags) {
498*621191d7SNuno Das Neves 		int ret;
499*621191d7SNuno Das Neves 
500*621191d7SNuno Das Neves 		/* nb: following will call schedule */
501*621191d7SNuno Das Neves 		ret = mshv_do_pre_guest_mode_work(th_flags);
502*621191d7SNuno Das Neves 
503*621191d7SNuno Das Neves 		if (ret)
504*621191d7SNuno Das Neves 			return ret;
505*621191d7SNuno Das Neves 
506*621191d7SNuno Das Neves 		th_flags = read_thread_flags();
507*621191d7SNuno Das Neves 	}
508*621191d7SNuno Das Neves 
509*621191d7SNuno Das Neves 	return 0;
510*621191d7SNuno Das Neves }
511*621191d7SNuno Das Neves 
512*621191d7SNuno Das Neves /* Must be called with interrupts enabled */
513*621191d7SNuno Das Neves static long mshv_run_vp_with_root_scheduler(struct mshv_vp *vp)
514*621191d7SNuno Das Neves {
515*621191d7SNuno Das Neves 	long ret;
516*621191d7SNuno Das Neves 
517*621191d7SNuno Das Neves 	if (vp->run.flags.root_sched_blocked) {
518*621191d7SNuno Das Neves 		/*
519*621191d7SNuno Das Neves 		 * Dispatch state of this VP is blocked. Need to wait
520*621191d7SNuno Das Neves 		 * for the hypervisor to clear the blocked state before
521*621191d7SNuno Das Neves 		 * dispatching it.
522*621191d7SNuno Das Neves 		 */
523*621191d7SNuno Das Neves 		ret = mshv_vp_wait_for_hv_kick(vp);
524*621191d7SNuno Das Neves 		if (ret)
525*621191d7SNuno Das Neves 			return ret;
526*621191d7SNuno Das Neves 	}
527*621191d7SNuno Das Neves 
528*621191d7SNuno Das Neves 	do {
529*621191d7SNuno Das Neves 		u32 flags = 0;
530*621191d7SNuno Das Neves 		struct hv_output_dispatch_vp output;
531*621191d7SNuno Das Neves 
532*621191d7SNuno Das Neves 		ret = mshv_pre_guest_mode_work(vp);
533*621191d7SNuno Das Neves 		if (ret)
534*621191d7SNuno Das Neves 			break;
535*621191d7SNuno Das Neves 
536*621191d7SNuno Das Neves 		if (vp->run.flags.intercept_suspend)
537*621191d7SNuno Das Neves 			flags |= HV_DISPATCH_VP_FLAG_CLEAR_INTERCEPT_SUSPEND;
538*621191d7SNuno Das Neves 
539*621191d7SNuno Das Neves 		if (mshv_vp_interrupt_pending(vp))
540*621191d7SNuno Das Neves 			flags |= HV_DISPATCH_VP_FLAG_SCAN_INTERRUPT_INJECTION;
541*621191d7SNuno Das Neves 
542*621191d7SNuno Das Neves 		ret = mshv_vp_dispatch(vp, flags, &output);
543*621191d7SNuno Das Neves 		if (ret)
544*621191d7SNuno Das Neves 			break;
545*621191d7SNuno Das Neves 
546*621191d7SNuno Das Neves 		vp->run.flags.intercept_suspend = 0;
547*621191d7SNuno Das Neves 
548*621191d7SNuno Das Neves 		if (output.dispatch_state == HV_VP_DISPATCH_STATE_BLOCKED) {
549*621191d7SNuno Das Neves 			if (output.dispatch_event ==
550*621191d7SNuno Das Neves 						HV_VP_DISPATCH_EVENT_SUSPEND) {
551*621191d7SNuno Das Neves 				/*
552*621191d7SNuno Das Neves 				 * TODO: remove the warning once VP canceling
553*621191d7SNuno Das Neves 				 *	 is supported
554*621191d7SNuno Das Neves 				 */
555*621191d7SNuno Das Neves 				WARN_ONCE(atomic64_read(&vp->run.vp_signaled_count),
556*621191d7SNuno Das Neves 					  "%s: vp#%d: unexpected explicit suspend\n",
557*621191d7SNuno Das Neves 					  __func__, vp->vp_index);
558*621191d7SNuno Das Neves 				/*
559*621191d7SNuno Das Neves 				 * Need to clear explicit suspend before
560*621191d7SNuno Das Neves 				 * dispatching.
561*621191d7SNuno Das Neves 				 * Explicit suspend is either:
562*621191d7SNuno Das Neves 				 * - set right after the first VP dispatch or
563*621191d7SNuno Das Neves 				 * - set explicitly via hypercall
564*621191d7SNuno Das Neves 				 * Since the latter case is not yet supported,
565*621191d7SNuno Das Neves 				 * simply clear it here.
566*621191d7SNuno Das Neves 				 */
567*621191d7SNuno Das Neves 				ret = mshv_vp_clear_explicit_suspend(vp);
568*621191d7SNuno Das Neves 				if (ret)
569*621191d7SNuno Das Neves 					break;
570*621191d7SNuno Das Neves 
571*621191d7SNuno Das Neves 				ret = mshv_vp_wait_for_hv_kick(vp);
572*621191d7SNuno Das Neves 				if (ret)
573*621191d7SNuno Das Neves 					break;
574*621191d7SNuno Das Neves 			} else {
575*621191d7SNuno Das Neves 				vp->run.flags.root_sched_blocked = 1;
576*621191d7SNuno Das Neves 				ret = mshv_vp_wait_for_hv_kick(vp);
577*621191d7SNuno Das Neves 				if (ret)
578*621191d7SNuno Das Neves 					break;
579*621191d7SNuno Das Neves 			}
580*621191d7SNuno Das Neves 		} else {
581*621191d7SNuno Das Neves 			/* HV_VP_DISPATCH_STATE_READY */
582*621191d7SNuno Das Neves 			if (output.dispatch_event ==
583*621191d7SNuno Das Neves 						HV_VP_DISPATCH_EVENT_INTERCEPT)
584*621191d7SNuno Das Neves 				vp->run.flags.intercept_suspend = 1;
585*621191d7SNuno Das Neves 		}
586*621191d7SNuno Das Neves 	} while (!vp->run.flags.intercept_suspend);
587*621191d7SNuno Das Neves 
588*621191d7SNuno Das Neves 	return ret;
589*621191d7SNuno Das Neves }
590*621191d7SNuno Das Neves 
591*621191d7SNuno Das Neves static_assert(sizeof(struct hv_message) <= MSHV_RUN_VP_BUF_SZ,
592*621191d7SNuno Das Neves 	      "sizeof(struct hv_message) must not exceed MSHV_RUN_VP_BUF_SZ");
593*621191d7SNuno Das Neves 
594*621191d7SNuno Das Neves static long mshv_vp_ioctl_run_vp(struct mshv_vp *vp, void __user *ret_msg)
595*621191d7SNuno Das Neves {
596*621191d7SNuno Das Neves 	long rc;
597*621191d7SNuno Das Neves 
598*621191d7SNuno Das Neves 	if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
599*621191d7SNuno Das Neves 		rc = mshv_run_vp_with_root_scheduler(vp);
600*621191d7SNuno Das Neves 	else
601*621191d7SNuno Das Neves 		rc = mshv_run_vp_with_hyp_scheduler(vp);
602*621191d7SNuno Das Neves 
603*621191d7SNuno Das Neves 	if (rc)
604*621191d7SNuno Das Neves 		return rc;
605*621191d7SNuno Das Neves 
606*621191d7SNuno Das Neves 	if (copy_to_user(ret_msg, vp->vp_intercept_msg_page,
607*621191d7SNuno Das Neves 			 sizeof(struct hv_message)))
608*621191d7SNuno Das Neves 		rc = -EFAULT;
609*621191d7SNuno Das Neves 
610*621191d7SNuno Das Neves 	return rc;
611*621191d7SNuno Das Neves }
612*621191d7SNuno Das Neves 
613*621191d7SNuno Das Neves static int
614*621191d7SNuno Das Neves mshv_vp_ioctl_get_set_state_pfn(struct mshv_vp *vp,
615*621191d7SNuno Das Neves 				struct hv_vp_state_data state_data,
616*621191d7SNuno Das Neves 				unsigned long user_pfn, size_t page_count,
617*621191d7SNuno Das Neves 				bool is_set)
618*621191d7SNuno Das Neves {
619*621191d7SNuno Das Neves 	int completed, ret = 0;
620*621191d7SNuno Das Neves 	unsigned long check;
621*621191d7SNuno Das Neves 	struct page **pages;
622*621191d7SNuno Das Neves 
623*621191d7SNuno Das Neves 	if (page_count > INT_MAX)
624*621191d7SNuno Das Neves 		return -EINVAL;
625*621191d7SNuno Das Neves 	/*
626*621191d7SNuno Das Neves 	 * Check the arithmetic for wraparound/overflow.
627*621191d7SNuno Das Neves 	 * The last page address in the buffer is:
628*621191d7SNuno Das Neves 	 * (user_pfn + (page_count - 1)) * PAGE_SIZE
629*621191d7SNuno Das Neves 	 */
630*621191d7SNuno Das Neves 	if (check_add_overflow(user_pfn, (page_count - 1), &check))
631*621191d7SNuno Das Neves 		return -EOVERFLOW;
632*621191d7SNuno Das Neves 	if (check_mul_overflow(check, PAGE_SIZE, &check))
633*621191d7SNuno Das Neves 		return -EOVERFLOW;
634*621191d7SNuno Das Neves 
635*621191d7SNuno Das Neves 	/* Pin user pages so hypervisor can copy directly to them */
636*621191d7SNuno Das Neves 	pages = kcalloc(page_count, sizeof(struct page *), GFP_KERNEL);
637*621191d7SNuno Das Neves 	if (!pages)
638*621191d7SNuno Das Neves 		return -ENOMEM;
639*621191d7SNuno Das Neves 
640*621191d7SNuno Das Neves 	for (completed = 0; completed < page_count; completed += ret) {
641*621191d7SNuno Das Neves 		unsigned long user_addr = (user_pfn + completed) * PAGE_SIZE;
642*621191d7SNuno Das Neves 		int remaining = page_count - completed;
643*621191d7SNuno Das Neves 
644*621191d7SNuno Das Neves 		ret = pin_user_pages_fast(user_addr, remaining, FOLL_WRITE,
645*621191d7SNuno Das Neves 					  &pages[completed]);
646*621191d7SNuno Das Neves 		if (ret < 0) {
647*621191d7SNuno Das Neves 			vp_err(vp, "%s: Failed to pin user pages error %i\n",
648*621191d7SNuno Das Neves 			       __func__, ret);
649*621191d7SNuno Das Neves 			goto unpin_pages;
650*621191d7SNuno Das Neves 		}
651*621191d7SNuno Das Neves 	}
652*621191d7SNuno Das Neves 
653*621191d7SNuno Das Neves 	if (is_set)
654*621191d7SNuno Das Neves 		ret = hv_call_set_vp_state(vp->vp_index,
655*621191d7SNuno Das Neves 					   vp->vp_partition->pt_id,
656*621191d7SNuno Das Neves 					   state_data, page_count, pages,
657*621191d7SNuno Das Neves 					   0, NULL);
658*621191d7SNuno Das Neves 	else
659*621191d7SNuno Das Neves 		ret = hv_call_get_vp_state(vp->vp_index,
660*621191d7SNuno Das Neves 					   vp->vp_partition->pt_id,
661*621191d7SNuno Das Neves 					   state_data, page_count, pages,
662*621191d7SNuno Das Neves 					   NULL);
663*621191d7SNuno Das Neves 
664*621191d7SNuno Das Neves unpin_pages:
665*621191d7SNuno Das Neves 	unpin_user_pages(pages, completed);
666*621191d7SNuno Das Neves 	kfree(pages);
667*621191d7SNuno Das Neves 	return ret;
668*621191d7SNuno Das Neves }
669*621191d7SNuno Das Neves 
670*621191d7SNuno Das Neves static long
671*621191d7SNuno Das Neves mshv_vp_ioctl_get_set_state(struct mshv_vp *vp,
672*621191d7SNuno Das Neves 			    struct mshv_get_set_vp_state __user *user_args,
673*621191d7SNuno Das Neves 			    bool is_set)
674*621191d7SNuno Das Neves {
675*621191d7SNuno Das Neves 	struct mshv_get_set_vp_state args;
676*621191d7SNuno Das Neves 	long ret = 0;
677*621191d7SNuno Das Neves 	union hv_output_get_vp_state vp_state;
678*621191d7SNuno Das Neves 	u32 data_sz;
679*621191d7SNuno Das Neves 	struct hv_vp_state_data state_data = {};
680*621191d7SNuno Das Neves 
681*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
682*621191d7SNuno Das Neves 		return -EFAULT;
683*621191d7SNuno Das Neves 
684*621191d7SNuno Das Neves 	if (args.type >= MSHV_VP_STATE_COUNT || mshv_field_nonzero(args, rsvd) ||
685*621191d7SNuno Das Neves 	    !args.buf_sz || !PAGE_ALIGNED(args.buf_sz) ||
686*621191d7SNuno Das Neves 	    !PAGE_ALIGNED(args.buf_ptr))
687*621191d7SNuno Das Neves 		return -EINVAL;
688*621191d7SNuno Das Neves 
689*621191d7SNuno Das Neves 	if (!access_ok((void __user *)args.buf_ptr, args.buf_sz))
690*621191d7SNuno Das Neves 		return -EFAULT;
691*621191d7SNuno Das Neves 
692*621191d7SNuno Das Neves 	switch (args.type) {
693*621191d7SNuno Das Neves 	case MSHV_VP_STATE_LAPIC:
694*621191d7SNuno Das Neves 		state_data.type = HV_GET_SET_VP_STATE_LAPIC_STATE;
695*621191d7SNuno Das Neves 		data_sz = HV_HYP_PAGE_SIZE;
696*621191d7SNuno Das Neves 		break;
697*621191d7SNuno Das Neves 	case MSHV_VP_STATE_XSAVE:
698*621191d7SNuno Das Neves 	{
699*621191d7SNuno Das Neves 		u64 data_sz_64;
700*621191d7SNuno Das Neves 
701*621191d7SNuno Das Neves 		ret = hv_call_get_partition_property(vp->vp_partition->pt_id,
702*621191d7SNuno Das Neves 						     HV_PARTITION_PROPERTY_XSAVE_STATES,
703*621191d7SNuno Das Neves 						     &state_data.xsave.states.as_uint64);
704*621191d7SNuno Das Neves 		if (ret)
705*621191d7SNuno Das Neves 			return ret;
706*621191d7SNuno Das Neves 
707*621191d7SNuno Das Neves 		ret = hv_call_get_partition_property(vp->vp_partition->pt_id,
708*621191d7SNuno Das Neves 						     HV_PARTITION_PROPERTY_MAX_XSAVE_DATA_SIZE,
709*621191d7SNuno Das Neves 						     &data_sz_64);
710*621191d7SNuno Das Neves 		if (ret)
711*621191d7SNuno Das Neves 			return ret;
712*621191d7SNuno Das Neves 
713*621191d7SNuno Das Neves 		data_sz = (u32)data_sz_64;
714*621191d7SNuno Das Neves 		state_data.xsave.flags = 0;
715*621191d7SNuno Das Neves 		/* Always request legacy states */
716*621191d7SNuno Das Neves 		state_data.xsave.states.legacy_x87 = 1;
717*621191d7SNuno Das Neves 		state_data.xsave.states.legacy_sse = 1;
718*621191d7SNuno Das Neves 		state_data.type = HV_GET_SET_VP_STATE_XSAVE;
719*621191d7SNuno Das Neves 		break;
720*621191d7SNuno Das Neves 	}
721*621191d7SNuno Das Neves 	case MSHV_VP_STATE_SIMP:
722*621191d7SNuno Das Neves 		state_data.type = HV_GET_SET_VP_STATE_SIM_PAGE;
723*621191d7SNuno Das Neves 		data_sz = HV_HYP_PAGE_SIZE;
724*621191d7SNuno Das Neves 		break;
725*621191d7SNuno Das Neves 	case MSHV_VP_STATE_SIEFP:
726*621191d7SNuno Das Neves 		state_data.type = HV_GET_SET_VP_STATE_SIEF_PAGE;
727*621191d7SNuno Das Neves 		data_sz = HV_HYP_PAGE_SIZE;
728*621191d7SNuno Das Neves 		break;
729*621191d7SNuno Das Neves 	case MSHV_VP_STATE_SYNTHETIC_TIMERS:
730*621191d7SNuno Das Neves 		state_data.type = HV_GET_SET_VP_STATE_SYNTHETIC_TIMERS;
731*621191d7SNuno Das Neves 		data_sz = sizeof(vp_state.synthetic_timers_state);
732*621191d7SNuno Das Neves 		break;
733*621191d7SNuno Das Neves 	default:
734*621191d7SNuno Das Neves 		return -EINVAL;
735*621191d7SNuno Das Neves 	}
736*621191d7SNuno Das Neves 
737*621191d7SNuno Das Neves 	if (copy_to_user(&user_args->buf_sz, &data_sz, sizeof(user_args->buf_sz)))
738*621191d7SNuno Das Neves 		return -EFAULT;
739*621191d7SNuno Das Neves 
740*621191d7SNuno Das Neves 	if (data_sz > args.buf_sz)
741*621191d7SNuno Das Neves 		return -EINVAL;
742*621191d7SNuno Das Neves 
743*621191d7SNuno Das Neves 	/* If the data is transmitted via pfns, delegate to helper */
744*621191d7SNuno Das Neves 	if (state_data.type & HV_GET_SET_VP_STATE_TYPE_PFN) {
745*621191d7SNuno Das Neves 		unsigned long user_pfn = PFN_DOWN(args.buf_ptr);
746*621191d7SNuno Das Neves 		size_t page_count = PFN_DOWN(args.buf_sz);
747*621191d7SNuno Das Neves 
748*621191d7SNuno Das Neves 		return mshv_vp_ioctl_get_set_state_pfn(vp, state_data, user_pfn,
749*621191d7SNuno Das Neves 						       page_count, is_set);
750*621191d7SNuno Das Neves 	}
751*621191d7SNuno Das Neves 
752*621191d7SNuno Das Neves 	/* Paranoia check - this shouldn't happen! */
753*621191d7SNuno Das Neves 	if (data_sz > sizeof(vp_state)) {
754*621191d7SNuno Das Neves 		vp_err(vp, "Invalid vp state data size!\n");
755*621191d7SNuno Das Neves 		return -EINVAL;
756*621191d7SNuno Das Neves 	}
757*621191d7SNuno Das Neves 
758*621191d7SNuno Das Neves 	if (is_set) {
759*621191d7SNuno Das Neves 		if (copy_from_user(&vp_state, (__user void *)args.buf_ptr, data_sz))
760*621191d7SNuno Das Neves 			return -EFAULT;
761*621191d7SNuno Das Neves 
762*621191d7SNuno Das Neves 		return hv_call_set_vp_state(vp->vp_index,
763*621191d7SNuno Das Neves 					    vp->vp_partition->pt_id,
764*621191d7SNuno Das Neves 					    state_data, 0, NULL,
765*621191d7SNuno Das Neves 					    sizeof(vp_state), (u8 *)&vp_state);
766*621191d7SNuno Das Neves 	}
767*621191d7SNuno Das Neves 
768*621191d7SNuno Das Neves 	ret = hv_call_get_vp_state(vp->vp_index, vp->vp_partition->pt_id,
769*621191d7SNuno Das Neves 				   state_data, 0, NULL, &vp_state);
770*621191d7SNuno Das Neves 	if (ret)
771*621191d7SNuno Das Neves 		return ret;
772*621191d7SNuno Das Neves 
773*621191d7SNuno Das Neves 	if (copy_to_user((void __user *)args.buf_ptr, &vp_state, data_sz))
774*621191d7SNuno Das Neves 		return -EFAULT;
775*621191d7SNuno Das Neves 
776*621191d7SNuno Das Neves 	return 0;
777*621191d7SNuno Das Neves }
778*621191d7SNuno Das Neves 
779*621191d7SNuno Das Neves static long
780*621191d7SNuno Das Neves mshv_vp_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
781*621191d7SNuno Das Neves {
782*621191d7SNuno Das Neves 	struct mshv_vp *vp = filp->private_data;
783*621191d7SNuno Das Neves 	long r = -ENOTTY;
784*621191d7SNuno Das Neves 
785*621191d7SNuno Das Neves 	if (mutex_lock_killable(&vp->vp_mutex))
786*621191d7SNuno Das Neves 		return -EINTR;
787*621191d7SNuno Das Neves 
788*621191d7SNuno Das Neves 	switch (ioctl) {
789*621191d7SNuno Das Neves 	case MSHV_RUN_VP:
790*621191d7SNuno Das Neves 		r = mshv_vp_ioctl_run_vp(vp, (void __user *)arg);
791*621191d7SNuno Das Neves 		break;
792*621191d7SNuno Das Neves 	case MSHV_GET_VP_STATE:
793*621191d7SNuno Das Neves 		r = mshv_vp_ioctl_get_set_state(vp, (void __user *)arg, false);
794*621191d7SNuno Das Neves 		break;
795*621191d7SNuno Das Neves 	case MSHV_SET_VP_STATE:
796*621191d7SNuno Das Neves 		r = mshv_vp_ioctl_get_set_state(vp, (void __user *)arg, true);
797*621191d7SNuno Das Neves 		break;
798*621191d7SNuno Das Neves 	case MSHV_ROOT_HVCALL:
799*621191d7SNuno Das Neves 		r = mshv_ioctl_passthru_hvcall(vp->vp_partition, false,
800*621191d7SNuno Das Neves 					       (void __user *)arg);
801*621191d7SNuno Das Neves 		break;
802*621191d7SNuno Das Neves 	default:
803*621191d7SNuno Das Neves 		vp_warn(vp, "Invalid ioctl: %#x\n", ioctl);
804*621191d7SNuno Das Neves 		break;
805*621191d7SNuno Das Neves 	}
806*621191d7SNuno Das Neves 	mutex_unlock(&vp->vp_mutex);
807*621191d7SNuno Das Neves 
808*621191d7SNuno Das Neves 	return r;
809*621191d7SNuno Das Neves }
810*621191d7SNuno Das Neves 
811*621191d7SNuno Das Neves static vm_fault_t mshv_vp_fault(struct vm_fault *vmf)
812*621191d7SNuno Das Neves {
813*621191d7SNuno Das Neves 	struct mshv_vp *vp = vmf->vma->vm_file->private_data;
814*621191d7SNuno Das Neves 
815*621191d7SNuno Das Neves 	switch (vmf->vma->vm_pgoff) {
816*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_REGISTERS:
817*621191d7SNuno Das Neves 		vmf->page = virt_to_page(vp->vp_register_page);
818*621191d7SNuno Das Neves 		break;
819*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_INTERCEPT_MESSAGE:
820*621191d7SNuno Das Neves 		vmf->page = virt_to_page(vp->vp_intercept_msg_page);
821*621191d7SNuno Das Neves 		break;
822*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_GHCB:
823*621191d7SNuno Das Neves 		vmf->page = virt_to_page(vp->vp_ghcb_page);
824*621191d7SNuno Das Neves 		break;
825*621191d7SNuno Das Neves 	default:
826*621191d7SNuno Das Neves 		return VM_FAULT_SIGBUS;
827*621191d7SNuno Das Neves 	}
828*621191d7SNuno Das Neves 
829*621191d7SNuno Das Neves 	get_page(vmf->page);
830*621191d7SNuno Das Neves 
831*621191d7SNuno Das Neves 	return 0;
832*621191d7SNuno Das Neves }
833*621191d7SNuno Das Neves 
834*621191d7SNuno Das Neves static int mshv_vp_mmap(struct file *file, struct vm_area_struct *vma)
835*621191d7SNuno Das Neves {
836*621191d7SNuno Das Neves 	struct mshv_vp *vp = file->private_data;
837*621191d7SNuno Das Neves 
838*621191d7SNuno Das Neves 	switch (vma->vm_pgoff) {
839*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_REGISTERS:
840*621191d7SNuno Das Neves 		if (!vp->vp_register_page)
841*621191d7SNuno Das Neves 			return -ENODEV;
842*621191d7SNuno Das Neves 		break;
843*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_INTERCEPT_MESSAGE:
844*621191d7SNuno Das Neves 		if (!vp->vp_intercept_msg_page)
845*621191d7SNuno Das Neves 			return -ENODEV;
846*621191d7SNuno Das Neves 		break;
847*621191d7SNuno Das Neves 	case MSHV_VP_MMAP_OFFSET_GHCB:
848*621191d7SNuno Das Neves 		if (!vp->vp_ghcb_page)
849*621191d7SNuno Das Neves 			return -ENODEV;
850*621191d7SNuno Das Neves 		break;
851*621191d7SNuno Das Neves 	default:
852*621191d7SNuno Das Neves 		return -EINVAL;
853*621191d7SNuno Das Neves 	}
854*621191d7SNuno Das Neves 
855*621191d7SNuno Das Neves 	vma->vm_ops = &mshv_vp_vm_ops;
856*621191d7SNuno Das Neves 	return 0;
857*621191d7SNuno Das Neves }
858*621191d7SNuno Das Neves 
859*621191d7SNuno Das Neves static int
860*621191d7SNuno Das Neves mshv_vp_release(struct inode *inode, struct file *filp)
861*621191d7SNuno Das Neves {
862*621191d7SNuno Das Neves 	struct mshv_vp *vp = filp->private_data;
863*621191d7SNuno Das Neves 
864*621191d7SNuno Das Neves 	/* Rest of VP cleanup happens in destroy_partition() */
865*621191d7SNuno Das Neves 	mshv_partition_put(vp->vp_partition);
866*621191d7SNuno Das Neves 	return 0;
867*621191d7SNuno Das Neves }
868*621191d7SNuno Das Neves 
869*621191d7SNuno Das Neves static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
870*621191d7SNuno Das Neves {
871*621191d7SNuno Das Neves 	union hv_stats_object_identity identity = {
872*621191d7SNuno Das Neves 		.vp.partition_id = partition_id,
873*621191d7SNuno Das Neves 		.vp.vp_index = vp_index,
874*621191d7SNuno Das Neves 	};
875*621191d7SNuno Das Neves 
876*621191d7SNuno Das Neves 	identity.vp.stats_area_type = HV_STATS_AREA_SELF;
877*621191d7SNuno Das Neves 	hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
878*621191d7SNuno Das Neves 
879*621191d7SNuno Das Neves 	identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
880*621191d7SNuno Das Neves 	hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
881*621191d7SNuno Das Neves }
882*621191d7SNuno Das Neves 
883*621191d7SNuno Das Neves static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
884*621191d7SNuno Das Neves 			     void *stats_pages[])
885*621191d7SNuno Das Neves {
886*621191d7SNuno Das Neves 	union hv_stats_object_identity identity = {
887*621191d7SNuno Das Neves 		.vp.partition_id = partition_id,
888*621191d7SNuno Das Neves 		.vp.vp_index = vp_index,
889*621191d7SNuno Das Neves 	};
890*621191d7SNuno Das Neves 	int err;
891*621191d7SNuno Das Neves 
892*621191d7SNuno Das Neves 	identity.vp.stats_area_type = HV_STATS_AREA_SELF;
893*621191d7SNuno Das Neves 	err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
894*621191d7SNuno Das Neves 				    &stats_pages[HV_STATS_AREA_SELF]);
895*621191d7SNuno Das Neves 	if (err)
896*621191d7SNuno Das Neves 		return err;
897*621191d7SNuno Das Neves 
898*621191d7SNuno Das Neves 	identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
899*621191d7SNuno Das Neves 	err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
900*621191d7SNuno Das Neves 				    &stats_pages[HV_STATS_AREA_PARENT]);
901*621191d7SNuno Das Neves 	if (err)
902*621191d7SNuno Das Neves 		goto unmap_self;
903*621191d7SNuno Das Neves 
904*621191d7SNuno Das Neves 	return 0;
905*621191d7SNuno Das Neves 
906*621191d7SNuno Das Neves unmap_self:
907*621191d7SNuno Das Neves 	identity.vp.stats_area_type = HV_STATS_AREA_SELF;
908*621191d7SNuno Das Neves 	hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
909*621191d7SNuno Das Neves 	return err;
910*621191d7SNuno Das Neves }
911*621191d7SNuno Das Neves 
912*621191d7SNuno Das Neves static long
913*621191d7SNuno Das Neves mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
914*621191d7SNuno Das Neves 			       void __user *arg)
915*621191d7SNuno Das Neves {
916*621191d7SNuno Das Neves 	struct mshv_create_vp args;
917*621191d7SNuno Das Neves 	struct mshv_vp *vp;
918*621191d7SNuno Das Neves 	struct page *intercept_message_page, *register_page, *ghcb_page;
919*621191d7SNuno Das Neves 	void *stats_pages[2];
920*621191d7SNuno Das Neves 	long ret;
921*621191d7SNuno Das Neves 
922*621191d7SNuno Das Neves 	if (copy_from_user(&args, arg, sizeof(args)))
923*621191d7SNuno Das Neves 		return -EFAULT;
924*621191d7SNuno Das Neves 
925*621191d7SNuno Das Neves 	if (args.vp_index >= MSHV_MAX_VPS)
926*621191d7SNuno Das Neves 		return -EINVAL;
927*621191d7SNuno Das Neves 
928*621191d7SNuno Das Neves 	if (partition->pt_vp_array[args.vp_index])
929*621191d7SNuno Das Neves 		return -EEXIST;
930*621191d7SNuno Das Neves 
931*621191d7SNuno Das Neves 	ret = hv_call_create_vp(NUMA_NO_NODE, partition->pt_id, args.vp_index,
932*621191d7SNuno Das Neves 				0 /* Only valid for root partition VPs */);
933*621191d7SNuno Das Neves 	if (ret)
934*621191d7SNuno Das Neves 		return ret;
935*621191d7SNuno Das Neves 
936*621191d7SNuno Das Neves 	ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
937*621191d7SNuno Das Neves 					HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
938*621191d7SNuno Das Neves 					input_vtl_zero,
939*621191d7SNuno Das Neves 					&intercept_message_page);
940*621191d7SNuno Das Neves 	if (ret)
941*621191d7SNuno Das Neves 		goto destroy_vp;
942*621191d7SNuno Das Neves 
943*621191d7SNuno Das Neves 	if (!mshv_partition_encrypted(partition)) {
944*621191d7SNuno Das Neves 		ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
945*621191d7SNuno Das Neves 						HV_VP_STATE_PAGE_REGISTERS,
946*621191d7SNuno Das Neves 						input_vtl_zero,
947*621191d7SNuno Das Neves 						&register_page);
948*621191d7SNuno Das Neves 		if (ret)
949*621191d7SNuno Das Neves 			goto unmap_intercept_message_page;
950*621191d7SNuno Das Neves 	}
951*621191d7SNuno Das Neves 
952*621191d7SNuno Das Neves 	if (mshv_partition_encrypted(partition) &&
953*621191d7SNuno Das Neves 	    is_ghcb_mapping_available()) {
954*621191d7SNuno Das Neves 		ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
955*621191d7SNuno Das Neves 						HV_VP_STATE_PAGE_GHCB,
956*621191d7SNuno Das Neves 						input_vtl_normal,
957*621191d7SNuno Das Neves 						&ghcb_page);
958*621191d7SNuno Das Neves 		if (ret)
959*621191d7SNuno Das Neves 			goto unmap_register_page;
960*621191d7SNuno Das Neves 	}
961*621191d7SNuno Das Neves 
962*621191d7SNuno Das Neves 	if (hv_parent_partition()) {
963*621191d7SNuno Das Neves 		ret = mshv_vp_stats_map(partition->pt_id, args.vp_index,
964*621191d7SNuno Das Neves 					stats_pages);
965*621191d7SNuno Das Neves 		if (ret)
966*621191d7SNuno Das Neves 			goto unmap_ghcb_page;
967*621191d7SNuno Das Neves 	}
968*621191d7SNuno Das Neves 
969*621191d7SNuno Das Neves 	vp = kzalloc(sizeof(*vp), GFP_KERNEL);
970*621191d7SNuno Das Neves 	if (!vp)
971*621191d7SNuno Das Neves 		goto unmap_stats_pages;
972*621191d7SNuno Das Neves 
973*621191d7SNuno Das Neves 	vp->vp_partition = mshv_partition_get(partition);
974*621191d7SNuno Das Neves 	if (!vp->vp_partition) {
975*621191d7SNuno Das Neves 		ret = -EBADF;
976*621191d7SNuno Das Neves 		goto free_vp;
977*621191d7SNuno Das Neves 	}
978*621191d7SNuno Das Neves 
979*621191d7SNuno Das Neves 	mutex_init(&vp->vp_mutex);
980*621191d7SNuno Das Neves 	init_waitqueue_head(&vp->run.vp_suspend_queue);
981*621191d7SNuno Das Neves 	atomic64_set(&vp->run.vp_signaled_count, 0);
982*621191d7SNuno Das Neves 
983*621191d7SNuno Das Neves 	vp->vp_index = args.vp_index;
984*621191d7SNuno Das Neves 	vp->vp_intercept_msg_page = page_to_virt(intercept_message_page);
985*621191d7SNuno Das Neves 	if (!mshv_partition_encrypted(partition))
986*621191d7SNuno Das Neves 		vp->vp_register_page = page_to_virt(register_page);
987*621191d7SNuno Das Neves 
988*621191d7SNuno Das Neves 	if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
989*621191d7SNuno Das Neves 		vp->vp_ghcb_page = page_to_virt(ghcb_page);
990*621191d7SNuno Das Neves 
991*621191d7SNuno Das Neves 	if (hv_parent_partition())
992*621191d7SNuno Das Neves 		memcpy(vp->vp_stats_pages, stats_pages, sizeof(stats_pages));
993*621191d7SNuno Das Neves 
994*621191d7SNuno Das Neves 	/*
995*621191d7SNuno Das Neves 	 * Keep anon_inode_getfd last: it installs fd in the file struct and
996*621191d7SNuno Das Neves 	 * thus makes the state accessible in user space.
997*621191d7SNuno Das Neves 	 */
998*621191d7SNuno Das Neves 	ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
999*621191d7SNuno Das Neves 			       O_RDWR | O_CLOEXEC);
1000*621191d7SNuno Das Neves 	if (ret < 0)
1001*621191d7SNuno Das Neves 		goto put_partition;
1002*621191d7SNuno Das Neves 
1003*621191d7SNuno Das Neves 	/* already exclusive with the partition mutex for all ioctls */
1004*621191d7SNuno Das Neves 	partition->pt_vp_count++;
1005*621191d7SNuno Das Neves 	partition->pt_vp_array[args.vp_index] = vp;
1006*621191d7SNuno Das Neves 
1007*621191d7SNuno Das Neves 	return ret;
1008*621191d7SNuno Das Neves 
1009*621191d7SNuno Das Neves put_partition:
1010*621191d7SNuno Das Neves 	mshv_partition_put(partition);
1011*621191d7SNuno Das Neves free_vp:
1012*621191d7SNuno Das Neves 	kfree(vp);
1013*621191d7SNuno Das Neves unmap_stats_pages:
1014*621191d7SNuno Das Neves 	if (hv_parent_partition())
1015*621191d7SNuno Das Neves 		mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
1016*621191d7SNuno Das Neves unmap_ghcb_page:
1017*621191d7SNuno Das Neves 	if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available()) {
1018*621191d7SNuno Das Neves 		hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
1019*621191d7SNuno Das Neves 					    HV_VP_STATE_PAGE_GHCB,
1020*621191d7SNuno Das Neves 					    input_vtl_normal);
1021*621191d7SNuno Das Neves 	}
1022*621191d7SNuno Das Neves unmap_register_page:
1023*621191d7SNuno Das Neves 	if (!mshv_partition_encrypted(partition)) {
1024*621191d7SNuno Das Neves 		hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
1025*621191d7SNuno Das Neves 					    HV_VP_STATE_PAGE_REGISTERS,
1026*621191d7SNuno Das Neves 					    input_vtl_zero);
1027*621191d7SNuno Das Neves 	}
1028*621191d7SNuno Das Neves unmap_intercept_message_page:
1029*621191d7SNuno Das Neves 	hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
1030*621191d7SNuno Das Neves 				    HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
1031*621191d7SNuno Das Neves 				    input_vtl_zero);
1032*621191d7SNuno Das Neves destroy_vp:
1033*621191d7SNuno Das Neves 	hv_call_delete_vp(partition->pt_id, args.vp_index);
1034*621191d7SNuno Das Neves 	return ret;
1035*621191d7SNuno Das Neves }
1036*621191d7SNuno Das Neves 
1037*621191d7SNuno Das Neves static int mshv_init_async_handler(struct mshv_partition *partition)
1038*621191d7SNuno Das Neves {
1039*621191d7SNuno Das Neves 	if (completion_done(&partition->async_hypercall)) {
1040*621191d7SNuno Das Neves 		pt_err(partition,
1041*621191d7SNuno Das Neves 		       "Cannot issue async hypercall while another one in progress!\n");
1042*621191d7SNuno Das Neves 		return -EPERM;
1043*621191d7SNuno Das Neves 	}
1044*621191d7SNuno Das Neves 
1045*621191d7SNuno Das Neves 	reinit_completion(&partition->async_hypercall);
1046*621191d7SNuno Das Neves 	return 0;
1047*621191d7SNuno Das Neves }
1048*621191d7SNuno Das Neves 
1049*621191d7SNuno Das Neves static void mshv_async_hvcall_handler(void *data, u64 *status)
1050*621191d7SNuno Das Neves {
1051*621191d7SNuno Das Neves 	struct mshv_partition *partition = data;
1052*621191d7SNuno Das Neves 
1053*621191d7SNuno Das Neves 	wait_for_completion(&partition->async_hypercall);
1054*621191d7SNuno Das Neves 	pt_dbg(partition, "Async hypercall completed!\n");
1055*621191d7SNuno Das Neves 
1056*621191d7SNuno Das Neves 	*status = partition->async_hypercall_status;
1057*621191d7SNuno Das Neves }
1058*621191d7SNuno Das Neves 
1059*621191d7SNuno Das Neves static int
1060*621191d7SNuno Das Neves mshv_partition_region_share(struct mshv_mem_region *region)
1061*621191d7SNuno Das Neves {
1062*621191d7SNuno Das Neves 	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
1063*621191d7SNuno Das Neves 
1064*621191d7SNuno Das Neves 	if (region->flags.large_pages)
1065*621191d7SNuno Das Neves 		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
1066*621191d7SNuno Das Neves 
1067*621191d7SNuno Das Neves 	return hv_call_modify_spa_host_access(region->partition->pt_id,
1068*621191d7SNuno Das Neves 			region->pages, region->nr_pages,
1069*621191d7SNuno Das Neves 			HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE,
1070*621191d7SNuno Das Neves 			flags, true);
1071*621191d7SNuno Das Neves }
1072*621191d7SNuno Das Neves 
1073*621191d7SNuno Das Neves static int
1074*621191d7SNuno Das Neves mshv_partition_region_unshare(struct mshv_mem_region *region)
1075*621191d7SNuno Das Neves {
1076*621191d7SNuno Das Neves 	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
1077*621191d7SNuno Das Neves 
1078*621191d7SNuno Das Neves 	if (region->flags.large_pages)
1079*621191d7SNuno Das Neves 		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
1080*621191d7SNuno Das Neves 
1081*621191d7SNuno Das Neves 	return hv_call_modify_spa_host_access(region->partition->pt_id,
1082*621191d7SNuno Das Neves 			region->pages, region->nr_pages,
1083*621191d7SNuno Das Neves 			0,
1084*621191d7SNuno Das Neves 			flags, false);
1085*621191d7SNuno Das Neves }
1086*621191d7SNuno Das Neves 
1087*621191d7SNuno Das Neves static int
1088*621191d7SNuno Das Neves mshv_region_remap_pages(struct mshv_mem_region *region, u32 map_flags,
1089*621191d7SNuno Das Neves 			u64 page_offset, u64 page_count)
1090*621191d7SNuno Das Neves {
1091*621191d7SNuno Das Neves 	if (page_offset + page_count > region->nr_pages)
1092*621191d7SNuno Das Neves 		return -EINVAL;
1093*621191d7SNuno Das Neves 
1094*621191d7SNuno Das Neves 	if (region->flags.large_pages)
1095*621191d7SNuno Das Neves 		map_flags |= HV_MAP_GPA_LARGE_PAGE;
1096*621191d7SNuno Das Neves 
1097*621191d7SNuno Das Neves 	/* ask the hypervisor to map guest ram */
1098*621191d7SNuno Das Neves 	return hv_call_map_gpa_pages(region->partition->pt_id,
1099*621191d7SNuno Das Neves 				     region->start_gfn + page_offset,
1100*621191d7SNuno Das Neves 				     page_count, map_flags,
1101*621191d7SNuno Das Neves 				     region->pages + page_offset);
1102*621191d7SNuno Das Neves }
1103*621191d7SNuno Das Neves 
1104*621191d7SNuno Das Neves static int
1105*621191d7SNuno Das Neves mshv_region_map(struct mshv_mem_region *region)
1106*621191d7SNuno Das Neves {
1107*621191d7SNuno Das Neves 	u32 map_flags = region->hv_map_flags;
1108*621191d7SNuno Das Neves 
1109*621191d7SNuno Das Neves 	return mshv_region_remap_pages(region, map_flags,
1110*621191d7SNuno Das Neves 				       0, region->nr_pages);
1111*621191d7SNuno Das Neves }
1112*621191d7SNuno Das Neves 
1113*621191d7SNuno Das Neves static void
1114*621191d7SNuno Das Neves mshv_region_evict_pages(struct mshv_mem_region *region,
1115*621191d7SNuno Das Neves 			u64 page_offset, u64 page_count)
1116*621191d7SNuno Das Neves {
1117*621191d7SNuno Das Neves 	if (region->flags.range_pinned)
1118*621191d7SNuno Das Neves 		unpin_user_pages(region->pages + page_offset, page_count);
1119*621191d7SNuno Das Neves 
1120*621191d7SNuno Das Neves 	memset(region->pages + page_offset, 0,
1121*621191d7SNuno Das Neves 	       page_count * sizeof(struct page *));
1122*621191d7SNuno Das Neves }
1123*621191d7SNuno Das Neves 
1124*621191d7SNuno Das Neves static void
1125*621191d7SNuno Das Neves mshv_region_evict(struct mshv_mem_region *region)
1126*621191d7SNuno Das Neves {
1127*621191d7SNuno Das Neves 	mshv_region_evict_pages(region, 0, region->nr_pages);
1128*621191d7SNuno Das Neves }
1129*621191d7SNuno Das Neves 
1130*621191d7SNuno Das Neves static int
1131*621191d7SNuno Das Neves mshv_region_populate_pages(struct mshv_mem_region *region,
1132*621191d7SNuno Das Neves 			   u64 page_offset, u64 page_count)
1133*621191d7SNuno Das Neves {
1134*621191d7SNuno Das Neves 	u64 done_count, nr_pages;
1135*621191d7SNuno Das Neves 	struct page **pages;
1136*621191d7SNuno Das Neves 	__u64 userspace_addr;
1137*621191d7SNuno Das Neves 	int ret;
1138*621191d7SNuno Das Neves 
1139*621191d7SNuno Das Neves 	if (page_offset + page_count > region->nr_pages)
1140*621191d7SNuno Das Neves 		return -EINVAL;
1141*621191d7SNuno Das Neves 
1142*621191d7SNuno Das Neves 	for (done_count = 0; done_count < page_count; done_count += ret) {
1143*621191d7SNuno Das Neves 		pages = region->pages + page_offset + done_count;
1144*621191d7SNuno Das Neves 		userspace_addr = region->start_uaddr +
1145*621191d7SNuno Das Neves 				(page_offset + done_count) *
1146*621191d7SNuno Das Neves 				HV_HYP_PAGE_SIZE;
1147*621191d7SNuno Das Neves 		nr_pages = min(page_count - done_count,
1148*621191d7SNuno Das Neves 			       MSHV_PIN_PAGES_BATCH_SIZE);
1149*621191d7SNuno Das Neves 
1150*621191d7SNuno Das Neves 		/*
1151*621191d7SNuno Das Neves 		 * Pinning assuming 4k pages works for large pages too.
1152*621191d7SNuno Das Neves 		 * All page structs within the large page are returned.
1153*621191d7SNuno Das Neves 		 *
1154*621191d7SNuno Das Neves 		 * Pin requests are batched because pin_user_pages_fast
1155*621191d7SNuno Das Neves 		 * with the FOLL_LONGTERM flag does a large temporary
1156*621191d7SNuno Das Neves 		 * allocation of contiguous memory.
1157*621191d7SNuno Das Neves 		 */
1158*621191d7SNuno Das Neves 		if (region->flags.range_pinned)
1159*621191d7SNuno Das Neves 			ret = pin_user_pages_fast(userspace_addr,
1160*621191d7SNuno Das Neves 						  nr_pages,
1161*621191d7SNuno Das Neves 						  FOLL_WRITE | FOLL_LONGTERM,
1162*621191d7SNuno Das Neves 						  pages);
1163*621191d7SNuno Das Neves 		else
1164*621191d7SNuno Das Neves 			ret = -EOPNOTSUPP;
1165*621191d7SNuno Das Neves 
1166*621191d7SNuno Das Neves 		if (ret < 0)
1167*621191d7SNuno Das Neves 			goto release_pages;
1168*621191d7SNuno Das Neves 	}
1169*621191d7SNuno Das Neves 
1170*621191d7SNuno Das Neves 	if (PageHuge(region->pages[page_offset]))
1171*621191d7SNuno Das Neves 		region->flags.large_pages = true;
1172*621191d7SNuno Das Neves 
1173*621191d7SNuno Das Neves 	return 0;
1174*621191d7SNuno Das Neves 
1175*621191d7SNuno Das Neves release_pages:
1176*621191d7SNuno Das Neves 	mshv_region_evict_pages(region, page_offset, done_count);
1177*621191d7SNuno Das Neves 	return ret;
1178*621191d7SNuno Das Neves }
1179*621191d7SNuno Das Neves 
1180*621191d7SNuno Das Neves static int
1181*621191d7SNuno Das Neves mshv_region_populate(struct mshv_mem_region *region)
1182*621191d7SNuno Das Neves {
1183*621191d7SNuno Das Neves 	return mshv_region_populate_pages(region, 0, region->nr_pages);
1184*621191d7SNuno Das Neves }
1185*621191d7SNuno Das Neves 
1186*621191d7SNuno Das Neves static struct mshv_mem_region *
1187*621191d7SNuno Das Neves mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
1188*621191d7SNuno Das Neves {
1189*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1190*621191d7SNuno Das Neves 
1191*621191d7SNuno Das Neves 	hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) {
1192*621191d7SNuno Das Neves 		if (gfn >= region->start_gfn &&
1193*621191d7SNuno Das Neves 		    gfn < region->start_gfn + region->nr_pages)
1194*621191d7SNuno Das Neves 			return region;
1195*621191d7SNuno Das Neves 	}
1196*621191d7SNuno Das Neves 
1197*621191d7SNuno Das Neves 	return NULL;
1198*621191d7SNuno Das Neves }
1199*621191d7SNuno Das Neves 
1200*621191d7SNuno Das Neves static struct mshv_mem_region *
1201*621191d7SNuno Das Neves mshv_partition_region_by_uaddr(struct mshv_partition *partition, u64 uaddr)
1202*621191d7SNuno Das Neves {
1203*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1204*621191d7SNuno Das Neves 
1205*621191d7SNuno Das Neves 	hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) {
1206*621191d7SNuno Das Neves 		if (uaddr >= region->start_uaddr &&
1207*621191d7SNuno Das Neves 		    uaddr < region->start_uaddr +
1208*621191d7SNuno Das Neves 			    (region->nr_pages << HV_HYP_PAGE_SHIFT))
1209*621191d7SNuno Das Neves 			return region;
1210*621191d7SNuno Das Neves 	}
1211*621191d7SNuno Das Neves 
1212*621191d7SNuno Das Neves 	return NULL;
1213*621191d7SNuno Das Neves }
1214*621191d7SNuno Das Neves 
1215*621191d7SNuno Das Neves /*
1216*621191d7SNuno Das Neves  * NB: caller checks and makes sure mem->size is page aligned
1217*621191d7SNuno Das Neves  * Returns: 0 with regionpp updated on success, or -errno
1218*621191d7SNuno Das Neves  */
1219*621191d7SNuno Das Neves static int mshv_partition_create_region(struct mshv_partition *partition,
1220*621191d7SNuno Das Neves 					struct mshv_user_mem_region *mem,
1221*621191d7SNuno Das Neves 					struct mshv_mem_region **regionpp,
1222*621191d7SNuno Das Neves 					bool is_mmio)
1223*621191d7SNuno Das Neves {
1224*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1225*621191d7SNuno Das Neves 	u64 nr_pages = HVPFN_DOWN(mem->size);
1226*621191d7SNuno Das Neves 
1227*621191d7SNuno Das Neves 	/* Reject overlapping regions */
1228*621191d7SNuno Das Neves 	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
1229*621191d7SNuno Das Neves 	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1) ||
1230*621191d7SNuno Das Neves 	    mshv_partition_region_by_uaddr(partition, mem->userspace_addr) ||
1231*621191d7SNuno Das Neves 	    mshv_partition_region_by_uaddr(partition, mem->userspace_addr + mem->size - 1))
1232*621191d7SNuno Das Neves 		return -EEXIST;
1233*621191d7SNuno Das Neves 
1234*621191d7SNuno Das Neves 	region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages);
1235*621191d7SNuno Das Neves 	if (!region)
1236*621191d7SNuno Das Neves 		return -ENOMEM;
1237*621191d7SNuno Das Neves 
1238*621191d7SNuno Das Neves 	region->nr_pages = nr_pages;
1239*621191d7SNuno Das Neves 	region->start_gfn = mem->guest_pfn;
1240*621191d7SNuno Das Neves 	region->start_uaddr = mem->userspace_addr;
1241*621191d7SNuno Das Neves 	region->hv_map_flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_ADJUSTABLE;
1242*621191d7SNuno Das Neves 	if (mem->flags & BIT(MSHV_SET_MEM_BIT_WRITABLE))
1243*621191d7SNuno Das Neves 		region->hv_map_flags |= HV_MAP_GPA_WRITABLE;
1244*621191d7SNuno Das Neves 	if (mem->flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
1245*621191d7SNuno Das Neves 		region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
1246*621191d7SNuno Das Neves 
1247*621191d7SNuno Das Neves 	/* Note: large_pages flag populated when we pin the pages */
1248*621191d7SNuno Das Neves 	if (!is_mmio)
1249*621191d7SNuno Das Neves 		region->flags.range_pinned = true;
1250*621191d7SNuno Das Neves 
1251*621191d7SNuno Das Neves 	region->partition = partition;
1252*621191d7SNuno Das Neves 
1253*621191d7SNuno Das Neves 	*regionpp = region;
1254*621191d7SNuno Das Neves 
1255*621191d7SNuno Das Neves 	return 0;
1256*621191d7SNuno Das Neves }
1257*621191d7SNuno Das Neves 
1258*621191d7SNuno Das Neves /*
1259*621191d7SNuno Das Neves  * Map guest ram. if snp, make sure to release that from the host first
1260*621191d7SNuno Das Neves  * Side Effects: In case of failure, pages are unpinned when feasible.
1261*621191d7SNuno Das Neves  */
1262*621191d7SNuno Das Neves static int
1263*621191d7SNuno Das Neves mshv_partition_mem_region_map(struct mshv_mem_region *region)
1264*621191d7SNuno Das Neves {
1265*621191d7SNuno Das Neves 	struct mshv_partition *partition = region->partition;
1266*621191d7SNuno Das Neves 	int ret;
1267*621191d7SNuno Das Neves 
1268*621191d7SNuno Das Neves 	ret = mshv_region_populate(region);
1269*621191d7SNuno Das Neves 	if (ret) {
1270*621191d7SNuno Das Neves 		pt_err(partition, "Failed to populate memory region: %d\n",
1271*621191d7SNuno Das Neves 		       ret);
1272*621191d7SNuno Das Neves 		goto err_out;
1273*621191d7SNuno Das Neves 	}
1274*621191d7SNuno Das Neves 
1275*621191d7SNuno Das Neves 	/*
1276*621191d7SNuno Das Neves 	 * For an SNP partition it is a requirement that for every memory region
1277*621191d7SNuno Das Neves 	 * that we are going to map for this partition we should make sure that
1278*621191d7SNuno Das Neves 	 * host access to that region is released. This is ensured by doing an
1279*621191d7SNuno Das Neves 	 * additional hypercall which will update the SLAT to release host
1280*621191d7SNuno Das Neves 	 * access to guest memory regions.
1281*621191d7SNuno Das Neves 	 */
1282*621191d7SNuno Das Neves 	if (mshv_partition_encrypted(partition)) {
1283*621191d7SNuno Das Neves 		ret = mshv_partition_region_unshare(region);
1284*621191d7SNuno Das Neves 		if (ret) {
1285*621191d7SNuno Das Neves 			pt_err(partition,
1286*621191d7SNuno Das Neves 			       "Failed to unshare memory region (guest_pfn: %llu): %d\n",
1287*621191d7SNuno Das Neves 			       region->start_gfn, ret);
1288*621191d7SNuno Das Neves 			goto evict_region;
1289*621191d7SNuno Das Neves 		}
1290*621191d7SNuno Das Neves 	}
1291*621191d7SNuno Das Neves 
1292*621191d7SNuno Das Neves 	ret = mshv_region_map(region);
1293*621191d7SNuno Das Neves 	if (ret && mshv_partition_encrypted(partition)) {
1294*621191d7SNuno Das Neves 		int shrc;
1295*621191d7SNuno Das Neves 
1296*621191d7SNuno Das Neves 		shrc = mshv_partition_region_share(region);
1297*621191d7SNuno Das Neves 		if (!shrc)
1298*621191d7SNuno Das Neves 			goto evict_region;
1299*621191d7SNuno Das Neves 
1300*621191d7SNuno Das Neves 		pt_err(partition,
1301*621191d7SNuno Das Neves 		       "Failed to share memory region (guest_pfn: %llu): %d\n",
1302*621191d7SNuno Das Neves 		       region->start_gfn, shrc);
1303*621191d7SNuno Das Neves 		/*
1304*621191d7SNuno Das Neves 		 * Don't unpin if marking shared failed because pages are no
1305*621191d7SNuno Das Neves 		 * longer mapped in the host, ie root, anymore.
1306*621191d7SNuno Das Neves 		 */
1307*621191d7SNuno Das Neves 		goto err_out;
1308*621191d7SNuno Das Neves 	}
1309*621191d7SNuno Das Neves 
1310*621191d7SNuno Das Neves 	return 0;
1311*621191d7SNuno Das Neves 
1312*621191d7SNuno Das Neves evict_region:
1313*621191d7SNuno Das Neves 	mshv_region_evict(region);
1314*621191d7SNuno Das Neves err_out:
1315*621191d7SNuno Das Neves 	return ret;
1316*621191d7SNuno Das Neves }
1317*621191d7SNuno Das Neves 
1318*621191d7SNuno Das Neves /*
1319*621191d7SNuno Das Neves  * This maps two things: guest RAM and for pci passthru mmio space.
1320*621191d7SNuno Das Neves  *
1321*621191d7SNuno Das Neves  * mmio:
1322*621191d7SNuno Das Neves  *  - vfio overloads vm_pgoff to store the mmio start pfn/spa.
1323*621191d7SNuno Das Neves  *  - Two things need to happen for mapping mmio range:
1324*621191d7SNuno Das Neves  *	1. mapped in the uaddr so VMM can access it.
1325*621191d7SNuno Das Neves  *	2. mapped in the hwpt (gfn <-> mmio phys addr) so guest can access it.
1326*621191d7SNuno Das Neves  *
1327*621191d7SNuno Das Neves  *   This function takes care of the second. The first one is managed by vfio,
1328*621191d7SNuno Das Neves  *   and hence is taken care of via vfio_pci_mmap_fault().
1329*621191d7SNuno Das Neves  */
1330*621191d7SNuno Das Neves static long
1331*621191d7SNuno Das Neves mshv_map_user_memory(struct mshv_partition *partition,
1332*621191d7SNuno Das Neves 		     struct mshv_user_mem_region mem)
1333*621191d7SNuno Das Neves {
1334*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1335*621191d7SNuno Das Neves 	struct vm_area_struct *vma;
1336*621191d7SNuno Das Neves 	bool is_mmio;
1337*621191d7SNuno Das Neves 	ulong mmio_pfn;
1338*621191d7SNuno Das Neves 	long ret;
1339*621191d7SNuno Das Neves 
1340*621191d7SNuno Das Neves 	if (mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP) ||
1341*621191d7SNuno Das Neves 	    !access_ok((const void *)mem.userspace_addr, mem.size))
1342*621191d7SNuno Das Neves 		return -EINVAL;
1343*621191d7SNuno Das Neves 
1344*621191d7SNuno Das Neves 	mmap_read_lock(current->mm);
1345*621191d7SNuno Das Neves 	vma = vma_lookup(current->mm, mem.userspace_addr);
1346*621191d7SNuno Das Neves 	is_mmio = vma ? !!(vma->vm_flags & (VM_IO | VM_PFNMAP)) : 0;
1347*621191d7SNuno Das Neves 	mmio_pfn = is_mmio ? vma->vm_pgoff : 0;
1348*621191d7SNuno Das Neves 	mmap_read_unlock(current->mm);
1349*621191d7SNuno Das Neves 
1350*621191d7SNuno Das Neves 	if (!vma)
1351*621191d7SNuno Das Neves 		return -EINVAL;
1352*621191d7SNuno Das Neves 
1353*621191d7SNuno Das Neves 	ret = mshv_partition_create_region(partition, &mem, &region,
1354*621191d7SNuno Das Neves 					   is_mmio);
1355*621191d7SNuno Das Neves 	if (ret)
1356*621191d7SNuno Das Neves 		return ret;
1357*621191d7SNuno Das Neves 
1358*621191d7SNuno Das Neves 	if (is_mmio)
1359*621191d7SNuno Das Neves 		ret = hv_call_map_mmio_pages(partition->pt_id, mem.guest_pfn,
1360*621191d7SNuno Das Neves 					     mmio_pfn, HVPFN_DOWN(mem.size));
1361*621191d7SNuno Das Neves 	else
1362*621191d7SNuno Das Neves 		ret = mshv_partition_mem_region_map(region);
1363*621191d7SNuno Das Neves 
1364*621191d7SNuno Das Neves 	if (ret)
1365*621191d7SNuno Das Neves 		goto errout;
1366*621191d7SNuno Das Neves 
1367*621191d7SNuno Das Neves 	/* Install the new region */
1368*621191d7SNuno Das Neves 	hlist_add_head(&region->hnode, &partition->pt_mem_regions);
1369*621191d7SNuno Das Neves 
1370*621191d7SNuno Das Neves 	return 0;
1371*621191d7SNuno Das Neves 
1372*621191d7SNuno Das Neves errout:
1373*621191d7SNuno Das Neves 	vfree(region);
1374*621191d7SNuno Das Neves 	return ret;
1375*621191d7SNuno Das Neves }
1376*621191d7SNuno Das Neves 
1377*621191d7SNuno Das Neves /* Called for unmapping both the guest ram and the mmio space */
1378*621191d7SNuno Das Neves static long
1379*621191d7SNuno Das Neves mshv_unmap_user_memory(struct mshv_partition *partition,
1380*621191d7SNuno Das Neves 		       struct mshv_user_mem_region mem)
1381*621191d7SNuno Das Neves {
1382*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1383*621191d7SNuno Das Neves 	u32 unmap_flags = 0;
1384*621191d7SNuno Das Neves 
1385*621191d7SNuno Das Neves 	if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
1386*621191d7SNuno Das Neves 		return -EINVAL;
1387*621191d7SNuno Das Neves 
1388*621191d7SNuno Das Neves 	region = mshv_partition_region_by_gfn(partition, mem.guest_pfn);
1389*621191d7SNuno Das Neves 	if (!region)
1390*621191d7SNuno Das Neves 		return -EINVAL;
1391*621191d7SNuno Das Neves 
1392*621191d7SNuno Das Neves 	/* Paranoia check */
1393*621191d7SNuno Das Neves 	if (region->start_uaddr != mem.userspace_addr ||
1394*621191d7SNuno Das Neves 	    region->start_gfn != mem.guest_pfn ||
1395*621191d7SNuno Das Neves 	    region->nr_pages != HVPFN_DOWN(mem.size))
1396*621191d7SNuno Das Neves 		return -EINVAL;
1397*621191d7SNuno Das Neves 
1398*621191d7SNuno Das Neves 	hlist_del(&region->hnode);
1399*621191d7SNuno Das Neves 
1400*621191d7SNuno Das Neves 	if (region->flags.large_pages)
1401*621191d7SNuno Das Neves 		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
1402*621191d7SNuno Das Neves 
1403*621191d7SNuno Das Neves 	/* ignore unmap failures and continue as process may be exiting */
1404*621191d7SNuno Das Neves 	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
1405*621191d7SNuno Das Neves 				region->nr_pages, unmap_flags);
1406*621191d7SNuno Das Neves 
1407*621191d7SNuno Das Neves 	mshv_region_evict(region);
1408*621191d7SNuno Das Neves 
1409*621191d7SNuno Das Neves 	vfree(region);
1410*621191d7SNuno Das Neves 	return 0;
1411*621191d7SNuno Das Neves }
1412*621191d7SNuno Das Neves 
1413*621191d7SNuno Das Neves static long
1414*621191d7SNuno Das Neves mshv_partition_ioctl_set_memory(struct mshv_partition *partition,
1415*621191d7SNuno Das Neves 				struct mshv_user_mem_region __user *user_mem)
1416*621191d7SNuno Das Neves {
1417*621191d7SNuno Das Neves 	struct mshv_user_mem_region mem;
1418*621191d7SNuno Das Neves 
1419*621191d7SNuno Das Neves 	if (copy_from_user(&mem, user_mem, sizeof(mem)))
1420*621191d7SNuno Das Neves 		return -EFAULT;
1421*621191d7SNuno Das Neves 
1422*621191d7SNuno Das Neves 	if (!mem.size ||
1423*621191d7SNuno Das Neves 	    !PAGE_ALIGNED(mem.size) ||
1424*621191d7SNuno Das Neves 	    !PAGE_ALIGNED(mem.userspace_addr) ||
1425*621191d7SNuno Das Neves 	    (mem.flags & ~MSHV_SET_MEM_FLAGS_MASK) ||
1426*621191d7SNuno Das Neves 	    mshv_field_nonzero(mem, rsvd))
1427*621191d7SNuno Das Neves 		return -EINVAL;
1428*621191d7SNuno Das Neves 
1429*621191d7SNuno Das Neves 	if (mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP))
1430*621191d7SNuno Das Neves 		return mshv_unmap_user_memory(partition, mem);
1431*621191d7SNuno Das Neves 
1432*621191d7SNuno Das Neves 	return mshv_map_user_memory(partition, mem);
1433*621191d7SNuno Das Neves }
1434*621191d7SNuno Das Neves 
1435*621191d7SNuno Das Neves static long
1436*621191d7SNuno Das Neves mshv_partition_ioctl_ioeventfd(struct mshv_partition *partition,
1437*621191d7SNuno Das Neves 			       void __user *user_args)
1438*621191d7SNuno Das Neves {
1439*621191d7SNuno Das Neves 	struct mshv_user_ioeventfd args;
1440*621191d7SNuno Das Neves 
1441*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
1442*621191d7SNuno Das Neves 		return -EFAULT;
1443*621191d7SNuno Das Neves 
1444*621191d7SNuno Das Neves 	return mshv_set_unset_ioeventfd(partition, &args);
1445*621191d7SNuno Das Neves }
1446*621191d7SNuno Das Neves 
1447*621191d7SNuno Das Neves static long
1448*621191d7SNuno Das Neves mshv_partition_ioctl_irqfd(struct mshv_partition *partition,
1449*621191d7SNuno Das Neves 			   void __user *user_args)
1450*621191d7SNuno Das Neves {
1451*621191d7SNuno Das Neves 	struct mshv_user_irqfd args;
1452*621191d7SNuno Das Neves 
1453*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
1454*621191d7SNuno Das Neves 		return -EFAULT;
1455*621191d7SNuno Das Neves 
1456*621191d7SNuno Das Neves 	return mshv_set_unset_irqfd(partition, &args);
1457*621191d7SNuno Das Neves }
1458*621191d7SNuno Das Neves 
1459*621191d7SNuno Das Neves static long
1460*621191d7SNuno Das Neves mshv_partition_ioctl_get_gpap_access_bitmap(struct mshv_partition *partition,
1461*621191d7SNuno Das Neves 					    void __user *user_args)
1462*621191d7SNuno Das Neves {
1463*621191d7SNuno Das Neves 	struct mshv_gpap_access_bitmap args;
1464*621191d7SNuno Das Neves 	union hv_gpa_page_access_state *states;
1465*621191d7SNuno Das Neves 	long ret, i;
1466*621191d7SNuno Das Neves 	union hv_gpa_page_access_state_flags hv_flags = {};
1467*621191d7SNuno Das Neves 	u8 hv_type_mask;
1468*621191d7SNuno Das Neves 	ulong bitmap_buf_sz, states_buf_sz;
1469*621191d7SNuno Das Neves 	int written = 0;
1470*621191d7SNuno Das Neves 
1471*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
1472*621191d7SNuno Das Neves 		return -EFAULT;
1473*621191d7SNuno Das Neves 
1474*621191d7SNuno Das Neves 	if (args.access_type >= MSHV_GPAP_ACCESS_TYPE_COUNT ||
1475*621191d7SNuno Das Neves 	    args.access_op >= MSHV_GPAP_ACCESS_OP_COUNT ||
1476*621191d7SNuno Das Neves 	    mshv_field_nonzero(args, rsvd) || !args.page_count ||
1477*621191d7SNuno Das Neves 	    !args.bitmap_ptr)
1478*621191d7SNuno Das Neves 		return -EINVAL;
1479*621191d7SNuno Das Neves 
1480*621191d7SNuno Das Neves 	if (check_mul_overflow(args.page_count, sizeof(*states), &states_buf_sz))
1481*621191d7SNuno Das Neves 		return -E2BIG;
1482*621191d7SNuno Das Neves 
1483*621191d7SNuno Das Neves 	/* Num bytes needed to store bitmap; one bit per page rounded up */
1484*621191d7SNuno Das Neves 	bitmap_buf_sz = DIV_ROUND_UP(args.page_count, 8);
1485*621191d7SNuno Das Neves 
1486*621191d7SNuno Das Neves 	/* Sanity check */
1487*621191d7SNuno Das Neves 	if (bitmap_buf_sz > states_buf_sz)
1488*621191d7SNuno Das Neves 		return -EBADFD;
1489*621191d7SNuno Das Neves 
1490*621191d7SNuno Das Neves 	switch (args.access_type) {
1491*621191d7SNuno Das Neves 	case MSHV_GPAP_ACCESS_TYPE_ACCESSED:
1492*621191d7SNuno Das Neves 		hv_type_mask = 1;
1493*621191d7SNuno Das Neves 		if (args.access_op == MSHV_GPAP_ACCESS_OP_CLEAR) {
1494*621191d7SNuno Das Neves 			hv_flags.clear_accessed = 1;
1495*621191d7SNuno Das Neves 			/* not accessed implies not dirty */
1496*621191d7SNuno Das Neves 			hv_flags.clear_dirty = 1;
1497*621191d7SNuno Das Neves 		} else { /* MSHV_GPAP_ACCESS_OP_SET */
1498*621191d7SNuno Das Neves 			hv_flags.set_accessed = 1;
1499*621191d7SNuno Das Neves 		}
1500*621191d7SNuno Das Neves 		break;
1501*621191d7SNuno Das Neves 	case MSHV_GPAP_ACCESS_TYPE_DIRTY:
1502*621191d7SNuno Das Neves 		hv_type_mask = 2;
1503*621191d7SNuno Das Neves 		if (args.access_op == MSHV_GPAP_ACCESS_OP_CLEAR) {
1504*621191d7SNuno Das Neves 			hv_flags.clear_dirty = 1;
1505*621191d7SNuno Das Neves 		} else { /* MSHV_GPAP_ACCESS_OP_SET */
1506*621191d7SNuno Das Neves 			hv_flags.set_dirty = 1;
1507*621191d7SNuno Das Neves 			/* dirty implies accessed */
1508*621191d7SNuno Das Neves 			hv_flags.set_accessed = 1;
1509*621191d7SNuno Das Neves 		}
1510*621191d7SNuno Das Neves 		break;
1511*621191d7SNuno Das Neves 	}
1512*621191d7SNuno Das Neves 
1513*621191d7SNuno Das Neves 	states = vzalloc(states_buf_sz);
1514*621191d7SNuno Das Neves 	if (!states)
1515*621191d7SNuno Das Neves 		return -ENOMEM;
1516*621191d7SNuno Das Neves 
1517*621191d7SNuno Das Neves 	ret = hv_call_get_gpa_access_states(partition->pt_id, args.page_count,
1518*621191d7SNuno Das Neves 					    args.gpap_base, hv_flags, &written,
1519*621191d7SNuno Das Neves 					    states);
1520*621191d7SNuno Das Neves 	if (ret)
1521*621191d7SNuno Das Neves 		goto free_return;
1522*621191d7SNuno Das Neves 
1523*621191d7SNuno Das Neves 	/*
1524*621191d7SNuno Das Neves 	 * Overwrite states buffer with bitmap - the bits in hv_type_mask
1525*621191d7SNuno Das Neves 	 * correspond to bitfields in hv_gpa_page_access_state
1526*621191d7SNuno Das Neves 	 */
1527*621191d7SNuno Das Neves 	for (i = 0; i < written; ++i)
1528*621191d7SNuno Das Neves 		__assign_bit(i, (ulong *)states,
1529*621191d7SNuno Das Neves 			     states[i].as_uint8 & hv_type_mask);
1530*621191d7SNuno Das Neves 
1531*621191d7SNuno Das Neves 	/* zero the unused bits in the last byte(s) of the returned bitmap */
1532*621191d7SNuno Das Neves 	for (i = written; i < bitmap_buf_sz * 8; ++i)
1533*621191d7SNuno Das Neves 		__clear_bit(i, (ulong *)states);
1534*621191d7SNuno Das Neves 
1535*621191d7SNuno Das Neves 	if (copy_to_user((void __user *)args.bitmap_ptr, states, bitmap_buf_sz))
1536*621191d7SNuno Das Neves 		ret = -EFAULT;
1537*621191d7SNuno Das Neves 
1538*621191d7SNuno Das Neves free_return:
1539*621191d7SNuno Das Neves 	vfree(states);
1540*621191d7SNuno Das Neves 	return ret;
1541*621191d7SNuno Das Neves }
1542*621191d7SNuno Das Neves 
1543*621191d7SNuno Das Neves static long
1544*621191d7SNuno Das Neves mshv_partition_ioctl_set_msi_routing(struct mshv_partition *partition,
1545*621191d7SNuno Das Neves 				     void __user *user_args)
1546*621191d7SNuno Das Neves {
1547*621191d7SNuno Das Neves 	struct mshv_user_irq_entry *entries = NULL;
1548*621191d7SNuno Das Neves 	struct mshv_user_irq_table args;
1549*621191d7SNuno Das Neves 	long ret;
1550*621191d7SNuno Das Neves 
1551*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_args, sizeof(args)))
1552*621191d7SNuno Das Neves 		return -EFAULT;
1553*621191d7SNuno Das Neves 
1554*621191d7SNuno Das Neves 	if (args.nr > MSHV_MAX_GUEST_IRQS ||
1555*621191d7SNuno Das Neves 	    mshv_field_nonzero(args, rsvd))
1556*621191d7SNuno Das Neves 		return -EINVAL;
1557*621191d7SNuno Das Neves 
1558*621191d7SNuno Das Neves 	if (args.nr) {
1559*621191d7SNuno Das Neves 		struct mshv_user_irq_table __user *urouting = user_args;
1560*621191d7SNuno Das Neves 
1561*621191d7SNuno Das Neves 		entries = vmemdup_user(urouting->entries,
1562*621191d7SNuno Das Neves 				       array_size(sizeof(*entries),
1563*621191d7SNuno Das Neves 						  args.nr));
1564*621191d7SNuno Das Neves 		if (IS_ERR(entries))
1565*621191d7SNuno Das Neves 			return PTR_ERR(entries);
1566*621191d7SNuno Das Neves 	}
1567*621191d7SNuno Das Neves 	ret = mshv_update_routing_table(partition, entries, args.nr);
1568*621191d7SNuno Das Neves 	kvfree(entries);
1569*621191d7SNuno Das Neves 
1570*621191d7SNuno Das Neves 	return ret;
1571*621191d7SNuno Das Neves }
1572*621191d7SNuno Das Neves 
1573*621191d7SNuno Das Neves static long
1574*621191d7SNuno Das Neves mshv_partition_ioctl_initialize(struct mshv_partition *partition)
1575*621191d7SNuno Das Neves {
1576*621191d7SNuno Das Neves 	long ret;
1577*621191d7SNuno Das Neves 
1578*621191d7SNuno Das Neves 	if (partition->pt_initialized)
1579*621191d7SNuno Das Neves 		return 0;
1580*621191d7SNuno Das Neves 
1581*621191d7SNuno Das Neves 	ret = hv_call_initialize_partition(partition->pt_id);
1582*621191d7SNuno Das Neves 	if (ret)
1583*621191d7SNuno Das Neves 		goto withdraw_mem;
1584*621191d7SNuno Das Neves 
1585*621191d7SNuno Das Neves 	partition->pt_initialized = true;
1586*621191d7SNuno Das Neves 
1587*621191d7SNuno Das Neves 	return 0;
1588*621191d7SNuno Das Neves 
1589*621191d7SNuno Das Neves withdraw_mem:
1590*621191d7SNuno Das Neves 	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
1591*621191d7SNuno Das Neves 
1592*621191d7SNuno Das Neves 	return ret;
1593*621191d7SNuno Das Neves }
1594*621191d7SNuno Das Neves 
1595*621191d7SNuno Das Neves static long
1596*621191d7SNuno Das Neves mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
1597*621191d7SNuno Das Neves {
1598*621191d7SNuno Das Neves 	struct mshv_partition *partition = filp->private_data;
1599*621191d7SNuno Das Neves 	long ret;
1600*621191d7SNuno Das Neves 	void __user *uarg = (void __user *)arg;
1601*621191d7SNuno Das Neves 
1602*621191d7SNuno Das Neves 	if (mutex_lock_killable(&partition->pt_mutex))
1603*621191d7SNuno Das Neves 		return -EINTR;
1604*621191d7SNuno Das Neves 
1605*621191d7SNuno Das Neves 	switch (ioctl) {
1606*621191d7SNuno Das Neves 	case MSHV_INITIALIZE_PARTITION:
1607*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_initialize(partition);
1608*621191d7SNuno Das Neves 		break;
1609*621191d7SNuno Das Neves 	case MSHV_SET_GUEST_MEMORY:
1610*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_set_memory(partition, uarg);
1611*621191d7SNuno Das Neves 		break;
1612*621191d7SNuno Das Neves 	case MSHV_CREATE_VP:
1613*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_create_vp(partition, uarg);
1614*621191d7SNuno Das Neves 		break;
1615*621191d7SNuno Das Neves 	case MSHV_IRQFD:
1616*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_irqfd(partition, uarg);
1617*621191d7SNuno Das Neves 		break;
1618*621191d7SNuno Das Neves 	case MSHV_IOEVENTFD:
1619*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_ioeventfd(partition, uarg);
1620*621191d7SNuno Das Neves 		break;
1621*621191d7SNuno Das Neves 	case MSHV_SET_MSI_ROUTING:
1622*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_set_msi_routing(partition, uarg);
1623*621191d7SNuno Das Neves 		break;
1624*621191d7SNuno Das Neves 	case MSHV_GET_GPAP_ACCESS_BITMAP:
1625*621191d7SNuno Das Neves 		ret = mshv_partition_ioctl_get_gpap_access_bitmap(partition,
1626*621191d7SNuno Das Neves 								  uarg);
1627*621191d7SNuno Das Neves 		break;
1628*621191d7SNuno Das Neves 	case MSHV_ROOT_HVCALL:
1629*621191d7SNuno Das Neves 		ret = mshv_ioctl_passthru_hvcall(partition, true, uarg);
1630*621191d7SNuno Das Neves 		break;
1631*621191d7SNuno Das Neves 	default:
1632*621191d7SNuno Das Neves 		ret = -ENOTTY;
1633*621191d7SNuno Das Neves 	}
1634*621191d7SNuno Das Neves 
1635*621191d7SNuno Das Neves 	mutex_unlock(&partition->pt_mutex);
1636*621191d7SNuno Das Neves 	return ret;
1637*621191d7SNuno Das Neves }
1638*621191d7SNuno Das Neves 
1639*621191d7SNuno Das Neves static int
1640*621191d7SNuno Das Neves disable_vp_dispatch(struct mshv_vp *vp)
1641*621191d7SNuno Das Neves {
1642*621191d7SNuno Das Neves 	int ret;
1643*621191d7SNuno Das Neves 	struct hv_register_assoc dispatch_suspend = {
1644*621191d7SNuno Das Neves 		.name = HV_REGISTER_DISPATCH_SUSPEND,
1645*621191d7SNuno Das Neves 		.value.dispatch_suspend.suspended = 1,
1646*621191d7SNuno Das Neves 	};
1647*621191d7SNuno Das Neves 
1648*621191d7SNuno Das Neves 	ret = mshv_set_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
1649*621191d7SNuno Das Neves 				    1, &dispatch_suspend);
1650*621191d7SNuno Das Neves 	if (ret)
1651*621191d7SNuno Das Neves 		vp_err(vp, "failed to suspend\n");
1652*621191d7SNuno Das Neves 
1653*621191d7SNuno Das Neves 	return ret;
1654*621191d7SNuno Das Neves }
1655*621191d7SNuno Das Neves 
1656*621191d7SNuno Das Neves static int
1657*621191d7SNuno Das Neves get_vp_signaled_count(struct mshv_vp *vp, u64 *count)
1658*621191d7SNuno Das Neves {
1659*621191d7SNuno Das Neves 	int ret;
1660*621191d7SNuno Das Neves 	struct hv_register_assoc root_signal_count = {
1661*621191d7SNuno Das Neves 		.name = HV_REGISTER_VP_ROOT_SIGNAL_COUNT,
1662*621191d7SNuno Das Neves 	};
1663*621191d7SNuno Das Neves 
1664*621191d7SNuno Das Neves 	ret = mshv_get_vp_registers(vp->vp_index, vp->vp_partition->pt_id,
1665*621191d7SNuno Das Neves 				    1, &root_signal_count);
1666*621191d7SNuno Das Neves 
1667*621191d7SNuno Das Neves 	if (ret) {
1668*621191d7SNuno Das Neves 		vp_err(vp, "Failed to get root signal count");
1669*621191d7SNuno Das Neves 		*count = 0;
1670*621191d7SNuno Das Neves 		return ret;
1671*621191d7SNuno Das Neves 	}
1672*621191d7SNuno Das Neves 
1673*621191d7SNuno Das Neves 	*count = root_signal_count.value.reg64;
1674*621191d7SNuno Das Neves 
1675*621191d7SNuno Das Neves 	return ret;
1676*621191d7SNuno Das Neves }
1677*621191d7SNuno Das Neves 
1678*621191d7SNuno Das Neves static void
1679*621191d7SNuno Das Neves drain_vp_signals(struct mshv_vp *vp)
1680*621191d7SNuno Das Neves {
1681*621191d7SNuno Das Neves 	u64 hv_signal_count;
1682*621191d7SNuno Das Neves 	u64 vp_signal_count;
1683*621191d7SNuno Das Neves 
1684*621191d7SNuno Das Neves 	get_vp_signaled_count(vp, &hv_signal_count);
1685*621191d7SNuno Das Neves 
1686*621191d7SNuno Das Neves 	vp_signal_count = atomic64_read(&vp->run.vp_signaled_count);
1687*621191d7SNuno Das Neves 
1688*621191d7SNuno Das Neves 	/*
1689*621191d7SNuno Das Neves 	 * There should be at most 1 outstanding notification, but be extra
1690*621191d7SNuno Das Neves 	 * careful anyway.
1691*621191d7SNuno Das Neves 	 */
1692*621191d7SNuno Das Neves 	while (hv_signal_count != vp_signal_count) {
1693*621191d7SNuno Das Neves 		WARN_ON(hv_signal_count - vp_signal_count != 1);
1694*621191d7SNuno Das Neves 
1695*621191d7SNuno Das Neves 		if (wait_event_interruptible(vp->run.vp_suspend_queue,
1696*621191d7SNuno Das Neves 					     vp->run.kicked_by_hv == 1))
1697*621191d7SNuno Das Neves 			break;
1698*621191d7SNuno Das Neves 		vp->run.kicked_by_hv = 0;
1699*621191d7SNuno Das Neves 		vp_signal_count = atomic64_read(&vp->run.vp_signaled_count);
1700*621191d7SNuno Das Neves 	}
1701*621191d7SNuno Das Neves }
1702*621191d7SNuno Das Neves 
1703*621191d7SNuno Das Neves static void drain_all_vps(const struct mshv_partition *partition)
1704*621191d7SNuno Das Neves {
1705*621191d7SNuno Das Neves 	int i;
1706*621191d7SNuno Das Neves 	struct mshv_vp *vp;
1707*621191d7SNuno Das Neves 
1708*621191d7SNuno Das Neves 	/*
1709*621191d7SNuno Das Neves 	 * VPs are reachable from ISR. It is safe to not take the partition
1710*621191d7SNuno Das Neves 	 * lock because nobody else can enter this function and drop the
1711*621191d7SNuno Das Neves 	 * partition from the list.
1712*621191d7SNuno Das Neves 	 */
1713*621191d7SNuno Das Neves 	for (i = 0; i < MSHV_MAX_VPS; i++) {
1714*621191d7SNuno Das Neves 		vp = partition->pt_vp_array[i];
1715*621191d7SNuno Das Neves 		if (!vp)
1716*621191d7SNuno Das Neves 			continue;
1717*621191d7SNuno Das Neves 		/*
1718*621191d7SNuno Das Neves 		 * Disable dispatching of the VP in the hypervisor. After this
1719*621191d7SNuno Das Neves 		 * the hypervisor guarantees it won't generate any signals for
1720*621191d7SNuno Das Neves 		 * the VP and the hypervisor's VP signal count won't change.
1721*621191d7SNuno Das Neves 		 */
1722*621191d7SNuno Das Neves 		disable_vp_dispatch(vp);
1723*621191d7SNuno Das Neves 		drain_vp_signals(vp);
1724*621191d7SNuno Das Neves 	}
1725*621191d7SNuno Das Neves }
1726*621191d7SNuno Das Neves 
1727*621191d7SNuno Das Neves static void
1728*621191d7SNuno Das Neves remove_partition(struct mshv_partition *partition)
1729*621191d7SNuno Das Neves {
1730*621191d7SNuno Das Neves 	spin_lock(&mshv_root.pt_ht_lock);
1731*621191d7SNuno Das Neves 	hlist_del_rcu(&partition->pt_hnode);
1732*621191d7SNuno Das Neves 	spin_unlock(&mshv_root.pt_ht_lock);
1733*621191d7SNuno Das Neves 
1734*621191d7SNuno Das Neves 	synchronize_rcu();
1735*621191d7SNuno Das Neves }
1736*621191d7SNuno Das Neves 
1737*621191d7SNuno Das Neves /*
1738*621191d7SNuno Das Neves  * Tear down a partition and remove it from the list.
1739*621191d7SNuno Das Neves  * Partition's refcount must be 0
1740*621191d7SNuno Das Neves  */
1741*621191d7SNuno Das Neves static void destroy_partition(struct mshv_partition *partition)
1742*621191d7SNuno Das Neves {
1743*621191d7SNuno Das Neves 	struct mshv_vp *vp;
1744*621191d7SNuno Das Neves 	struct mshv_mem_region *region;
1745*621191d7SNuno Das Neves 	int i, ret;
1746*621191d7SNuno Das Neves 	struct hlist_node *n;
1747*621191d7SNuno Das Neves 
1748*621191d7SNuno Das Neves 	if (refcount_read(&partition->pt_ref_count)) {
1749*621191d7SNuno Das Neves 		pt_err(partition,
1750*621191d7SNuno Das Neves 		       "Attempt to destroy partition but refcount > 0\n");
1751*621191d7SNuno Das Neves 		return;
1752*621191d7SNuno Das Neves 	}
1753*621191d7SNuno Das Neves 
1754*621191d7SNuno Das Neves 	if (partition->pt_initialized) {
1755*621191d7SNuno Das Neves 		/*
1756*621191d7SNuno Das Neves 		 * We only need to drain signals for root scheduler. This should be
1757*621191d7SNuno Das Neves 		 * done before removing the partition from the partition list.
1758*621191d7SNuno Das Neves 		 */
1759*621191d7SNuno Das Neves 		if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
1760*621191d7SNuno Das Neves 			drain_all_vps(partition);
1761*621191d7SNuno Das Neves 
1762*621191d7SNuno Das Neves 		/* Remove vps */
1763*621191d7SNuno Das Neves 		for (i = 0; i < MSHV_MAX_VPS; ++i) {
1764*621191d7SNuno Das Neves 			vp = partition->pt_vp_array[i];
1765*621191d7SNuno Das Neves 			if (!vp)
1766*621191d7SNuno Das Neves 				continue;
1767*621191d7SNuno Das Neves 
1768*621191d7SNuno Das Neves 			if (hv_parent_partition())
1769*621191d7SNuno Das Neves 				mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
1770*621191d7SNuno Das Neves 
1771*621191d7SNuno Das Neves 			if (vp->vp_register_page) {
1772*621191d7SNuno Das Neves 				(void)hv_call_unmap_vp_state_page(partition->pt_id,
1773*621191d7SNuno Das Neves 								  vp->vp_index,
1774*621191d7SNuno Das Neves 								  HV_VP_STATE_PAGE_REGISTERS,
1775*621191d7SNuno Das Neves 								  input_vtl_zero);
1776*621191d7SNuno Das Neves 				vp->vp_register_page = NULL;
1777*621191d7SNuno Das Neves 			}
1778*621191d7SNuno Das Neves 
1779*621191d7SNuno Das Neves 			(void)hv_call_unmap_vp_state_page(partition->pt_id,
1780*621191d7SNuno Das Neves 							  vp->vp_index,
1781*621191d7SNuno Das Neves 							  HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
1782*621191d7SNuno Das Neves 							  input_vtl_zero);
1783*621191d7SNuno Das Neves 			vp->vp_intercept_msg_page = NULL;
1784*621191d7SNuno Das Neves 
1785*621191d7SNuno Das Neves 			if (vp->vp_ghcb_page) {
1786*621191d7SNuno Das Neves 				(void)hv_call_unmap_vp_state_page(partition->pt_id,
1787*621191d7SNuno Das Neves 								  vp->vp_index,
1788*621191d7SNuno Das Neves 								  HV_VP_STATE_PAGE_GHCB,
1789*621191d7SNuno Das Neves 								  input_vtl_normal);
1790*621191d7SNuno Das Neves 				vp->vp_ghcb_page = NULL;
1791*621191d7SNuno Das Neves 			}
1792*621191d7SNuno Das Neves 
1793*621191d7SNuno Das Neves 			kfree(vp);
1794*621191d7SNuno Das Neves 
1795*621191d7SNuno Das Neves 			partition->pt_vp_array[i] = NULL;
1796*621191d7SNuno Das Neves 		}
1797*621191d7SNuno Das Neves 
1798*621191d7SNuno Das Neves 		/* Deallocates and unmaps everything including vcpus, GPA mappings etc */
1799*621191d7SNuno Das Neves 		hv_call_finalize_partition(partition->pt_id);
1800*621191d7SNuno Das Neves 
1801*621191d7SNuno Das Neves 		partition->pt_initialized = false;
1802*621191d7SNuno Das Neves 	}
1803*621191d7SNuno Das Neves 
1804*621191d7SNuno Das Neves 	remove_partition(partition);
1805*621191d7SNuno Das Neves 
1806*621191d7SNuno Das Neves 	/* Remove regions, regain access to the memory and unpin the pages */
1807*621191d7SNuno Das Neves 	hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
1808*621191d7SNuno Das Neves 				  hnode) {
1809*621191d7SNuno Das Neves 		hlist_del(&region->hnode);
1810*621191d7SNuno Das Neves 
1811*621191d7SNuno Das Neves 		if (mshv_partition_encrypted(partition)) {
1812*621191d7SNuno Das Neves 			ret = mshv_partition_region_share(region);
1813*621191d7SNuno Das Neves 			if (ret) {
1814*621191d7SNuno Das Neves 				pt_err(partition,
1815*621191d7SNuno Das Neves 				       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
1816*621191d7SNuno Das Neves 				      ret);
1817*621191d7SNuno Das Neves 				return;
1818*621191d7SNuno Das Neves 			}
1819*621191d7SNuno Das Neves 		}
1820*621191d7SNuno Das Neves 
1821*621191d7SNuno Das Neves 		mshv_region_evict(region);
1822*621191d7SNuno Das Neves 
1823*621191d7SNuno Das Neves 		vfree(region);
1824*621191d7SNuno Das Neves 	}
1825*621191d7SNuno Das Neves 
1826*621191d7SNuno Das Neves 	/* Withdraw and free all pages we deposited */
1827*621191d7SNuno Das Neves 	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
1828*621191d7SNuno Das Neves 	hv_call_delete_partition(partition->pt_id);
1829*621191d7SNuno Das Neves 
1830*621191d7SNuno Das Neves 	mshv_free_routing_table(partition);
1831*621191d7SNuno Das Neves 	kfree(partition);
1832*621191d7SNuno Das Neves }
1833*621191d7SNuno Das Neves 
1834*621191d7SNuno Das Neves struct
1835*621191d7SNuno Das Neves mshv_partition *mshv_partition_get(struct mshv_partition *partition)
1836*621191d7SNuno Das Neves {
1837*621191d7SNuno Das Neves 	if (refcount_inc_not_zero(&partition->pt_ref_count))
1838*621191d7SNuno Das Neves 		return partition;
1839*621191d7SNuno Das Neves 	return NULL;
1840*621191d7SNuno Das Neves }
1841*621191d7SNuno Das Neves 
1842*621191d7SNuno Das Neves struct
1843*621191d7SNuno Das Neves mshv_partition *mshv_partition_find(u64 partition_id)
1844*621191d7SNuno Das Neves 	__must_hold(RCU)
1845*621191d7SNuno Das Neves {
1846*621191d7SNuno Das Neves 	struct mshv_partition *p;
1847*621191d7SNuno Das Neves 
1848*621191d7SNuno Das Neves 	hash_for_each_possible_rcu(mshv_root.pt_htable, p, pt_hnode,
1849*621191d7SNuno Das Neves 				   partition_id)
1850*621191d7SNuno Das Neves 		if (p->pt_id == partition_id)
1851*621191d7SNuno Das Neves 			return p;
1852*621191d7SNuno Das Neves 
1853*621191d7SNuno Das Neves 	return NULL;
1854*621191d7SNuno Das Neves }
1855*621191d7SNuno Das Neves 
1856*621191d7SNuno Das Neves void
1857*621191d7SNuno Das Neves mshv_partition_put(struct mshv_partition *partition)
1858*621191d7SNuno Das Neves {
1859*621191d7SNuno Das Neves 	if (refcount_dec_and_test(&partition->pt_ref_count))
1860*621191d7SNuno Das Neves 		destroy_partition(partition);
1861*621191d7SNuno Das Neves }
1862*621191d7SNuno Das Neves 
1863*621191d7SNuno Das Neves static int
1864*621191d7SNuno Das Neves mshv_partition_release(struct inode *inode, struct file *filp)
1865*621191d7SNuno Das Neves {
1866*621191d7SNuno Das Neves 	struct mshv_partition *partition = filp->private_data;
1867*621191d7SNuno Das Neves 
1868*621191d7SNuno Das Neves 	mshv_eventfd_release(partition);
1869*621191d7SNuno Das Neves 
1870*621191d7SNuno Das Neves 	cleanup_srcu_struct(&partition->pt_irq_srcu);
1871*621191d7SNuno Das Neves 
1872*621191d7SNuno Das Neves 	mshv_partition_put(partition);
1873*621191d7SNuno Das Neves 
1874*621191d7SNuno Das Neves 	return 0;
1875*621191d7SNuno Das Neves }
1876*621191d7SNuno Das Neves 
1877*621191d7SNuno Das Neves static int
1878*621191d7SNuno Das Neves add_partition(struct mshv_partition *partition)
1879*621191d7SNuno Das Neves {
1880*621191d7SNuno Das Neves 	spin_lock(&mshv_root.pt_ht_lock);
1881*621191d7SNuno Das Neves 
1882*621191d7SNuno Das Neves 	hash_add_rcu(mshv_root.pt_htable, &partition->pt_hnode,
1883*621191d7SNuno Das Neves 		     partition->pt_id);
1884*621191d7SNuno Das Neves 
1885*621191d7SNuno Das Neves 	spin_unlock(&mshv_root.pt_ht_lock);
1886*621191d7SNuno Das Neves 
1887*621191d7SNuno Das Neves 	return 0;
1888*621191d7SNuno Das Neves }
1889*621191d7SNuno Das Neves 
1890*621191d7SNuno Das Neves static long
1891*621191d7SNuno Das Neves mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
1892*621191d7SNuno Das Neves {
1893*621191d7SNuno Das Neves 	struct mshv_create_partition args;
1894*621191d7SNuno Das Neves 	u64 creation_flags;
1895*621191d7SNuno Das Neves 	struct hv_partition_creation_properties creation_properties = {};
1896*621191d7SNuno Das Neves 	union hv_partition_isolation_properties isolation_properties = {};
1897*621191d7SNuno Das Neves 	struct mshv_partition *partition;
1898*621191d7SNuno Das Neves 	struct file *file;
1899*621191d7SNuno Das Neves 	int fd;
1900*621191d7SNuno Das Neves 	long ret;
1901*621191d7SNuno Das Neves 
1902*621191d7SNuno Das Neves 	if (copy_from_user(&args, user_arg, sizeof(args)))
1903*621191d7SNuno Das Neves 		return -EFAULT;
1904*621191d7SNuno Das Neves 
1905*621191d7SNuno Das Neves 	if ((args.pt_flags & ~MSHV_PT_FLAGS_MASK) ||
1906*621191d7SNuno Das Neves 	    args.pt_isolation >= MSHV_PT_ISOLATION_COUNT)
1907*621191d7SNuno Das Neves 		return -EINVAL;
1908*621191d7SNuno Das Neves 
1909*621191d7SNuno Das Neves 	/* Only support EXO partitions */
1910*621191d7SNuno Das Neves 	creation_flags = HV_PARTITION_CREATION_FLAG_EXO_PARTITION |
1911*621191d7SNuno Das Neves 			 HV_PARTITION_CREATION_FLAG_INTERCEPT_MESSAGE_PAGE_ENABLED;
1912*621191d7SNuno Das Neves 
1913*621191d7SNuno Das Neves 	if (args.pt_flags & BIT(MSHV_PT_BIT_LAPIC))
1914*621191d7SNuno Das Neves 		creation_flags |= HV_PARTITION_CREATION_FLAG_LAPIC_ENABLED;
1915*621191d7SNuno Das Neves 	if (args.pt_flags & BIT(MSHV_PT_BIT_X2APIC))
1916*621191d7SNuno Das Neves 		creation_flags |= HV_PARTITION_CREATION_FLAG_X2APIC_CAPABLE;
1917*621191d7SNuno Das Neves 	if (args.pt_flags & BIT(MSHV_PT_BIT_GPA_SUPER_PAGES))
1918*621191d7SNuno Das Neves 		creation_flags |= HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED;
1919*621191d7SNuno Das Neves 
1920*621191d7SNuno Das Neves 	switch (args.pt_isolation) {
1921*621191d7SNuno Das Neves 	case MSHV_PT_ISOLATION_NONE:
1922*621191d7SNuno Das Neves 		isolation_properties.isolation_type =
1923*621191d7SNuno Das Neves 			HV_PARTITION_ISOLATION_TYPE_NONE;
1924*621191d7SNuno Das Neves 		break;
1925*621191d7SNuno Das Neves 	}
1926*621191d7SNuno Das Neves 
1927*621191d7SNuno Das Neves 	partition = kzalloc(sizeof(*partition), GFP_KERNEL);
1928*621191d7SNuno Das Neves 	if (!partition)
1929*621191d7SNuno Das Neves 		return -ENOMEM;
1930*621191d7SNuno Das Neves 
1931*621191d7SNuno Das Neves 	partition->pt_module_dev = module_dev;
1932*621191d7SNuno Das Neves 	partition->isolation_type = isolation_properties.isolation_type;
1933*621191d7SNuno Das Neves 
1934*621191d7SNuno Das Neves 	refcount_set(&partition->pt_ref_count, 1);
1935*621191d7SNuno Das Neves 
1936*621191d7SNuno Das Neves 	mutex_init(&partition->pt_mutex);
1937*621191d7SNuno Das Neves 
1938*621191d7SNuno Das Neves 	mutex_init(&partition->pt_irq_lock);
1939*621191d7SNuno Das Neves 
1940*621191d7SNuno Das Neves 	init_completion(&partition->async_hypercall);
1941*621191d7SNuno Das Neves 
1942*621191d7SNuno Das Neves 	INIT_HLIST_HEAD(&partition->irq_ack_notifier_list);
1943*621191d7SNuno Das Neves 
1944*621191d7SNuno Das Neves 	INIT_HLIST_HEAD(&partition->pt_devices);
1945*621191d7SNuno Das Neves 
1946*621191d7SNuno Das Neves 	INIT_HLIST_HEAD(&partition->pt_mem_regions);
1947*621191d7SNuno Das Neves 
1948*621191d7SNuno Das Neves 	mshv_eventfd_init(partition);
1949*621191d7SNuno Das Neves 
1950*621191d7SNuno Das Neves 	ret = init_srcu_struct(&partition->pt_irq_srcu);
1951*621191d7SNuno Das Neves 	if (ret)
1952*621191d7SNuno Das Neves 		goto free_partition;
1953*621191d7SNuno Das Neves 
1954*621191d7SNuno Das Neves 	ret = hv_call_create_partition(creation_flags,
1955*621191d7SNuno Das Neves 				       creation_properties,
1956*621191d7SNuno Das Neves 				       isolation_properties,
1957*621191d7SNuno Das Neves 				       &partition->pt_id);
1958*621191d7SNuno Das Neves 	if (ret)
1959*621191d7SNuno Das Neves 		goto cleanup_irq_srcu;
1960*621191d7SNuno Das Neves 
1961*621191d7SNuno Das Neves 	ret = add_partition(partition);
1962*621191d7SNuno Das Neves 	if (ret)
1963*621191d7SNuno Das Neves 		goto delete_partition;
1964*621191d7SNuno Das Neves 
1965*621191d7SNuno Das Neves 	ret = mshv_init_async_handler(partition);
1966*621191d7SNuno Das Neves 	if (ret)
1967*621191d7SNuno Das Neves 		goto remove_partition;
1968*621191d7SNuno Das Neves 
1969*621191d7SNuno Das Neves 	fd = get_unused_fd_flags(O_CLOEXEC);
1970*621191d7SNuno Das Neves 	if (fd < 0) {
1971*621191d7SNuno Das Neves 		ret = fd;
1972*621191d7SNuno Das Neves 		goto remove_partition;
1973*621191d7SNuno Das Neves 	}
1974*621191d7SNuno Das Neves 
1975*621191d7SNuno Das Neves 	file = anon_inode_getfile("mshv_partition", &mshv_partition_fops,
1976*621191d7SNuno Das Neves 				  partition, O_RDWR);
1977*621191d7SNuno Das Neves 	if (IS_ERR(file)) {
1978*621191d7SNuno Das Neves 		ret = PTR_ERR(file);
1979*621191d7SNuno Das Neves 		goto put_fd;
1980*621191d7SNuno Das Neves 	}
1981*621191d7SNuno Das Neves 
1982*621191d7SNuno Das Neves 	fd_install(fd, file);
1983*621191d7SNuno Das Neves 
1984*621191d7SNuno Das Neves 	return fd;
1985*621191d7SNuno Das Neves 
1986*621191d7SNuno Das Neves put_fd:
1987*621191d7SNuno Das Neves 	put_unused_fd(fd);
1988*621191d7SNuno Das Neves remove_partition:
1989*621191d7SNuno Das Neves 	remove_partition(partition);
1990*621191d7SNuno Das Neves delete_partition:
1991*621191d7SNuno Das Neves 	hv_call_delete_partition(partition->pt_id);
1992*621191d7SNuno Das Neves cleanup_irq_srcu:
1993*621191d7SNuno Das Neves 	cleanup_srcu_struct(&partition->pt_irq_srcu);
1994*621191d7SNuno Das Neves free_partition:
1995*621191d7SNuno Das Neves 	kfree(partition);
1996*621191d7SNuno Das Neves 
1997*621191d7SNuno Das Neves 	return ret;
1998*621191d7SNuno Das Neves }
1999*621191d7SNuno Das Neves 
2000*621191d7SNuno Das Neves static long mshv_dev_ioctl(struct file *filp, unsigned int ioctl,
2001*621191d7SNuno Das Neves 			   unsigned long arg)
2002*621191d7SNuno Das Neves {
2003*621191d7SNuno Das Neves 	struct miscdevice *misc = filp->private_data;
2004*621191d7SNuno Das Neves 
2005*621191d7SNuno Das Neves 	switch (ioctl) {
2006*621191d7SNuno Das Neves 	case MSHV_CREATE_PARTITION:
2007*621191d7SNuno Das Neves 		return mshv_ioctl_create_partition((void __user *)arg,
2008*621191d7SNuno Das Neves 						misc->this_device);
2009*621191d7SNuno Das Neves 	}
2010*621191d7SNuno Das Neves 
2011*621191d7SNuno Das Neves 	return -ENOTTY;
2012*621191d7SNuno Das Neves }
2013*621191d7SNuno Das Neves 
2014*621191d7SNuno Das Neves static int
2015*621191d7SNuno Das Neves mshv_dev_open(struct inode *inode, struct file *filp)
2016*621191d7SNuno Das Neves {
2017*621191d7SNuno Das Neves 	return 0;
2018*621191d7SNuno Das Neves }
2019*621191d7SNuno Das Neves 
2020*621191d7SNuno Das Neves static int
2021*621191d7SNuno Das Neves mshv_dev_release(struct inode *inode, struct file *filp)
2022*621191d7SNuno Das Neves {
2023*621191d7SNuno Das Neves 	return 0;
2024*621191d7SNuno Das Neves }
2025*621191d7SNuno Das Neves 
2026*621191d7SNuno Das Neves static int mshv_cpuhp_online;
2027*621191d7SNuno Das Neves static int mshv_root_sched_online;
2028*621191d7SNuno Das Neves 
2029*621191d7SNuno Das Neves static const char *scheduler_type_to_string(enum hv_scheduler_type type)
2030*621191d7SNuno Das Neves {
2031*621191d7SNuno Das Neves 	switch (type) {
2032*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_LP:
2033*621191d7SNuno Das Neves 		return "classic scheduler without SMT";
2034*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_LP_SMT:
2035*621191d7SNuno Das Neves 		return "classic scheduler with SMT";
2036*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_CORE_SMT:
2037*621191d7SNuno Das Neves 		return "core scheduler";
2038*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_ROOT:
2039*621191d7SNuno Das Neves 		return "root scheduler";
2040*621191d7SNuno Das Neves 	default:
2041*621191d7SNuno Das Neves 		return "unknown scheduler";
2042*621191d7SNuno Das Neves 	};
2043*621191d7SNuno Das Neves }
2044*621191d7SNuno Das Neves 
2045*621191d7SNuno Das Neves /* TODO move this to hv_common.c when needed outside */
2046*621191d7SNuno Das Neves static int __init hv_retrieve_scheduler_type(enum hv_scheduler_type *out)
2047*621191d7SNuno Das Neves {
2048*621191d7SNuno Das Neves 	struct hv_input_get_system_property *input;
2049*621191d7SNuno Das Neves 	struct hv_output_get_system_property *output;
2050*621191d7SNuno Das Neves 	unsigned long flags;
2051*621191d7SNuno Das Neves 	u64 status;
2052*621191d7SNuno Das Neves 
2053*621191d7SNuno Das Neves 	local_irq_save(flags);
2054*621191d7SNuno Das Neves 	input = *this_cpu_ptr(hyperv_pcpu_input_arg);
2055*621191d7SNuno Das Neves 	output = *this_cpu_ptr(hyperv_pcpu_output_arg);
2056*621191d7SNuno Das Neves 
2057*621191d7SNuno Das Neves 	memset(input, 0, sizeof(*input));
2058*621191d7SNuno Das Neves 	memset(output, 0, sizeof(*output));
2059*621191d7SNuno Das Neves 	input->property_id = HV_SYSTEM_PROPERTY_SCHEDULER_TYPE;
2060*621191d7SNuno Das Neves 
2061*621191d7SNuno Das Neves 	status = hv_do_hypercall(HVCALL_GET_SYSTEM_PROPERTY, input, output);
2062*621191d7SNuno Das Neves 	if (!hv_result_success(status)) {
2063*621191d7SNuno Das Neves 		local_irq_restore(flags);
2064*621191d7SNuno Das Neves 		pr_err("%s: %s\n", __func__, hv_result_to_string(status));
2065*621191d7SNuno Das Neves 		return hv_result_to_errno(status);
2066*621191d7SNuno Das Neves 	}
2067*621191d7SNuno Das Neves 
2068*621191d7SNuno Das Neves 	*out = output->scheduler_type;
2069*621191d7SNuno Das Neves 	local_irq_restore(flags);
2070*621191d7SNuno Das Neves 
2071*621191d7SNuno Das Neves 	return 0;
2072*621191d7SNuno Das Neves }
2073*621191d7SNuno Das Neves 
2074*621191d7SNuno Das Neves /* Retrieve and stash the supported scheduler type */
2075*621191d7SNuno Das Neves static int __init mshv_retrieve_scheduler_type(struct device *dev)
2076*621191d7SNuno Das Neves {
2077*621191d7SNuno Das Neves 	int ret;
2078*621191d7SNuno Das Neves 
2079*621191d7SNuno Das Neves 	ret = hv_retrieve_scheduler_type(&hv_scheduler_type);
2080*621191d7SNuno Das Neves 	if (ret)
2081*621191d7SNuno Das Neves 		return ret;
2082*621191d7SNuno Das Neves 
2083*621191d7SNuno Das Neves 	dev_info(dev, "Hypervisor using %s\n",
2084*621191d7SNuno Das Neves 		 scheduler_type_to_string(hv_scheduler_type));
2085*621191d7SNuno Das Neves 
2086*621191d7SNuno Das Neves 	switch (hv_scheduler_type) {
2087*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_CORE_SMT:
2088*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_LP_SMT:
2089*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_ROOT:
2090*621191d7SNuno Das Neves 	case HV_SCHEDULER_TYPE_LP:
2091*621191d7SNuno Das Neves 		/* Supported scheduler, nothing to do */
2092*621191d7SNuno Das Neves 		break;
2093*621191d7SNuno Das Neves 	default:
2094*621191d7SNuno Das Neves 		dev_err(dev, "unsupported scheduler 0x%x, bailing.\n",
2095*621191d7SNuno Das Neves 			hv_scheduler_type);
2096*621191d7SNuno Das Neves 		return -EOPNOTSUPP;
2097*621191d7SNuno Das Neves 	}
2098*621191d7SNuno Das Neves 
2099*621191d7SNuno Das Neves 	return 0;
2100*621191d7SNuno Das Neves }
2101*621191d7SNuno Das Neves 
2102*621191d7SNuno Das Neves static int mshv_root_scheduler_init(unsigned int cpu)
2103*621191d7SNuno Das Neves {
2104*621191d7SNuno Das Neves 	void **inputarg, **outputarg, *p;
2105*621191d7SNuno Das Neves 
2106*621191d7SNuno Das Neves 	inputarg = (void **)this_cpu_ptr(root_scheduler_input);
2107*621191d7SNuno Das Neves 	outputarg = (void **)this_cpu_ptr(root_scheduler_output);
2108*621191d7SNuno Das Neves 
2109*621191d7SNuno Das Neves 	/* Allocate two consecutive pages. One for input, one for output. */
2110*621191d7SNuno Das Neves 	p = kmalloc(2 * HV_HYP_PAGE_SIZE, GFP_KERNEL);
2111*621191d7SNuno Das Neves 	if (!p)
2112*621191d7SNuno Das Neves 		return -ENOMEM;
2113*621191d7SNuno Das Neves 
2114*621191d7SNuno Das Neves 	*inputarg = p;
2115*621191d7SNuno Das Neves 	*outputarg = (char *)p + HV_HYP_PAGE_SIZE;
2116*621191d7SNuno Das Neves 
2117*621191d7SNuno Das Neves 	return 0;
2118*621191d7SNuno Das Neves }
2119*621191d7SNuno Das Neves 
2120*621191d7SNuno Das Neves static int mshv_root_scheduler_cleanup(unsigned int cpu)
2121*621191d7SNuno Das Neves {
2122*621191d7SNuno Das Neves 	void *p, **inputarg, **outputarg;
2123*621191d7SNuno Das Neves 
2124*621191d7SNuno Das Neves 	inputarg = (void **)this_cpu_ptr(root_scheduler_input);
2125*621191d7SNuno Das Neves 	outputarg = (void **)this_cpu_ptr(root_scheduler_output);
2126*621191d7SNuno Das Neves 
2127*621191d7SNuno Das Neves 	p = *inputarg;
2128*621191d7SNuno Das Neves 
2129*621191d7SNuno Das Neves 	*inputarg = NULL;
2130*621191d7SNuno Das Neves 	*outputarg = NULL;
2131*621191d7SNuno Das Neves 
2132*621191d7SNuno Das Neves 	kfree(p);
2133*621191d7SNuno Das Neves 
2134*621191d7SNuno Das Neves 	return 0;
2135*621191d7SNuno Das Neves }
2136*621191d7SNuno Das Neves 
2137*621191d7SNuno Das Neves /* Must be called after retrieving the scheduler type */
2138*621191d7SNuno Das Neves static int
2139*621191d7SNuno Das Neves root_scheduler_init(struct device *dev)
2140*621191d7SNuno Das Neves {
2141*621191d7SNuno Das Neves 	int ret;
2142*621191d7SNuno Das Neves 
2143*621191d7SNuno Das Neves 	if (hv_scheduler_type != HV_SCHEDULER_TYPE_ROOT)
2144*621191d7SNuno Das Neves 		return 0;
2145*621191d7SNuno Das Neves 
2146*621191d7SNuno Das Neves 	root_scheduler_input = alloc_percpu(void *);
2147*621191d7SNuno Das Neves 	root_scheduler_output = alloc_percpu(void *);
2148*621191d7SNuno Das Neves 
2149*621191d7SNuno Das Neves 	if (!root_scheduler_input || !root_scheduler_output) {
2150*621191d7SNuno Das Neves 		dev_err(dev, "Failed to allocate root scheduler buffers\n");
2151*621191d7SNuno Das Neves 		ret = -ENOMEM;
2152*621191d7SNuno Das Neves 		goto out;
2153*621191d7SNuno Das Neves 	}
2154*621191d7SNuno Das Neves 
2155*621191d7SNuno Das Neves 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_root_sched",
2156*621191d7SNuno Das Neves 				mshv_root_scheduler_init,
2157*621191d7SNuno Das Neves 				mshv_root_scheduler_cleanup);
2158*621191d7SNuno Das Neves 
2159*621191d7SNuno Das Neves 	if (ret < 0) {
2160*621191d7SNuno Das Neves 		dev_err(dev, "Failed to setup root scheduler state: %i\n", ret);
2161*621191d7SNuno Das Neves 		goto out;
2162*621191d7SNuno Das Neves 	}
2163*621191d7SNuno Das Neves 
2164*621191d7SNuno Das Neves 	mshv_root_sched_online = ret;
2165*621191d7SNuno Das Neves 
2166*621191d7SNuno Das Neves 	return 0;
2167*621191d7SNuno Das Neves 
2168*621191d7SNuno Das Neves out:
2169*621191d7SNuno Das Neves 	free_percpu(root_scheduler_input);
2170*621191d7SNuno Das Neves 	free_percpu(root_scheduler_output);
2171*621191d7SNuno Das Neves 	return ret;
2172*621191d7SNuno Das Neves }
2173*621191d7SNuno Das Neves 
2174*621191d7SNuno Das Neves static void
2175*621191d7SNuno Das Neves root_scheduler_deinit(void)
2176*621191d7SNuno Das Neves {
2177*621191d7SNuno Das Neves 	if (hv_scheduler_type != HV_SCHEDULER_TYPE_ROOT)
2178*621191d7SNuno Das Neves 		return;
2179*621191d7SNuno Das Neves 
2180*621191d7SNuno Das Neves 	cpuhp_remove_state(mshv_root_sched_online);
2181*621191d7SNuno Das Neves 	free_percpu(root_scheduler_input);
2182*621191d7SNuno Das Neves 	free_percpu(root_scheduler_output);
2183*621191d7SNuno Das Neves }
2184*621191d7SNuno Das Neves 
2185*621191d7SNuno Das Neves static int mshv_reboot_notify(struct notifier_block *nb,
2186*621191d7SNuno Das Neves 			      unsigned long code, void *unused)
2187*621191d7SNuno Das Neves {
2188*621191d7SNuno Das Neves 	cpuhp_remove_state(mshv_cpuhp_online);
2189*621191d7SNuno Das Neves 	return 0;
2190*621191d7SNuno Das Neves }
2191*621191d7SNuno Das Neves 
2192*621191d7SNuno Das Neves struct notifier_block mshv_reboot_nb = {
2193*621191d7SNuno Das Neves 	.notifier_call = mshv_reboot_notify,
2194*621191d7SNuno Das Neves };
2195*621191d7SNuno Das Neves 
2196*621191d7SNuno Das Neves static void mshv_root_partition_exit(void)
2197*621191d7SNuno Das Neves {
2198*621191d7SNuno Das Neves 	unregister_reboot_notifier(&mshv_reboot_nb);
2199*621191d7SNuno Das Neves 	root_scheduler_deinit();
2200*621191d7SNuno Das Neves }
2201*621191d7SNuno Das Neves 
2202*621191d7SNuno Das Neves static int __init mshv_root_partition_init(struct device *dev)
2203*621191d7SNuno Das Neves {
2204*621191d7SNuno Das Neves 	int err;
2205*621191d7SNuno Das Neves 
2206*621191d7SNuno Das Neves 	if (mshv_retrieve_scheduler_type(dev))
2207*621191d7SNuno Das Neves 		return -ENODEV;
2208*621191d7SNuno Das Neves 
2209*621191d7SNuno Das Neves 	err = root_scheduler_init(dev);
2210*621191d7SNuno Das Neves 	if (err)
2211*621191d7SNuno Das Neves 		return err;
2212*621191d7SNuno Das Neves 
2213*621191d7SNuno Das Neves 	err = register_reboot_notifier(&mshv_reboot_nb);
2214*621191d7SNuno Das Neves 	if (err)
2215*621191d7SNuno Das Neves 		goto root_sched_deinit;
2216*621191d7SNuno Das Neves 
2217*621191d7SNuno Das Neves 	return 0;
2218*621191d7SNuno Das Neves 
2219*621191d7SNuno Das Neves root_sched_deinit:
2220*621191d7SNuno Das Neves 	root_scheduler_deinit();
2221*621191d7SNuno Das Neves 	return err;
2222*621191d7SNuno Das Neves }
2223*621191d7SNuno Das Neves 
2224*621191d7SNuno Das Neves static int __init mshv_parent_partition_init(void)
2225*621191d7SNuno Das Neves {
2226*621191d7SNuno Das Neves 	int ret;
2227*621191d7SNuno Das Neves 	struct device *dev;
2228*621191d7SNuno Das Neves 	union hv_hypervisor_version_info version_info;
2229*621191d7SNuno Das Neves 
2230*621191d7SNuno Das Neves 	if (!hv_root_partition() || is_kdump_kernel())
2231*621191d7SNuno Das Neves 		return -ENODEV;
2232*621191d7SNuno Das Neves 
2233*621191d7SNuno Das Neves 	if (hv_get_hypervisor_version(&version_info))
2234*621191d7SNuno Das Neves 		return -ENODEV;
2235*621191d7SNuno Das Neves 
2236*621191d7SNuno Das Neves 	ret = misc_register(&mshv_dev);
2237*621191d7SNuno Das Neves 	if (ret)
2238*621191d7SNuno Das Neves 		return ret;
2239*621191d7SNuno Das Neves 
2240*621191d7SNuno Das Neves 	dev = mshv_dev.this_device;
2241*621191d7SNuno Das Neves 
2242*621191d7SNuno Das Neves 	if (version_info.build_number < MSHV_HV_MIN_VERSION ||
2243*621191d7SNuno Das Neves 	    version_info.build_number > MSHV_HV_MAX_VERSION) {
2244*621191d7SNuno Das Neves 		dev_err(dev, "Running on unvalidated Hyper-V version\n");
2245*621191d7SNuno Das Neves 		dev_err(dev, "Versions: current: %u  min: %u  max: %u\n",
2246*621191d7SNuno Das Neves 			version_info.build_number, MSHV_HV_MIN_VERSION,
2247*621191d7SNuno Das Neves 			MSHV_HV_MAX_VERSION);
2248*621191d7SNuno Das Neves 	}
2249*621191d7SNuno Das Neves 
2250*621191d7SNuno Das Neves 	mshv_root.synic_pages = alloc_percpu(struct hv_synic_pages);
2251*621191d7SNuno Das Neves 	if (!mshv_root.synic_pages) {
2252*621191d7SNuno Das Neves 		dev_err(dev, "Failed to allocate percpu synic page\n");
2253*621191d7SNuno Das Neves 		ret = -ENOMEM;
2254*621191d7SNuno Das Neves 		goto device_deregister;
2255*621191d7SNuno Das Neves 	}
2256*621191d7SNuno Das Neves 
2257*621191d7SNuno Das Neves 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
2258*621191d7SNuno Das Neves 				mshv_synic_init,
2259*621191d7SNuno Das Neves 				mshv_synic_cleanup);
2260*621191d7SNuno Das Neves 	if (ret < 0) {
2261*621191d7SNuno Das Neves 		dev_err(dev, "Failed to setup cpu hotplug state: %i\n", ret);
2262*621191d7SNuno Das Neves 		goto free_synic_pages;
2263*621191d7SNuno Das Neves 	}
2264*621191d7SNuno Das Neves 
2265*621191d7SNuno Das Neves 	mshv_cpuhp_online = ret;
2266*621191d7SNuno Das Neves 
2267*621191d7SNuno Das Neves 	ret = mshv_root_partition_init(dev);
2268*621191d7SNuno Das Neves 	if (ret)
2269*621191d7SNuno Das Neves 		goto remove_cpu_state;
2270*621191d7SNuno Das Neves 
2271*621191d7SNuno Das Neves 	ret = mshv_irqfd_wq_init();
2272*621191d7SNuno Das Neves 	if (ret)
2273*621191d7SNuno Das Neves 		goto exit_partition;
2274*621191d7SNuno Das Neves 
2275*621191d7SNuno Das Neves 	spin_lock_init(&mshv_root.pt_ht_lock);
2276*621191d7SNuno Das Neves 	hash_init(mshv_root.pt_htable);
2277*621191d7SNuno Das Neves 
2278*621191d7SNuno Das Neves 	hv_setup_mshv_handler(mshv_isr);
2279*621191d7SNuno Das Neves 
2280*621191d7SNuno Das Neves 	return 0;
2281*621191d7SNuno Das Neves 
2282*621191d7SNuno Das Neves exit_partition:
2283*621191d7SNuno Das Neves 	if (hv_root_partition())
2284*621191d7SNuno Das Neves 		mshv_root_partition_exit();
2285*621191d7SNuno Das Neves remove_cpu_state:
2286*621191d7SNuno Das Neves 	cpuhp_remove_state(mshv_cpuhp_online);
2287*621191d7SNuno Das Neves free_synic_pages:
2288*621191d7SNuno Das Neves 	free_percpu(mshv_root.synic_pages);
2289*621191d7SNuno Das Neves device_deregister:
2290*621191d7SNuno Das Neves 	misc_deregister(&mshv_dev);
2291*621191d7SNuno Das Neves 	return ret;
2292*621191d7SNuno Das Neves }
2293*621191d7SNuno Das Neves 
2294*621191d7SNuno Das Neves static void __exit mshv_parent_partition_exit(void)
2295*621191d7SNuno Das Neves {
2296*621191d7SNuno Das Neves 	hv_setup_mshv_handler(NULL);
2297*621191d7SNuno Das Neves 	mshv_port_table_fini();
2298*621191d7SNuno Das Neves 	misc_deregister(&mshv_dev);
2299*621191d7SNuno Das Neves 	mshv_irqfd_wq_cleanup();
2300*621191d7SNuno Das Neves 	if (hv_root_partition())
2301*621191d7SNuno Das Neves 		mshv_root_partition_exit();
2302*621191d7SNuno Das Neves 	cpuhp_remove_state(mshv_cpuhp_online);
2303*621191d7SNuno Das Neves 	free_percpu(mshv_root.synic_pages);
2304*621191d7SNuno Das Neves }
2305*621191d7SNuno Das Neves 
2306*621191d7SNuno Das Neves module_init(mshv_parent_partition_init);
2307*621191d7SNuno Das Neves module_exit(mshv_parent_partition_exit);
2308