]>
Commit | Line | Data |
---|---|---|
27b190bd MZ |
1 | /* |
2 | * (not much of an) Emulation layer for 32bit guests. | |
3 | * | |
4 | * Copyright (C) 2012,2013 - ARM Ltd | |
5 | * Author: Marc Zyngier <marc.zyngier@arm.com> | |
6 | * | |
7 | * based on arch/arm/kvm/emulate.c | |
8 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | |
9 | * Author: Christoffer Dall <c.dall@virtualopensystems.com> | |
10 | * | |
11 | * This program is free software: you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | */ | |
23 | ||
24 | #include <linux/kvm_host.h> | |
25 | #include <asm/kvm_emulate.h> | |
8cebe750 | 26 | #include <asm/kvm_hyp.h> |
27b190bd MZ |
27 | |
28 | /* | |
29 | * stolen from arch/arm/kernel/opcodes.c | |
30 | * | |
31 | * condition code lookup table | |
32 | * index into the table is test code: EQ, NE, ... LT, GT, AL, NV | |
33 | * | |
34 | * bit position in short is condition code: NZCV | |
35 | */ | |
36 | static const unsigned short cc_map[16] = { | |
37 | 0xF0F0, /* EQ == Z set */ | |
38 | 0x0F0F, /* NE */ | |
39 | 0xCCCC, /* CS == C set */ | |
40 | 0x3333, /* CC */ | |
41 | 0xFF00, /* MI == N set */ | |
42 | 0x00FF, /* PL */ | |
43 | 0xAAAA, /* VS == V set */ | |
44 | 0x5555, /* VC */ | |
45 | 0x0C0C, /* HI == C set && Z clear */ | |
46 | 0xF3F3, /* LS == C clear || Z set */ | |
47 | 0xAA55, /* GE == (N==V) */ | |
48 | 0x55AA, /* LT == (N!=V) */ | |
49 | 0x0A05, /* GT == (!Z && (N==V)) */ | |
50 | 0xF5FA, /* LE == (Z || (N!=V)) */ | |
51 | 0xFFFF, /* AL always */ | |
52 | 0 /* NV */ | |
53 | }; | |
54 | ||
27b190bd MZ |
55 | /* |
56 | * Check if a trapped instruction should have been executed or not. | |
57 | */ | |
021234ef | 58 | bool __hyp_text kvm_condition_valid32(const struct kvm_vcpu *vcpu) |
27b190bd MZ |
59 | { |
60 | unsigned long cpsr; | |
61 | u32 cpsr_cond; | |
62 | int cond; | |
63 | ||
64 | /* Top two bits non-zero? Unconditional. */ | |
65 | if (kvm_vcpu_get_hsr(vcpu) >> 30) | |
66 | return true; | |
67 | ||
68 | /* Is condition field valid? */ | |
69 | cond = kvm_vcpu_get_condition(vcpu); | |
70 | if (cond == 0xE) | |
71 | return true; | |
72 | ||
73 | cpsr = *vcpu_cpsr(vcpu); | |
74 | ||
75 | if (cond < 0) { | |
76 | /* This can happen in Thumb mode: examine IT state. */ | |
77 | unsigned long it; | |
78 | ||
79 | it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3); | |
80 | ||
81 | /* it == 0 => unconditional. */ | |
82 | if (it == 0) | |
83 | return true; | |
84 | ||
85 | /* The cond for this insn works out as the top 4 bits. */ | |
86 | cond = (it >> 4); | |
87 | } | |
88 | ||
89 | cpsr_cond = cpsr >> 28; | |
90 | ||
91 | if (!((cc_map[cond] >> cpsr_cond) & 1)) | |
92 | return false; | |
93 | ||
94 | return true; | |
95 | } | |
96 | ||
97 | /** | |
98 | * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block | |
99 | * @vcpu: The VCPU pointer | |
100 | * | |
101 | * When exceptions occur while instructions are executed in Thumb IF-THEN | |
102 | * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have | |
103 | * to do this little bit of work manually. The fields map like this: | |
104 | * | |
105 | * IT[7:0] -> CPSR[26:25],CPSR[15:10] | |
106 | */ | |
8cebe750 | 107 | static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu) |
27b190bd MZ |
108 | { |
109 | unsigned long itbits, cond; | |
110 | unsigned long cpsr = *vcpu_cpsr(vcpu); | |
111 | bool is_arm = !(cpsr & COMPAT_PSR_T_BIT); | |
112 | ||
427d7cac | 113 | if (is_arm || !(cpsr & COMPAT_PSR_IT_MASK)) |
27b190bd MZ |
114 | return; |
115 | ||
116 | cond = (cpsr & 0xe000) >> 13; | |
117 | itbits = (cpsr & 0x1c00) >> (10 - 2); | |
118 | itbits |= (cpsr & (0x3 << 25)) >> 25; | |
119 | ||
120 | /* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */ | |
121 | if ((itbits & 0x7) == 0) | |
122 | itbits = cond = 0; | |
123 | else | |
124 | itbits = (itbits << 1) & 0x1f; | |
125 | ||
126 | cpsr &= ~COMPAT_PSR_IT_MASK; | |
127 | cpsr |= cond << 13; | |
128 | cpsr |= (itbits & 0x1c) << (10 - 2); | |
129 | cpsr |= (itbits & 0x3) << 25; | |
130 | *vcpu_cpsr(vcpu) = cpsr; | |
131 | } | |
132 | ||
133 | /** | |
134 | * kvm_skip_instr - skip a trapped instruction and proceed to the next | |
135 | * @vcpu: The vcpu pointer | |
136 | */ | |
8cebe750 | 137 | void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr) |
27b190bd MZ |
138 | { |
139 | bool is_thumb; | |
140 | ||
141 | is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT); | |
142 | if (is_thumb && !is_wide_instr) | |
143 | *vcpu_pc(vcpu) += 2; | |
144 | else | |
145 | *vcpu_pc(vcpu) += 4; | |
146 | kvm_adjust_itstate(vcpu); | |
147 | } | |
74a64a98 MZ |
148 | |
149 | /* | |
150 | * Table taken from ARMv8 ARM DDI0487B-B, table G1-10. | |
151 | */ | |
152 | static const u8 return_offsets[8][2] = { | |
153 | [0] = { 0, 0 }, /* Reset, unused */ | |
154 | [1] = { 4, 2 }, /* Undefined */ | |
155 | [2] = { 0, 0 }, /* SVC, unused */ | |
156 | [3] = { 4, 4 }, /* Prefetch abort */ | |
157 | [4] = { 8, 8 }, /* Data abort */ | |
158 | [5] = { 0, 0 }, /* HVC, unused */ | |
159 | [6] = { 4, 4 }, /* IRQ, unused */ | |
160 | [7] = { 4, 4 }, /* FIQ, unused */ | |
161 | }; | |
162 | ||
163 | static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) | |
164 | { | |
165 | unsigned long cpsr; | |
166 | unsigned long new_spsr_value = *vcpu_cpsr(vcpu); | |
167 | bool is_thumb = (new_spsr_value & COMPAT_PSR_T_BIT); | |
168 | u32 return_offset = return_offsets[vect_offset >> 2][is_thumb]; | |
169 | u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); | |
170 | ||
171 | cpsr = mode | COMPAT_PSR_I_BIT; | |
172 | ||
173 | if (sctlr & (1 << 30)) | |
174 | cpsr |= COMPAT_PSR_T_BIT; | |
175 | if (sctlr & (1 << 25)) | |
176 | cpsr |= COMPAT_PSR_E_BIT; | |
177 | ||
178 | *vcpu_cpsr(vcpu) = cpsr; | |
179 | ||
180 | /* Note: These now point to the banked copies */ | |
181 | *vcpu_spsr(vcpu) = new_spsr_value; | |
182 | *vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset; | |
183 | ||
184 | /* Branch to exception vector */ | |
185 | if (sctlr & (1 << 13)) | |
186 | vect_offset += 0xffff0000; | |
187 | else /* always have security exceptions */ | |
188 | vect_offset += vcpu_cp15(vcpu, c12_VBAR); | |
189 | ||
190 | *vcpu_pc(vcpu) = vect_offset; | |
191 | } | |
192 | ||
193 | void kvm_inject_undef32(struct kvm_vcpu *vcpu) | |
194 | { | |
195 | prepare_fault32(vcpu, COMPAT_PSR_MODE_UND, 4); | |
196 | } | |
197 | ||
198 | /* | |
199 | * Modelled after TakeDataAbortException() and TakePrefetchAbortException | |
200 | * pseudocode. | |
201 | */ | |
202 | static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, | |
203 | unsigned long addr) | |
204 | { | |
205 | u32 vect_offset; | |
206 | u32 *far, *fsr; | |
207 | bool is_lpae; | |
208 | ||
209 | if (is_pabt) { | |
210 | vect_offset = 12; | |
211 | far = &vcpu_cp15(vcpu, c6_IFAR); | |
212 | fsr = &vcpu_cp15(vcpu, c5_IFSR); | |
213 | } else { /* !iabt */ | |
214 | vect_offset = 16; | |
215 | far = &vcpu_cp15(vcpu, c6_DFAR); | |
216 | fsr = &vcpu_cp15(vcpu, c5_DFSR); | |
217 | } | |
218 | ||
219 | prepare_fault32(vcpu, COMPAT_PSR_MODE_ABT | COMPAT_PSR_A_BIT, vect_offset); | |
220 | ||
221 | *far = addr; | |
222 | ||
223 | /* Give the guest an IMPLEMENTATION DEFINED exception */ | |
224 | is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31); | |
225 | if (is_lpae) | |
226 | *fsr = 1 << 9 | 0x34; | |
227 | else | |
228 | *fsr = 0x14; | |
229 | } | |
230 | ||
231 | void kvm_inject_dabt32(struct kvm_vcpu *vcpu, unsigned long addr) | |
232 | { | |
233 | inject_abt32(vcpu, false, addr); | |
234 | } | |
235 | ||
236 | void kvm_inject_pabt32(struct kvm_vcpu *vcpu, unsigned long addr) | |
237 | { | |
238 | inject_abt32(vcpu, true, addr); | |
239 | } |