xref: /linux/drivers/hv/mshv_common.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  * This file contains functions that will be called from one or more modules.
6*621191d7SNuno Das Neves  * If any of these modules are configured to build, this file is built and just
7*621191d7SNuno Das Neves  * statically linked in.
8*621191d7SNuno Das Neves  *
9*621191d7SNuno Das Neves  * Authors: Microsoft Linux virtualization team
10*621191d7SNuno Das Neves  */
11*621191d7SNuno Das Neves 
12*621191d7SNuno Das Neves #include <linux/kernel.h>
13*621191d7SNuno Das Neves #include <linux/mm.h>
14*621191d7SNuno Das Neves #include <asm/mshyperv.h>
15*621191d7SNuno Das Neves #include <linux/resume_user_mode.h>
16*621191d7SNuno Das Neves 
17*621191d7SNuno Das Neves #include "mshv.h"
18*621191d7SNuno Das Neves 
19*621191d7SNuno Das Neves #define HV_GET_REGISTER_BATCH_SIZE	\
20*621191d7SNuno Das Neves 	(HV_HYP_PAGE_SIZE / sizeof(union hv_register_value))
21*621191d7SNuno Das Neves #define HV_SET_REGISTER_BATCH_SIZE	\
22*621191d7SNuno Das Neves 	((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_set_vp_registers)) \
23*621191d7SNuno Das Neves 		/ sizeof(struct hv_register_assoc))
24*621191d7SNuno Das Neves 
25*621191d7SNuno Das Neves int hv_call_get_vp_registers(u32 vp_index, u64 partition_id, u16 count,
26*621191d7SNuno Das Neves 			     union hv_input_vtl input_vtl,
27*621191d7SNuno Das Neves 			     struct hv_register_assoc *registers)
28*621191d7SNuno Das Neves {
29*621191d7SNuno Das Neves 	struct hv_input_get_vp_registers *input_page;
30*621191d7SNuno Das Neves 	union hv_register_value *output_page;
31*621191d7SNuno Das Neves 	u16 completed = 0;
32*621191d7SNuno Das Neves 	unsigned long remaining = count;
33*621191d7SNuno Das Neves 	int rep_count, i;
34*621191d7SNuno Das Neves 	u64 status = HV_STATUS_SUCCESS;
35*621191d7SNuno Das Neves 	unsigned long flags;
36*621191d7SNuno Das Neves 
37*621191d7SNuno Das Neves 	local_irq_save(flags);
38*621191d7SNuno Das Neves 
39*621191d7SNuno Das Neves 	input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
40*621191d7SNuno Das Neves 	output_page = *this_cpu_ptr(hyperv_pcpu_output_arg);
41*621191d7SNuno Das Neves 
42*621191d7SNuno Das Neves 	input_page->partition_id = partition_id;
43*621191d7SNuno Das Neves 	input_page->vp_index = vp_index;
44*621191d7SNuno Das Neves 	input_page->input_vtl.as_uint8 = input_vtl.as_uint8;
45*621191d7SNuno Das Neves 	input_page->rsvd_z8 = 0;
46*621191d7SNuno Das Neves 	input_page->rsvd_z16 = 0;
47*621191d7SNuno Das Neves 
48*621191d7SNuno Das Neves 	while (remaining) {
49*621191d7SNuno Das Neves 		rep_count = min(remaining, HV_GET_REGISTER_BATCH_SIZE);
50*621191d7SNuno Das Neves 		for (i = 0; i < rep_count; ++i)
51*621191d7SNuno Das Neves 			input_page->names[i] = registers[i].name;
52*621191d7SNuno Das Neves 
53*621191d7SNuno Das Neves 		status = hv_do_rep_hypercall(HVCALL_GET_VP_REGISTERS, rep_count,
54*621191d7SNuno Das Neves 					     0, input_page, output_page);
55*621191d7SNuno Das Neves 		if (!hv_result_success(status))
56*621191d7SNuno Das Neves 			break;
57*621191d7SNuno Das Neves 
58*621191d7SNuno Das Neves 		completed = hv_repcomp(status);
59*621191d7SNuno Das Neves 		for (i = 0; i < completed; ++i)
60*621191d7SNuno Das Neves 			registers[i].value = output_page[i];
61*621191d7SNuno Das Neves 
62*621191d7SNuno Das Neves 		registers += completed;
63*621191d7SNuno Das Neves 		remaining -= completed;
64*621191d7SNuno Das Neves 	}
65*621191d7SNuno Das Neves 	local_irq_restore(flags);
66*621191d7SNuno Das Neves 
67*621191d7SNuno Das Neves 	return hv_result_to_errno(status);
68*621191d7SNuno Das Neves }
69*621191d7SNuno Das Neves EXPORT_SYMBOL_GPL(hv_call_get_vp_registers);
70*621191d7SNuno Das Neves 
71*621191d7SNuno Das Neves int hv_call_set_vp_registers(u32 vp_index, u64 partition_id, u16 count,
72*621191d7SNuno Das Neves 			     union hv_input_vtl input_vtl,
73*621191d7SNuno Das Neves 			     struct hv_register_assoc *registers)
74*621191d7SNuno Das Neves {
75*621191d7SNuno Das Neves 	struct hv_input_set_vp_registers *input_page;
76*621191d7SNuno Das Neves 	u16 completed = 0;
77*621191d7SNuno Das Neves 	unsigned long remaining = count;
78*621191d7SNuno Das Neves 	int rep_count;
79*621191d7SNuno Das Neves 	u64 status = HV_STATUS_SUCCESS;
80*621191d7SNuno Das Neves 	unsigned long flags;
81*621191d7SNuno Das Neves 
82*621191d7SNuno Das Neves 	local_irq_save(flags);
83*621191d7SNuno Das Neves 	input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
84*621191d7SNuno Das Neves 
85*621191d7SNuno Das Neves 	input_page->partition_id = partition_id;
86*621191d7SNuno Das Neves 	input_page->vp_index = vp_index;
87*621191d7SNuno Das Neves 	input_page->input_vtl.as_uint8 = input_vtl.as_uint8;
88*621191d7SNuno Das Neves 	input_page->rsvd_z8 = 0;
89*621191d7SNuno Das Neves 	input_page->rsvd_z16 = 0;
90*621191d7SNuno Das Neves 
91*621191d7SNuno Das Neves 	while (remaining) {
92*621191d7SNuno Das Neves 		rep_count = min(remaining, HV_SET_REGISTER_BATCH_SIZE);
93*621191d7SNuno Das Neves 		memcpy(input_page->elements, registers,
94*621191d7SNuno Das Neves 		       sizeof(struct hv_register_assoc) * rep_count);
95*621191d7SNuno Das Neves 
96*621191d7SNuno Das Neves 		status = hv_do_rep_hypercall(HVCALL_SET_VP_REGISTERS, rep_count,
97*621191d7SNuno Das Neves 					     0, input_page, NULL);
98*621191d7SNuno Das Neves 		if (!hv_result_success(status))
99*621191d7SNuno Das Neves 			break;
100*621191d7SNuno Das Neves 
101*621191d7SNuno Das Neves 		completed = hv_repcomp(status);
102*621191d7SNuno Das Neves 		registers += completed;
103*621191d7SNuno Das Neves 		remaining -= completed;
104*621191d7SNuno Das Neves 	}
105*621191d7SNuno Das Neves 
106*621191d7SNuno Das Neves 	local_irq_restore(flags);
107*621191d7SNuno Das Neves 
108*621191d7SNuno Das Neves 	return hv_result_to_errno(status);
109*621191d7SNuno Das Neves }
110*621191d7SNuno Das Neves EXPORT_SYMBOL_GPL(hv_call_set_vp_registers);
111*621191d7SNuno Das Neves 
112*621191d7SNuno Das Neves int hv_call_get_partition_property(u64 partition_id,
113*621191d7SNuno Das Neves 				   u64 property_code,
114*621191d7SNuno Das Neves 				   u64 *property_value)
115*621191d7SNuno Das Neves {
116*621191d7SNuno Das Neves 	u64 status;
117*621191d7SNuno Das Neves 	unsigned long flags;
118*621191d7SNuno Das Neves 	struct hv_input_get_partition_property *input;
119*621191d7SNuno Das Neves 	struct hv_output_get_partition_property *output;
120*621191d7SNuno Das Neves 
121*621191d7SNuno Das Neves 	local_irq_save(flags);
122*621191d7SNuno Das Neves 	input = *this_cpu_ptr(hyperv_pcpu_input_arg);
123*621191d7SNuno Das Neves 	output = *this_cpu_ptr(hyperv_pcpu_output_arg);
124*621191d7SNuno Das Neves 	memset(input, 0, sizeof(*input));
125*621191d7SNuno Das Neves 	input->partition_id = partition_id;
126*621191d7SNuno Das Neves 	input->property_code = property_code;
127*621191d7SNuno Das Neves 	status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY, input, output);
128*621191d7SNuno Das Neves 
129*621191d7SNuno Das Neves 	if (!hv_result_success(status)) {
130*621191d7SNuno Das Neves 		local_irq_restore(flags);
131*621191d7SNuno Das Neves 		return hv_result_to_errno(status);
132*621191d7SNuno Das Neves 	}
133*621191d7SNuno Das Neves 	*property_value = output->property_value;
134*621191d7SNuno Das Neves 
135*621191d7SNuno Das Neves 	local_irq_restore(flags);
136*621191d7SNuno Das Neves 
137*621191d7SNuno Das Neves 	return 0;
138*621191d7SNuno Das Neves }
139*621191d7SNuno Das Neves EXPORT_SYMBOL_GPL(hv_call_get_partition_property);
140*621191d7SNuno Das Neves 
141*621191d7SNuno Das Neves /*
142*621191d7SNuno Das Neves  * Handle any pre-processing before going into the guest mode on this cpu, most
143*621191d7SNuno Das Neves  * notably call schedule(). Must be invoked with both preemption and
144*621191d7SNuno Das Neves  * interrupts enabled.
145*621191d7SNuno Das Neves  *
146*621191d7SNuno Das Neves  * Returns: 0 on success, -errno on error.
147*621191d7SNuno Das Neves  */
148*621191d7SNuno Das Neves int mshv_do_pre_guest_mode_work(ulong th_flags)
149*621191d7SNuno Das Neves {
150*621191d7SNuno Das Neves 	if (th_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
151*621191d7SNuno Das Neves 		return -EINTR;
152*621191d7SNuno Das Neves 
153*621191d7SNuno Das Neves 	if (th_flags & _TIF_NEED_RESCHED)
154*621191d7SNuno Das Neves 		schedule();
155*621191d7SNuno Das Neves 
156*621191d7SNuno Das Neves 	if (th_flags & _TIF_NOTIFY_RESUME)
157*621191d7SNuno Das Neves 		resume_user_mode_work(NULL);
158*621191d7SNuno Das Neves 
159*621191d7SNuno Das Neves 	return 0;
160*621191d7SNuno Das Neves }
161*621191d7SNuno Das Neves EXPORT_SYMBOL_GPL(mshv_do_pre_guest_mode_work);
162