blob: 62569ca438577d8d77af175b582374b9d98da20c [file] [log] [blame]
Joao Martins23200b72018-06-13 09:55:44 -04001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright © 2019 Oracle and/or its affiliates. All rights reserved.
4 * Copyright © 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 *
6 * KVM Xen emulation
7 */
8
9#include "x86.h"
10#include "xen.h"
11
12#include <linux/kvm_host.h>
13
14#include <trace/events/kvm.h>
15
16#include "trace.h"
17
18int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
19{
20 struct kvm *kvm = vcpu->kvm;
21 u32 page_num = data & ~PAGE_MASK;
22 u64 page_addr = data & PAGE_MASK;
23
24 /*
25 * If Xen hypercall intercept is enabled, fill the hypercall
26 * page with VMCALL/VMMCALL instructions since that's what
27 * we catch. Else the VMM has provided the hypercall pages
28 * with instructions of its own choosing, so use those.
29 */
30 if (kvm_xen_hypercall_enabled(kvm)) {
31 u8 instructions[32];
32 int i;
33
34 if (page_num)
35 return 1;
36
37 /* mov imm32, %eax */
38 instructions[0] = 0xb8;
39
40 /* vmcall / vmmcall */
41 kvm_x86_ops.patch_hypercall(vcpu, instructions + 5);
42
43 /* ret */
44 instructions[8] = 0xc3;
45
46 /* int3 to pad */
47 memset(instructions + 9, 0xcc, sizeof(instructions) - 9);
48
49 for (i = 0; i < PAGE_SIZE / sizeof(instructions); i++) {
50 *(u32 *)&instructions[1] = i;
51 if (kvm_vcpu_write_guest(vcpu,
52 page_addr + (i * sizeof(instructions)),
53 instructions, sizeof(instructions)))
54 return 1;
55 }
56 } else {
57 int lm = is_long_mode(vcpu);
58 u64 blob_addr = lm ? kvm->arch.xen_hvm_config.blob_addr_64
59 : kvm->arch.xen_hvm_config.blob_addr_32;
60 u8 blob_size = lm ? kvm->arch.xen_hvm_config.blob_size_64
61 : kvm->arch.xen_hvm_config.blob_size_32;
62 u8 *page;
63
64 if (page_num >= blob_size)
65 return 1;
66
67 blob_addr += page_num * PAGE_SIZE;
68
69 page = memdup_user((u8 __user *)blob_addr, PAGE_SIZE);
70 if (IS_ERR(page))
71 return PTR_ERR(page);
72
73 if (kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE)) {
74 kfree(page);
75 return 1;
76 }
77 }
78 return 0;
79}
80
81static int kvm_xen_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result)
82{
83 kvm_rax_write(vcpu, result);
84 return kvm_skip_emulated_instruction(vcpu);
85}
86
87static int kvm_xen_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
88{
89 struct kvm_run *run = vcpu->run;
90
91 if (unlikely(!kvm_is_linear_rip(vcpu, vcpu->arch.xen.hypercall_rip)))
92 return 1;
93
94 return kvm_xen_hypercall_set_result(vcpu, run->xen.u.hcall.result);
95}
96
97int kvm_xen_hypercall(struct kvm_vcpu *vcpu)
98{
99 bool longmode;
100 u64 input, params[6];
101
102 input = (u64)kvm_register_read(vcpu, VCPU_REGS_RAX);
103
104 longmode = is_64_bit_mode(vcpu);
105 if (!longmode) {
106 params[0] = (u32)kvm_rbx_read(vcpu);
107 params[1] = (u32)kvm_rcx_read(vcpu);
108 params[2] = (u32)kvm_rdx_read(vcpu);
109 params[3] = (u32)kvm_rsi_read(vcpu);
110 params[4] = (u32)kvm_rdi_read(vcpu);
111 params[5] = (u32)kvm_rbp_read(vcpu);
112 }
113#ifdef CONFIG_X86_64
114 else {
115 params[0] = (u64)kvm_rdi_read(vcpu);
116 params[1] = (u64)kvm_rsi_read(vcpu);
117 params[2] = (u64)kvm_rdx_read(vcpu);
118 params[3] = (u64)kvm_r10_read(vcpu);
119 params[4] = (u64)kvm_r8_read(vcpu);
120 params[5] = (u64)kvm_r9_read(vcpu);
121 }
122#endif
123 trace_kvm_xen_hypercall(input, params[0], params[1], params[2],
124 params[3], params[4], params[5]);
125
126 vcpu->run->exit_reason = KVM_EXIT_XEN;
127 vcpu->run->xen.type = KVM_EXIT_XEN_HCALL;
128 vcpu->run->xen.u.hcall.longmode = longmode;
129 vcpu->run->xen.u.hcall.cpl = kvm_x86_ops.get_cpl(vcpu);
130 vcpu->run->xen.u.hcall.input = input;
131 vcpu->run->xen.u.hcall.params[0] = params[0];
132 vcpu->run->xen.u.hcall.params[1] = params[1];
133 vcpu->run->xen.u.hcall.params[2] = params[2];
134 vcpu->run->xen.u.hcall.params[3] = params[3];
135 vcpu->run->xen.u.hcall.params[4] = params[4];
136 vcpu->run->xen.u.hcall.params[5] = params[5];
137 vcpu->arch.xen.hypercall_rip = kvm_get_linear_rip(vcpu);
138 vcpu->arch.complete_userspace_io =
139 kvm_xen_hypercall_complete_userspace;
140
141 return 0;
142}