]>
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> | |
15 | #include <asm/esr.h> | |
16 | ||
17 | #define PSTATE_FAULT_BITS_64 (PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | \ | |
18 | PSR_I_BIT | PSR_D_BIT) | |
8fc153cd MZ |
19 | |
20 | #define CURRENT_EL_SP_EL0_VECTOR 0x0 | |
21 | #define CURRENT_EL_SP_ELx_VECTOR 0x200 | |
22 | #define LOWER_EL_AArch64_VECTOR 0x400 | |
23 | #define LOWER_EL_AArch32_VECTOR 0x600 | |
aa8eff9b | 24 | |
8fc153cd MZ |
25 | enum exception_type { |
26 | except_type_sync = 0, | |
27 | except_type_irq = 0x80, | |
28 | except_type_fiq = 0x100, | |
29 | except_type_serror = 0x180, | |
30 | }; | |
31 | ||
32 | static u64 get_except_vector(struct kvm_vcpu *vcpu, enum exception_type type) | |
33 | { | |
34 | u64 exc_offset; | |
35 | ||
36 | switch (*vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT)) { | |
37 | case PSR_MODE_EL1t: | |
38 | exc_offset = CURRENT_EL_SP_EL0_VECTOR; | |
39 | break; | |
40 | case PSR_MODE_EL1h: | |
41 | exc_offset = CURRENT_EL_SP_ELx_VECTOR; | |
42 | break; | |
43 | case PSR_MODE_EL0t: | |
44 | exc_offset = LOWER_EL_AArch64_VECTOR; | |
45 | break; | |
46 | default: | |
47 | exc_offset = LOWER_EL_AArch32_VECTOR; | |
48 | } | |
49 | ||
8d404c4c | 50 | return vcpu_read_sys_reg(vcpu, VBAR_EL1) + exc_offset + type; |
8fc153cd MZ |
51 | } |
52 | ||
aa8eff9b MZ |
53 | static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) |
54 | { | |
55 | unsigned long cpsr = *vcpu_cpsr(vcpu); | |
89581f06 | 56 | bool is_aarch32 = vcpu_mode_is_32bit(vcpu); |
aa8eff9b MZ |
57 | u32 esr = 0; |
58 | ||
6d4bd909 | 59 | vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu)); |
8fc153cd | 60 | *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); |
89581f06 | 61 | |
aa8eff9b | 62 | *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; |
00536ec4 | 63 | vcpu_write_spsr(vcpu, cpsr); |
aa8eff9b | 64 | |
8d404c4c | 65 | vcpu_write_sys_reg(vcpu, addr, FAR_EL1); |
aa8eff9b MZ |
66 | |
67 | /* | |
68 | * Build an {i,d}abort, depending on the level and the | |
69 | * instruction set. Report an external synchronous abort. | |
70 | */ | |
71 | if (kvm_vcpu_trap_il_is32bit(vcpu)) | |
c6d01a94 | 72 | esr |= ESR_ELx_IL; |
aa8eff9b MZ |
73 | |
74 | /* | |
75 | * Here, the guest runs in AArch64 mode when in EL1. If we get | |
76 | * an AArch32 fault, it means we managed to trap an EL0 fault. | |
77 | */ | |
78 | if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t) | |
c6d01a94 | 79 | esr |= (ESR_ELx_EC_IABT_LOW << ESR_ELx_EC_SHIFT); |
aa8eff9b | 80 | else |
c6d01a94 | 81 | esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT); |
aa8eff9b MZ |
82 | |
83 | if (!is_iabt) | |
e4fe9e7d | 84 | esr |= ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT; |
aa8eff9b | 85 | |
8d404c4c | 86 | vcpu_write_sys_reg(vcpu, esr | ESR_ELx_FSC_EXTABT, ESR_EL1); |
aa8eff9b MZ |
87 | } |
88 | ||
89 | static void inject_undef64(struct kvm_vcpu *vcpu) | |
90 | { | |
91 | unsigned long cpsr = *vcpu_cpsr(vcpu); | |
c6d01a94 | 92 | u32 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); |
aa8eff9b | 93 | |
6d4bd909 | 94 | vcpu_write_elr_el1(vcpu, *vcpu_pc(vcpu)); |
8fc153cd | 95 | *vcpu_pc(vcpu) = get_except_vector(vcpu, except_type_sync); |
89581f06 | 96 | |
aa8eff9b | 97 | *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; |
00536ec4 | 98 | vcpu_write_spsr(vcpu, cpsr); |
aa8eff9b MZ |
99 | |
100 | /* | |
101 | * Build an unknown exception, depending on the instruction | |
102 | * set. | |
103 | */ | |
104 | if (kvm_vcpu_trap_il_is32bit(vcpu)) | |
c6d01a94 | 105 | esr |= ESR_ELx_IL; |
aa8eff9b | 106 | |
8d404c4c | 107 | vcpu_write_sys_reg(vcpu, esr, ESR_EL1); |
aa8eff9b MZ |
108 | } |
109 | ||
110 | /** | |
111 | * kvm_inject_dabt - inject a data abort into the guest | |
112 | * @vcpu: The VCPU to receive the undefined exception | |
113 | * @addr: The address to report in the DFAR | |
114 | * | |
115 | * It is assumed that this code is called from the VCPU thread and that the | |
116 | * VCPU therefore is not currently executing guest code. | |
117 | */ | |
118 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) | |
119 | { | |
e72341c5 | 120 | if (vcpu_el1_is_32bit(vcpu)) |
74a64a98 | 121 | kvm_inject_dabt32(vcpu, addr); |
126c69a0 MZ |
122 | else |
123 | inject_abt64(vcpu, false, addr); | |
aa8eff9b MZ |
124 | } |
125 | ||
126 | /** | |
127 | * kvm_inject_pabt - inject a prefetch abort into the guest | |
128 | * @vcpu: The VCPU to receive the undefined exception | |
129 | * @addr: The address to report in the DFAR | |
130 | * | |
131 | * It is assumed that this code is called from the VCPU thread and that the | |
132 | * VCPU therefore is not currently executing guest code. | |
133 | */ | |
134 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) | |
135 | { | |
e72341c5 | 136 | if (vcpu_el1_is_32bit(vcpu)) |
74a64a98 | 137 | kvm_inject_pabt32(vcpu, addr); |
126c69a0 MZ |
138 | else |
139 | inject_abt64(vcpu, true, addr); | |
aa8eff9b MZ |
140 | } |
141 | ||
142 | /** | |
143 | * kvm_inject_undefined - inject an undefined instruction into the guest | |
144 | * | |
145 | * It is assumed that this code is called from the VCPU thread and that the | |
146 | * VCPU therefore is not currently executing guest code. | |
147 | */ | |
148 | void kvm_inject_undefined(struct kvm_vcpu *vcpu) | |
149 | { | |
e72341c5 | 150 | if (vcpu_el1_is_32bit(vcpu)) |
74a64a98 | 151 | kvm_inject_undef32(vcpu); |
126c69a0 MZ |
152 | else |
153 | inject_undef64(vcpu); | |
aa8eff9b | 154 | } |
10cf3390 | 155 | |
b7b27fac | 156 | void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 esr) |
4715c14b | 157 | { |
b7b27fac | 158 | vcpu_set_vsesr(vcpu, esr & ESR_ELx_ISS_MASK); |
3df59d8d | 159 | *vcpu_hcr(vcpu) |= HCR_VSE; |
4715c14b JM |
160 | } |
161 | ||
10cf3390 MZ |
162 | /** |
163 | * kvm_inject_vabt - inject an async abort / SError into the guest | |
164 | * @vcpu: The VCPU to receive the exception | |
165 | * | |
166 | * It is assumed that this code is called from the VCPU thread and that the | |
167 | * VCPU therefore is not currently executing guest code. | |
4715c14b JM |
168 | * |
169 | * Systems with the RAS Extensions specify an imp-def ESR (ISV/IDS = 1) with | |
170 | * the remaining ISS all-zeros so that this error is not interpreted as an | |
171 | * uncategorized RAS error. Without the RAS Extensions we can't specify an ESR | |
172 | * value, so the CPU generates an imp-def value. | |
10cf3390 MZ |
173 | */ |
174 | void kvm_inject_vabt(struct kvm_vcpu *vcpu) | |
175 | { | |
b7b27fac | 176 | kvm_set_sei_esr(vcpu, ESR_ELx_ISV); |
10cf3390 | 177 | } |