Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
aa8eff9b | 2 | /* |
e82e0305 | 3 | * Fault injection for both 32 and 64bit guests. |
aa8eff9b MZ |
4 | * |
5 | * Copyright (C) 2012,2013 - ARM Ltd | |
6 | * Author: Marc Zyngier <marc.zyngier@arm.com> | |
7 | * | |
8 | * Based on arch/arm/kvm/emulate.c | |
9 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | |
10 | * Author: Christoffer Dall <c.dall@virtualopensystems.com> | |
aa8eff9b MZ |
11 | */ |
12 | ||
13 | #include <linux/kvm_host.h> | |
14 | #include <asm/kvm_emulate.h> | |
47f3a2fc | 15 | #include <asm/kvm_nested.h> |
aa8eff9b MZ |
16 | #include <asm/esr.h> |
17 | ||
47f3a2fc JL |
18 | static void pend_sync_exception(struct kvm_vcpu *vcpu) |
19 | { | |
20 | /* If not nesting, EL1 is the only possible exception target */ | |
21 | if (likely(!vcpu_has_nv(vcpu))) { | |
22 | kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); | |
23 | return; | |
24 | } | |
25 | ||
26 | /* | |
27 | * With NV, we need to pick between EL1 and EL2. Note that we | |
28 | * never deal with a nesting exception here, hence never | |
29 | * changing context, and the exception itself can be delayed | |
30 | * until the next entry. | |
31 | */ | |
32 | switch(*vcpu_cpsr(vcpu) & PSR_MODE_MASK) { | |
33 | case PSR_MODE_EL2h: | |
34 | case PSR_MODE_EL2t: | |
35 | kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); | |
36 | break; | |
37 | case PSR_MODE_EL1h: | |
38 | case PSR_MODE_EL1t: | |
39 | kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); | |
40 | break; | |
41 | case PSR_MODE_EL0t: | |
42 | if (vcpu_el2_tge_is_set(vcpu)) | |
43 | kvm_pend_exception(vcpu, EXCEPT_AA64_EL2_SYNC); | |
44 | else | |
45 | kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); | |
46 | break; | |
47 | default: | |
48 | BUG(); | |
49 | } | |
50 | } | |
51 | ||
52 | static bool match_target_el(struct kvm_vcpu *vcpu, unsigned long target) | |
53 | { | |
54 | return (vcpu_get_flag(vcpu, EXCEPT_MASK) == target); | |
55 | } | |
56 | ||
aa8eff9b MZ |
57 | static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) |
58 | { | |
59 | unsigned long cpsr = *vcpu_cpsr(vcpu); | |
89581f06 | 60 | bool is_aarch32 = vcpu_mode_is_32bit(vcpu); |
0b12620f | 61 | u64 esr = 0; |
aa8eff9b | 62 | |
47f3a2fc | 63 | pend_sync_exception(vcpu); |
aa8eff9b MZ |
64 | |
65 | /* | |
66 | * Build an {i,d}abort, depending on the level and the | |
67 | * instruction set. Report an external synchronous abort. | |
68 | */ | |
69 | if (kvm_vcpu_trap_il_is32bit(vcpu)) | |
c6d01a94 | 70 | esr |= ESR_ELx_IL; |
aa8eff9b MZ |
71 | |
72 | /* | |
73 | * Here, the guest runs in AArch64 mode when in EL1. If we get | |
74 | * an AArch32 fault, it means we managed to trap an EL0 fault. | |
75 | */ | |
76 | if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t) | |
c6d01a94 | 77 | esr |= (ESR_ELx_EC_IABT_LOW << ESR_ELx_EC_SHIFT); |
aa8eff9b | 78 | else |
c6d01a94 | 79 | esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT); |
aa8eff9b MZ |
80 | |
81 | if (!is_iabt) | |
e4fe9e7d | 82 | esr |= ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT; |
aa8eff9b | 83 | |
47f3a2fc JL |
84 | esr |= ESR_ELx_FSC_EXTABT; |
85 | ||
86 | if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) { | |
87 | vcpu_write_sys_reg(vcpu, addr, FAR_EL1); | |
88 | vcpu_write_sys_reg(vcpu, esr, ESR_EL1); | |
89 | } else { | |
90 | vcpu_write_sys_reg(vcpu, addr, FAR_EL2); | |
91 | vcpu_write_sys_reg(vcpu, esr, ESR_EL2); | |
92 | } | |
aa8eff9b MZ |
93 | } |
94 | ||
95 | static void inject_undef64(struct kvm_vcpu *vcpu) | |
96 | { | |
0b12620f | 97 | u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); |
aa8eff9b | 98 | |
47f3a2fc | 99 | pend_sync_exception(vcpu); |
aa8eff9b MZ |
100 | |
101 | /* | |
102 | * Build an unknown exception, depending on the instruction | |
103 | * set. | |
104 | */ | |
105 | if (kvm_vcpu_trap_il_is32bit(vcpu)) | |
c6d01a94 | 106 | esr |= ESR_ELx_IL; |
aa8eff9b | 107 | |
47f3a2fc JL |
108 | if (match_target_el(vcpu, unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC))) |
109 | vcpu_write_sys_reg(vcpu, esr, ESR_EL1); | |
110 | else | |
111 | vcpu_write_sys_reg(vcpu, esr, ESR_EL2); | |
aa8eff9b MZ |
112 | } |
113 | ||
dcfba399 MZ |
114 | #define DFSR_FSC_EXTABT_LPAE 0x10 |
115 | #define DFSR_FSC_EXTABT_nLPAE 0x08 | |
116 | #define DFSR_LPAE BIT(9) | |
4ff3fc31 | 117 | #define TTBCR_EAE BIT(31) |
dcfba399 MZ |
118 | |
119 | static void inject_undef32(struct kvm_vcpu *vcpu) | |
120 | { | |
699bb2e0 | 121 | kvm_pend_exception(vcpu, EXCEPT_AA32_UND); |
dcfba399 MZ |
122 | } |
123 | ||
124 | /* | |
125 | * Modelled after TakeDataAbortException() and TakePrefetchAbortException | |
126 | * pseudocode. | |
127 | */ | |
4ff3fc31 | 128 | static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr) |
dcfba399 | 129 | { |
4ff3fc31 MZ |
130 | u64 far; |
131 | u32 fsr; | |
132 | ||
133 | /* Give the guest an IMPLEMENTATION DEFINED exception */ | |
134 | if (vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE) { | |
135 | fsr = DFSR_LPAE | DFSR_FSC_EXTABT_LPAE; | |
136 | } else { | |
75841d89 | 137 | /* no need to shuffle FS[4] into DFSR[10] as it's 0 */ |
4ff3fc31 MZ |
138 | fsr = DFSR_FSC_EXTABT_nLPAE; |
139 | } | |
dcfba399 | 140 | |
4ff3fc31 | 141 | far = vcpu_read_sys_reg(vcpu, FAR_EL1); |
dcfba399 MZ |
142 | |
143 | if (is_pabt) { | |
699bb2e0 | 144 | kvm_pend_exception(vcpu, EXCEPT_AA32_IABT); |
4ff3fc31 MZ |
145 | far &= GENMASK(31, 0); |
146 | far |= (u64)addr << 32; | |
147 | vcpu_write_sys_reg(vcpu, fsr, IFSR32_EL2); | |
dcfba399 | 148 | } else { /* !iabt */ |
699bb2e0 | 149 | kvm_pend_exception(vcpu, EXCEPT_AA32_DABT); |
4ff3fc31 MZ |
150 | far &= GENMASK(63, 32); |
151 | far |= addr; | |
152 | vcpu_write_sys_reg(vcpu, fsr, ESR_EL1); | |
dcfba399 MZ |
153 | } |
154 | ||
4ff3fc31 | 155 | vcpu_write_sys_reg(vcpu, far, FAR_EL1); |
dcfba399 MZ |
156 | } |
157 | ||
aa8eff9b MZ |
158 | /** |
159 | * kvm_inject_dabt - inject a data abort into the guest | |
da345174 | 160 | * @vcpu: The VCPU to receive the data abort |
aa8eff9b MZ |
161 | * @addr: The address to report in the DFAR |
162 | * | |
163 | * It is assumed that this code is called from the VCPU thread and that the | |
164 | * VCPU therefore is not currently executing guest code. | |
165 | */ | |
166 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) | |
167 | { | |
e72341c5 | 168 | if (vcpu_el1_is_32bit(vcpu)) |
dcfba399 | 169 | inject_abt32(vcpu, false, addr); |
126c69a0 MZ |
170 | else |
171 | inject_abt64(vcpu, false, addr); | |
aa8eff9b MZ |
172 | } |
173 | ||
174 | /** | |
175 | * kvm_inject_pabt - inject a prefetch abort into the guest | |
da345174 | 176 | * @vcpu: The VCPU to receive the prefetch abort |
aa8eff9b MZ |
177 | * @addr: The address to report in the DFAR |
178 | * | |
179 | * It is assumed that this code is called from the VCPU thread and that the | |
180 | * VCPU therefore is not currently executing guest code. | |
181 | */ | |
182 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) | |
183 | { | |
e72341c5 | 184 | if (vcpu_el1_is_32bit(vcpu)) |
dcfba399 | 185 | inject_abt32(vcpu, true, addr); |
126c69a0 MZ |
186 | else |
187 | inject_abt64(vcpu, true, addr); | |
aa8eff9b MZ |
188 | } |
189 | ||
85ea6b1e MZ |
190 | void kvm_inject_size_fault(struct kvm_vcpu *vcpu) |
191 | { | |
192 | unsigned long addr, esr; | |
193 | ||
194 | addr = kvm_vcpu_get_fault_ipa(vcpu); | |
195 | addr |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0); | |
196 | ||
197 | if (kvm_vcpu_trap_is_iabt(vcpu)) | |
198 | kvm_inject_pabt(vcpu, addr); | |
199 | else | |
200 | kvm_inject_dabt(vcpu, addr); | |
201 | ||
202 | /* | |
203 | * If AArch64 or LPAE, set FSC to 0 to indicate an Address | |
204 | * Size Fault at level 0, as if exceeding PARange. | |
205 | * | |
206 | * Non-LPAE guests will only get the external abort, as there | |
3d179356 | 207 | * is no way to describe the ASF. |
85ea6b1e MZ |
208 | */ |
209 | if (vcpu_el1_is_32bit(vcpu) && | |
210 | !(vcpu_read_sys_reg(vcpu, TCR_EL1) & TTBCR_EAE)) | |
211 | return; | |
212 | ||
213 | esr = vcpu_read_sys_reg(vcpu, ESR_EL1); | |
214 | esr &= ~GENMASK_ULL(5, 0); | |
215 | vcpu_write_sys_reg(vcpu, esr, ESR_EL1); | |
216 | } | |
217 | ||
aa8eff9b MZ |
218 | /** |
219 | * kvm_inject_undefined - inject an undefined instruction into the guest | |
8a4374f9 | 220 | * @vcpu: The vCPU in which to inject the exception |
aa8eff9b MZ |
221 | * |
222 | * It is assumed that this code is called from the VCPU thread and that the | |
223 | * VCPU therefore is not currently executing guest code. | |
224 | */ | |
225 | void kvm_inject_undefined(struct kvm_vcpu *vcpu) | |
226 | { | |
e72341c5 | 227 | if (vcpu_el1_is_32bit(vcpu)) |
dcfba399 | 228 | inject_undef32(vcpu); |
126c69a0 MZ |
229 | else |
230 | inject_undef64(vcpu); | |
aa8eff9b | 231 | } |
10cf3390 | 232 | |
b7b27fac | 233 | void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 esr) |
4715c14b | 234 | { |
b7b27fac | 235 | vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK); |
3df59d8d | 236 | *vcpu_hcr(vcpu) |= HCR_VSE; |
4715c14b JM |
237 | } |
238 | ||
10cf3390 MZ |
239 | /** |
240 | * kvm_inject_vabt - inject an async abort / SError into the guest | |
241 | * @vcpu: The VCPU to receive the exception | |
242 | * | |
243 | * It is assumed that this code is called from the VCPU thread and that the | |
244 | * VCPU therefore is not currently executing guest code. | |
4715c14b JM |
245 | * |
246 | * Systems with the RAS Extensions specify an imp-def ESR (ISV/IDS = 1) with | |
247 | * the remaining ISS all-zeros so that this error is not interpreted as an | |
248 | * uncategorized RAS error. Without the RAS Extensions we can't specify an ESR | |
249 | * value, so the CPU generates an imp-def value. | |
10cf3390 MZ |
250 | */ |
251 | void kvm_inject_vabt(struct kvm_vcpu *vcpu) | |
252 | { | |
b7b27fac | 253 | kvm_set_sei_esr(vcpu, ESR_ELx_ISV); |
10cf3390 | 254 | } |