1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/kvm.h>
23#include <linux/kvm_host.h>
24
25#include <asm/esr.h>
26#include <asm/kvm_asm.h>
27#include <asm/kvm_coproc.h>
28#include <asm/kvm_emulate.h>
29#include <asm/kvm_mmu.h>
30#include <asm/kvm_psci.h>
31
32#define CREATE_TRACE_POINTS
33#include "trace.h"
34
35typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
36
37static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
38{
39 int ret;
40
41 trace_kvm_hvc_arm64(*vcpu_pc(vcpu), vcpu_get_reg(vcpu, 0),
42 kvm_vcpu_hvc_get_imm(vcpu));
43 vcpu->stat.hvc_exit_stat++;
44
45 ret = kvm_psci_call(vcpu);
46 if (ret < 0) {
47 kvm_inject_undefined(vcpu);
48 return 1;
49 }
50
51 return ret;
52}
53
54static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
55{
56 kvm_inject_undefined(vcpu);
57 return 1;
58}
59
60
61
62
63
64
65
66
67
68
69
70
71
72static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
73{
74 if (kvm_vcpu_get_hsr(vcpu) & ESR_ELx_WFx_ISS_WFE) {
75 trace_kvm_wfx_arm64(*vcpu_pc(vcpu), true);
76 vcpu->stat.wfe_exit_stat++;
77 kvm_vcpu_on_spin(vcpu);
78 } else {
79 trace_kvm_wfx_arm64(*vcpu_pc(vcpu), false);
80 vcpu->stat.wfi_exit_stat++;
81 kvm_vcpu_block(vcpu);
82 }
83
84 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
85
86 return 1;
87}
88
89
90
91
92
93
94
95
96
97
98
99
100
101static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu, struct kvm_run *run)
102{
103 u32 hsr = kvm_vcpu_get_hsr(vcpu);
104 int ret = 0;
105
106 run->exit_reason = KVM_EXIT_DEBUG;
107 run->debug.arch.hsr = hsr;
108
109 switch (ESR_ELx_EC(hsr)) {
110 case ESR_ELx_EC_WATCHPT_LOW:
111 run->debug.arch.far = vcpu->arch.fault.far_el2;
112
113 case ESR_ELx_EC_SOFTSTP_LOW:
114 case ESR_ELx_EC_BREAKPT_LOW:
115 case ESR_ELx_EC_BKPT32:
116 case ESR_ELx_EC_BRK64:
117 break;
118 default:
119 kvm_err("%s: un-handled case hsr: %#08x\n",
120 __func__, (unsigned int) hsr);
121 ret = -1;
122 break;
123 }
124
125 return ret;
126}
127
128static exit_handle_fn arm_exit_handlers[] = {
129 [ESR_ELx_EC_WFx] = kvm_handle_wfx,
130 [ESR_ELx_EC_CP15_32] = kvm_handle_cp15_32,
131 [ESR_ELx_EC_CP15_64] = kvm_handle_cp15_64,
132 [ESR_ELx_EC_CP14_MR] = kvm_handle_cp14_32,
133 [ESR_ELx_EC_CP14_LS] = kvm_handle_cp14_load_store,
134 [ESR_ELx_EC_CP14_64] = kvm_handle_cp14_64,
135 [ESR_ELx_EC_HVC32] = handle_hvc,
136 [ESR_ELx_EC_SMC32] = handle_smc,
137 [ESR_ELx_EC_HVC64] = handle_hvc,
138 [ESR_ELx_EC_SMC64] = handle_smc,
139 [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg,
140 [ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort,
141 [ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort,
142 [ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug,
143 [ESR_ELx_EC_WATCHPT_LOW]= kvm_handle_guest_debug,
144 [ESR_ELx_EC_BREAKPT_LOW]= kvm_handle_guest_debug,
145 [ESR_ELx_EC_BKPT32] = kvm_handle_guest_debug,
146 [ESR_ELx_EC_BRK64] = kvm_handle_guest_debug,
147};
148
149static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
150{
151 u32 hsr = kvm_vcpu_get_hsr(vcpu);
152 u8 hsr_ec = ESR_ELx_EC(hsr);
153
154 if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) ||
155 !arm_exit_handlers[hsr_ec]) {
156 kvm_err("Unknown exception class: hsr: %#08x -- %s\n",
157 hsr, esr_get_class_string(hsr));
158 BUG();
159 }
160
161 return arm_exit_handlers[hsr_ec];
162}
163
164
165
166
167
168int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
169 int exception_index)
170{
171 exit_handle_fn exit_handler;
172
173 if (ARM_SERROR_PENDING(exception_index)) {
174 u8 hsr_ec = ESR_ELx_EC(kvm_vcpu_get_hsr(vcpu));
175
176
177
178
179
180
181 if (hsr_ec == ESR_ELx_EC_HVC32 || hsr_ec == ESR_ELx_EC_HVC64 ||
182 hsr_ec == ESR_ELx_EC_SMC32 || hsr_ec == ESR_ELx_EC_SMC64) {
183 u32 adj = kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
184 *vcpu_pc(vcpu) -= adj;
185 }
186
187 kvm_inject_vabt(vcpu);
188 return 1;
189 }
190
191 exception_index = ARM_EXCEPTION_CODE(exception_index);
192
193 switch (exception_index) {
194 case ARM_EXCEPTION_IRQ:
195 return 1;
196 case ARM_EXCEPTION_EL1_SERROR:
197 kvm_inject_vabt(vcpu);
198 return 1;
199 case ARM_EXCEPTION_TRAP:
200
201
202
203
204 if (!kvm_condition_valid(vcpu)) {
205 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
206 return 1;
207 }
208
209 exit_handler = kvm_get_exit_handler(vcpu);
210
211 return exit_handler(vcpu, run);
212 case ARM_EXCEPTION_HYP_GONE:
213
214
215
216
217 run->exit_reason = KVM_EXIT_FAIL_ENTRY;
218 return 0;
219 default:
220 kvm_pr_unimpl("Unsupported exception type: %d",
221 exception_index);
222 run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
223 return 0;
224 }
225}
226