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