xref: /OK3568_Linux_fs/kernel/arch/x86/hyperv/nested.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun 
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun  * Hyper-V nested virtualization code.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2018, Microsoft, Inc.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Author : Lan Tianyu <Tianyu.Lan@microsoft.com>
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #define pr_fmt(fmt)  "Hyper-V: " fmt
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/types.h>
14*4882a593Smuzhiyun #include <asm/hyperv-tlfs.h>
15*4882a593Smuzhiyun #include <asm/mshyperv.h>
16*4882a593Smuzhiyun #include <asm/tlbflush.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <asm/trace/hyperv.h>
19*4882a593Smuzhiyun 
hyperv_flush_guest_mapping(u64 as)20*4882a593Smuzhiyun int hyperv_flush_guest_mapping(u64 as)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun 	struct hv_guest_mapping_flush **flush_pcpu;
23*4882a593Smuzhiyun 	struct hv_guest_mapping_flush *flush;
24*4882a593Smuzhiyun 	u64 status;
25*4882a593Smuzhiyun 	unsigned long flags;
26*4882a593Smuzhiyun 	int ret = -ENOTSUPP;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	if (!hv_hypercall_pg)
29*4882a593Smuzhiyun 		goto fault;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	local_irq_save(flags);
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	flush_pcpu = (struct hv_guest_mapping_flush **)
34*4882a593Smuzhiyun 		this_cpu_ptr(hyperv_pcpu_input_arg);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	flush = *flush_pcpu;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	if (unlikely(!flush)) {
39*4882a593Smuzhiyun 		local_irq_restore(flags);
40*4882a593Smuzhiyun 		goto fault;
41*4882a593Smuzhiyun 	}
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	flush->address_space = as;
44*4882a593Smuzhiyun 	flush->flags = 0;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	status = hv_do_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE,
47*4882a593Smuzhiyun 				 flush, NULL);
48*4882a593Smuzhiyun 	local_irq_restore(flags);
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	if (!(status & HV_HYPERCALL_RESULT_MASK))
51*4882a593Smuzhiyun 		ret = 0;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun fault:
54*4882a593Smuzhiyun 	trace_hyperv_nested_flush_guest_mapping(as, ret);
55*4882a593Smuzhiyun 	return ret;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping);
58*4882a593Smuzhiyun 
hyperv_fill_flush_guest_mapping_list(struct hv_guest_mapping_flush_list * flush,u64 start_gfn,u64 pages)59*4882a593Smuzhiyun int hyperv_fill_flush_guest_mapping_list(
60*4882a593Smuzhiyun 		struct hv_guest_mapping_flush_list *flush,
61*4882a593Smuzhiyun 		u64 start_gfn, u64 pages)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	u64 cur = start_gfn;
64*4882a593Smuzhiyun 	u64 additional_pages;
65*4882a593Smuzhiyun 	int gpa_n = 0;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	do {
68*4882a593Smuzhiyun 		/*
69*4882a593Smuzhiyun 		 * If flush requests exceed max flush count, go back to
70*4882a593Smuzhiyun 		 * flush tlbs without range.
71*4882a593Smuzhiyun 		 */
72*4882a593Smuzhiyun 		if (gpa_n >= HV_MAX_FLUSH_REP_COUNT)
73*4882a593Smuzhiyun 			return -ENOSPC;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 		additional_pages = min_t(u64, pages, HV_MAX_FLUSH_PAGES) - 1;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		flush->gpa_list[gpa_n].page.additional_pages = additional_pages;
78*4882a593Smuzhiyun 		flush->gpa_list[gpa_n].page.largepage = false;
79*4882a593Smuzhiyun 		flush->gpa_list[gpa_n].page.basepfn = cur;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 		pages -= additional_pages + 1;
82*4882a593Smuzhiyun 		cur += additional_pages + 1;
83*4882a593Smuzhiyun 		gpa_n++;
84*4882a593Smuzhiyun 	} while (pages > 0);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	return gpa_n;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(hyperv_fill_flush_guest_mapping_list);
89*4882a593Smuzhiyun 
hyperv_flush_guest_mapping_range(u64 as,hyperv_fill_flush_list_func fill_flush_list_func,void * data)90*4882a593Smuzhiyun int hyperv_flush_guest_mapping_range(u64 as,
91*4882a593Smuzhiyun 		hyperv_fill_flush_list_func fill_flush_list_func, void *data)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct hv_guest_mapping_flush_list **flush_pcpu;
94*4882a593Smuzhiyun 	struct hv_guest_mapping_flush_list *flush;
95*4882a593Smuzhiyun 	u64 status = 0;
96*4882a593Smuzhiyun 	unsigned long flags;
97*4882a593Smuzhiyun 	int ret = -ENOTSUPP;
98*4882a593Smuzhiyun 	int gpa_n = 0;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	if (!hv_hypercall_pg || !fill_flush_list_func)
101*4882a593Smuzhiyun 		goto fault;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	local_irq_save(flags);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	flush_pcpu = (struct hv_guest_mapping_flush_list **)
106*4882a593Smuzhiyun 		this_cpu_ptr(hyperv_pcpu_input_arg);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	flush = *flush_pcpu;
109*4882a593Smuzhiyun 	if (unlikely(!flush)) {
110*4882a593Smuzhiyun 		local_irq_restore(flags);
111*4882a593Smuzhiyun 		goto fault;
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	flush->address_space = as;
115*4882a593Smuzhiyun 	flush->flags = 0;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	gpa_n = fill_flush_list_func(flush, data);
118*4882a593Smuzhiyun 	if (gpa_n < 0) {
119*4882a593Smuzhiyun 		local_irq_restore(flags);
120*4882a593Smuzhiyun 		goto fault;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	status = hv_do_rep_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST,
124*4882a593Smuzhiyun 				     gpa_n, 0, flush, NULL);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	local_irq_restore(flags);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	if (!(status & HV_HYPERCALL_RESULT_MASK))
129*4882a593Smuzhiyun 		ret = 0;
130*4882a593Smuzhiyun 	else
131*4882a593Smuzhiyun 		ret = status;
132*4882a593Smuzhiyun fault:
133*4882a593Smuzhiyun 	trace_hyperv_nested_flush_guest_mapping_range(as, ret);
134*4882a593Smuzhiyun 	return ret;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping_range);
137