]> git.ipfire.org Git - thirdparty/linux.git/blob - arch/x86/hyperv/ivm.c
Merge tag 'objtool-core-2023-04-27' of git://git.kernel.org/pub/scm/linux/kernel...
[thirdparty/linux.git] / arch / x86 / hyperv / ivm.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Hyper-V Isolation VM interface with paravisor and hypervisor
4 *
5 * Author:
6 * Tianyu Lan <Tianyu.Lan@microsoft.com>
7 */
8
9 #include <linux/bitfield.h>
10 #include <linux/hyperv.h>
11 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <asm/svm.h>
14 #include <asm/sev.h>
15 #include <asm/io.h>
16 #include <asm/coco.h>
17 #include <asm/mem_encrypt.h>
18 #include <asm/mshyperv.h>
19 #include <asm/hypervisor.h>
20
21 #ifdef CONFIG_AMD_MEM_ENCRYPT
22
23 #define GHCB_USAGE_HYPERV_CALL 1
24
25 union hv_ghcb {
26 struct ghcb ghcb;
27 struct {
28 u64 hypercalldata[509];
29 u64 outputgpa;
30 union {
31 union {
32 struct {
33 u32 callcode : 16;
34 u32 isfast : 1;
35 u32 reserved1 : 14;
36 u32 isnested : 1;
37 u32 countofelements : 12;
38 u32 reserved2 : 4;
39 u32 repstartindex : 12;
40 u32 reserved3 : 4;
41 };
42 u64 asuint64;
43 } hypercallinput;
44 union {
45 struct {
46 u16 callstatus;
47 u16 reserved1;
48 u32 elementsprocessed : 12;
49 u32 reserved2 : 20;
50 };
51 u64 asunit64;
52 } hypercalloutput;
53 };
54 u64 reserved2;
55 } hypercall;
56 } __packed __aligned(HV_HYP_PAGE_SIZE);
57
58 static u16 hv_ghcb_version __ro_after_init;
59
60 u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
61 {
62 union hv_ghcb *hv_ghcb;
63 void **ghcb_base;
64 unsigned long flags;
65 u64 status;
66
67 if (!hv_ghcb_pg)
68 return -EFAULT;
69
70 WARN_ON(in_nmi());
71
72 local_irq_save(flags);
73 ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
74 hv_ghcb = (union hv_ghcb *)*ghcb_base;
75 if (!hv_ghcb) {
76 local_irq_restore(flags);
77 return -EFAULT;
78 }
79
80 hv_ghcb->ghcb.protocol_version = GHCB_PROTOCOL_MAX;
81 hv_ghcb->ghcb.ghcb_usage = GHCB_USAGE_HYPERV_CALL;
82
83 hv_ghcb->hypercall.outputgpa = (u64)output;
84 hv_ghcb->hypercall.hypercallinput.asuint64 = 0;
85 hv_ghcb->hypercall.hypercallinput.callcode = control;
86
87 if (input_size)
88 memcpy(hv_ghcb->hypercall.hypercalldata, input, input_size);
89
90 VMGEXIT();
91
92 hv_ghcb->ghcb.ghcb_usage = 0xffffffff;
93 memset(hv_ghcb->ghcb.save.valid_bitmap, 0,
94 sizeof(hv_ghcb->ghcb.save.valid_bitmap));
95
96 status = hv_ghcb->hypercall.hypercalloutput.callstatus;
97
98 local_irq_restore(flags);
99
100 return status;
101 }
102
103 static inline u64 rd_ghcb_msr(void)
104 {
105 return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
106 }
107
108 static inline void wr_ghcb_msr(u64 val)
109 {
110 native_wrmsrl(MSR_AMD64_SEV_ES_GHCB, val);
111 }
112
113 static enum es_result hv_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
114 u64 exit_info_1, u64 exit_info_2)
115 {
116 /* Fill in protocol and format specifiers */
117 ghcb->protocol_version = hv_ghcb_version;
118 ghcb->ghcb_usage = GHCB_DEFAULT_USAGE;
119
120 ghcb_set_sw_exit_code(ghcb, exit_code);
121 ghcb_set_sw_exit_info_1(ghcb, exit_info_1);
122 ghcb_set_sw_exit_info_2(ghcb, exit_info_2);
123
124 VMGEXIT();
125
126 if (ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0))
127 return ES_VMM_ERROR;
128 else
129 return ES_OK;
130 }
131
132 void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason)
133 {
134 u64 val = GHCB_MSR_TERM_REQ;
135
136 /* Tell the hypervisor what went wrong. */
137 val |= GHCB_SEV_TERM_REASON(set, reason);
138
139 /* Request Guest Termination from Hypvervisor */
140 wr_ghcb_msr(val);
141 VMGEXIT();
142
143 while (true)
144 asm volatile("hlt\n" : : : "memory");
145 }
146
147 bool hv_ghcb_negotiate_protocol(void)
148 {
149 u64 ghcb_gpa;
150 u64 val;
151
152 /* Save ghcb page gpa. */
153 ghcb_gpa = rd_ghcb_msr();
154
155 /* Do the GHCB protocol version negotiation */
156 wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ);
157 VMGEXIT();
158 val = rd_ghcb_msr();
159
160 if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP)
161 return false;
162
163 if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN ||
164 GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX)
165 return false;
166
167 hv_ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val),
168 GHCB_PROTOCOL_MAX);
169
170 /* Write ghcb page back after negotiating protocol. */
171 wr_ghcb_msr(ghcb_gpa);
172 VMGEXIT();
173
174 return true;
175 }
176
177 void hv_ghcb_msr_write(u64 msr, u64 value)
178 {
179 union hv_ghcb *hv_ghcb;
180 void **ghcb_base;
181 unsigned long flags;
182
183 if (!hv_ghcb_pg)
184 return;
185
186 WARN_ON(in_nmi());
187
188 local_irq_save(flags);
189 ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
190 hv_ghcb = (union hv_ghcb *)*ghcb_base;
191 if (!hv_ghcb) {
192 local_irq_restore(flags);
193 return;
194 }
195
196 ghcb_set_rcx(&hv_ghcb->ghcb, msr);
197 ghcb_set_rax(&hv_ghcb->ghcb, lower_32_bits(value));
198 ghcb_set_rdx(&hv_ghcb->ghcb, upper_32_bits(value));
199
200 if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 1, 0))
201 pr_warn("Fail to write msr via ghcb %llx.\n", msr);
202
203 local_irq_restore(flags);
204 }
205 EXPORT_SYMBOL_GPL(hv_ghcb_msr_write);
206
207 void hv_ghcb_msr_read(u64 msr, u64 *value)
208 {
209 union hv_ghcb *hv_ghcb;
210 void **ghcb_base;
211 unsigned long flags;
212
213 /* Check size of union hv_ghcb here. */
214 BUILD_BUG_ON(sizeof(union hv_ghcb) != HV_HYP_PAGE_SIZE);
215
216 if (!hv_ghcb_pg)
217 return;
218
219 WARN_ON(in_nmi());
220
221 local_irq_save(flags);
222 ghcb_base = (void **)this_cpu_ptr(hv_ghcb_pg);
223 hv_ghcb = (union hv_ghcb *)*ghcb_base;
224 if (!hv_ghcb) {
225 local_irq_restore(flags);
226 return;
227 }
228
229 ghcb_set_rcx(&hv_ghcb->ghcb, msr);
230 if (hv_ghcb_hv_call(&hv_ghcb->ghcb, SVM_EXIT_MSR, 0, 0))
231 pr_warn("Fail to read msr via ghcb %llx.\n", msr);
232 else
233 *value = (u64)lower_32_bits(hv_ghcb->ghcb.save.rax)
234 | ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32);
235 local_irq_restore(flags);
236 }
237 EXPORT_SYMBOL_GPL(hv_ghcb_msr_read);
238
239 /*
240 * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
241 *
242 * In Isolation VM, all guest memory is encrypted from host and guest
243 * needs to set memory visible to host via hvcall before sharing memory
244 * with host.
245 */
246 static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
247 enum hv_mem_host_visibility visibility)
248 {
249 struct hv_gpa_range_for_visibility **input_pcpu, *input;
250 u16 pages_processed;
251 u64 hv_status;
252 unsigned long flags;
253
254 /* no-op if partition isolation is not enabled */
255 if (!hv_is_isolation_supported())
256 return 0;
257
258 if (count > HV_MAX_MODIFY_GPA_REP_COUNT) {
259 pr_err("Hyper-V: GPA count:%d exceeds supported:%lu\n", count,
260 HV_MAX_MODIFY_GPA_REP_COUNT);
261 return -EINVAL;
262 }
263
264 local_irq_save(flags);
265 input_pcpu = (struct hv_gpa_range_for_visibility **)
266 this_cpu_ptr(hyperv_pcpu_input_arg);
267 input = *input_pcpu;
268 if (unlikely(!input)) {
269 local_irq_restore(flags);
270 return -EINVAL;
271 }
272
273 input->partition_id = HV_PARTITION_ID_SELF;
274 input->host_visibility = visibility;
275 input->reserved0 = 0;
276 input->reserved1 = 0;
277 memcpy((void *)input->gpa_page_list, pfn, count * sizeof(*pfn));
278 hv_status = hv_do_rep_hypercall(
279 HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY, count,
280 0, input, &pages_processed);
281 local_irq_restore(flags);
282
283 if (hv_result_success(hv_status))
284 return 0;
285 else
286 return -EFAULT;
287 }
288
289 /*
290 * hv_vtom_set_host_visibility - Set specified memory visible to host.
291 *
292 * In Isolation VM, all guest memory is encrypted from host and guest
293 * needs to set memory visible to host via hvcall before sharing memory
294 * with host. This function works as wrap of hv_mark_gpa_visibility()
295 * with memory base and size.
296 */
297 static bool hv_vtom_set_host_visibility(unsigned long kbuffer, int pagecount, bool enc)
298 {
299 enum hv_mem_host_visibility visibility = enc ?
300 VMBUS_PAGE_NOT_VISIBLE : VMBUS_PAGE_VISIBLE_READ_WRITE;
301 u64 *pfn_array;
302 int ret = 0;
303 bool result = true;
304 int i, pfn;
305
306 pfn_array = kmalloc(HV_HYP_PAGE_SIZE, GFP_KERNEL);
307 if (!pfn_array)
308 return false;
309
310 for (i = 0, pfn = 0; i < pagecount; i++) {
311 pfn_array[pfn] = virt_to_hvpfn((void *)kbuffer + i * HV_HYP_PAGE_SIZE);
312 pfn++;
313
314 if (pfn == HV_MAX_MODIFY_GPA_REP_COUNT || i == pagecount - 1) {
315 ret = hv_mark_gpa_visibility(pfn, pfn_array,
316 visibility);
317 if (ret) {
318 result = false;
319 goto err_free_pfn_array;
320 }
321 pfn = 0;
322 }
323 }
324
325 err_free_pfn_array:
326 kfree(pfn_array);
327 return result;
328 }
329
330 static bool hv_vtom_tlb_flush_required(bool private)
331 {
332 return true;
333 }
334
335 static bool hv_vtom_cache_flush_required(void)
336 {
337 return false;
338 }
339
340 static bool hv_is_private_mmio(u64 addr)
341 {
342 /*
343 * Hyper-V always provides a single IO-APIC in a guest VM.
344 * When a paravisor is used, it is emulated by the paravisor
345 * in the guest context and must be mapped private.
346 */
347 if (addr >= HV_IOAPIC_BASE_ADDRESS &&
348 addr < (HV_IOAPIC_BASE_ADDRESS + PAGE_SIZE))
349 return true;
350
351 /* Same with a vTPM */
352 if (addr >= VTPM_BASE_ADDRESS &&
353 addr < (VTPM_BASE_ADDRESS + PAGE_SIZE))
354 return true;
355
356 return false;
357 }
358
359 void __init hv_vtom_init(void)
360 {
361 /*
362 * By design, a VM using vTOM doesn't see the SEV setting,
363 * so SEV initialization is bypassed and sev_status isn't set.
364 * Set it here to indicate a vTOM VM.
365 */
366 sev_status = MSR_AMD64_SNP_VTOM;
367 cc_set_vendor(CC_VENDOR_AMD);
368 cc_set_mask(ms_hyperv.shared_gpa_boundary);
369 physical_mask &= ms_hyperv.shared_gpa_boundary - 1;
370
371 x86_platform.hyper.is_private_mmio = hv_is_private_mmio;
372 x86_platform.guest.enc_cache_flush_required = hv_vtom_cache_flush_required;
373 x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
374 x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
375 }
376
377 #endif /* CONFIG_AMD_MEM_ENCRYPT */
378
379 enum hv_isolation_type hv_get_isolation_type(void)
380 {
381 if (!(ms_hyperv.priv_high & HV_ISOLATION))
382 return HV_ISOLATION_TYPE_NONE;
383 return FIELD_GET(HV_ISOLATION_TYPE, ms_hyperv.isolation_config_b);
384 }
385 EXPORT_SYMBOL_GPL(hv_get_isolation_type);
386
387 /*
388 * hv_is_isolation_supported - Check system runs in the Hyper-V
389 * isolation VM.
390 */
391 bool hv_is_isolation_supported(void)
392 {
393 if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
394 return false;
395
396 if (!hypervisor_is_type(X86_HYPER_MS_HYPERV))
397 return false;
398
399 return hv_get_isolation_type() != HV_ISOLATION_TYPE_NONE;
400 }
401
402 DEFINE_STATIC_KEY_FALSE(isolation_type_snp);
403
404 /*
405 * hv_isolation_type_snp - Check system runs in the AMD SEV-SNP based
406 * isolation VM.
407 */
408 bool hv_isolation_type_snp(void)
409 {
410 return static_branch_unlikely(&isolation_type_snp);
411 }