diff options
Diffstat (limited to 'arch/arm/kvm/emulate.c')
-rw-r--r-- | arch/arm/kvm/emulate.c | 75 |
1 files changed, 52 insertions, 23 deletions
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c index d61450ac6665..bdede9e7da51 100644 --- a/arch/arm/kvm/emulate.c +++ b/arch/arm/kvm/emulate.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/kvm_host.h> | 20 | #include <linux/kvm_host.h> |
21 | #include <asm/kvm_arm.h> | 21 | #include <asm/kvm_arm.h> |
22 | #include <asm/kvm_emulate.h> | 22 | #include <asm/kvm_emulate.h> |
23 | #include <asm/opcodes.h> | ||
23 | #include <trace/events/kvm.h> | 24 | #include <trace/events/kvm.h> |
24 | 25 | ||
25 | #include "trace.h" | 26 | #include "trace.h" |
@@ -109,10 +110,10 @@ static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = { | |||
109 | * Return a pointer to the register number valid in the current mode of | 110 | * Return a pointer to the register number valid in the current mode of |
110 | * the virtual CPU. | 111 | * the virtual CPU. |
111 | */ | 112 | */ |
112 | u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num) | 113 | unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num) |
113 | { | 114 | { |
114 | u32 *reg_array = (u32 *)&vcpu->arch.regs; | 115 | unsigned long *reg_array = (unsigned long *)&vcpu->arch.regs; |
115 | u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK; | 116 | unsigned long mode = *vcpu_cpsr(vcpu) & MODE_MASK; |
116 | 117 | ||
117 | switch (mode) { | 118 | switch (mode) { |
118 | case USR_MODE...SVC_MODE: | 119 | case USR_MODE...SVC_MODE: |
@@ -141,9 +142,9 @@ u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num) | |||
141 | /* | 142 | /* |
142 | * Return the SPSR for the current mode of the virtual CPU. | 143 | * Return the SPSR for the current mode of the virtual CPU. |
143 | */ | 144 | */ |
144 | u32 *vcpu_spsr(struct kvm_vcpu *vcpu) | 145 | unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu) |
145 | { | 146 | { |
146 | u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK; | 147 | unsigned long mode = *vcpu_cpsr(vcpu) & MODE_MASK; |
147 | switch (mode) { | 148 | switch (mode) { |
148 | case SVC_MODE: | 149 | case SVC_MODE: |
149 | return &vcpu->arch.regs.KVM_ARM_SVC_spsr; | 150 | return &vcpu->arch.regs.KVM_ARM_SVC_spsr; |
@@ -160,20 +161,48 @@ u32 *vcpu_spsr(struct kvm_vcpu *vcpu) | |||
160 | } | 161 | } |
161 | } | 162 | } |
162 | 163 | ||
163 | /** | 164 | /* |
164 | * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest | 165 | * A conditional instruction is allowed to trap, even though it |
165 | * @vcpu: the vcpu pointer | 166 | * wouldn't be executed. So let's re-implement the hardware, in |
166 | * @run: the kvm_run structure pointer | 167 | * software! |
167 | * | ||
168 | * Simply sets the wait_for_interrupts flag on the vcpu structure, which will | ||
169 | * halt execution of world-switches and schedule other host processes until | ||
170 | * there is an incoming IRQ or FIQ to the VM. | ||
171 | */ | 168 | */ |
172 | int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) | 169 | bool kvm_condition_valid(struct kvm_vcpu *vcpu) |
173 | { | 170 | { |
174 | trace_kvm_wfi(*vcpu_pc(vcpu)); | 171 | unsigned long cpsr, cond, insn; |
175 | kvm_vcpu_block(vcpu); | 172 | |
176 | return 1; | 173 | /* |
174 | * Exception Code 0 can only happen if we set HCR.TGE to 1, to | ||
175 | * catch undefined instructions, and then we won't get past | ||
176 | * the arm_exit_handlers test anyway. | ||
177 | */ | ||
178 | BUG_ON(!kvm_vcpu_trap_get_class(vcpu)); | ||
179 | |||
180 | /* Top two bits non-zero? Unconditional. */ | ||
181 | if (kvm_vcpu_get_hsr(vcpu) >> 30) | ||
182 | return true; | ||
183 | |||
184 | cpsr = *vcpu_cpsr(vcpu); | ||
185 | |||
186 | /* Is condition field valid? */ | ||
187 | if ((kvm_vcpu_get_hsr(vcpu) & HSR_CV) >> HSR_CV_SHIFT) | ||
188 | cond = (kvm_vcpu_get_hsr(vcpu) & HSR_COND) >> HSR_COND_SHIFT; | ||
189 | else { | ||
190 | /* This can happen in Thumb mode: examine IT state. */ | ||
191 | unsigned long it; | ||
192 | |||
193 | it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3); | ||
194 | |||
195 | /* it == 0 => unconditional. */ | ||
196 | if (it == 0) | ||
197 | return true; | ||
198 | |||
199 | /* The cond for this insn works out as the top 4 bits. */ | ||
200 | cond = (it >> 4); | ||
201 | } | ||
202 | |||
203 | /* Shift makes it look like an ARM-mode instruction */ | ||
204 | insn = cond << 28; | ||
205 | return arm_check_condition(insn, cpsr) != ARM_OPCODE_CONDTEST_FAIL; | ||
177 | } | 206 | } |
178 | 207 | ||
179 | /** | 208 | /** |
@@ -257,9 +286,9 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu) | |||
257 | */ | 286 | */ |
258 | void kvm_inject_undefined(struct kvm_vcpu *vcpu) | 287 | void kvm_inject_undefined(struct kvm_vcpu *vcpu) |
259 | { | 288 | { |
260 | u32 new_lr_value; | 289 | unsigned long new_lr_value; |
261 | u32 new_spsr_value; | 290 | unsigned long new_spsr_value; |
262 | u32 cpsr = *vcpu_cpsr(vcpu); | 291 | unsigned long cpsr = *vcpu_cpsr(vcpu); |
263 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; | 292 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; |
264 | bool is_thumb = (cpsr & PSR_T_BIT); | 293 | bool is_thumb = (cpsr & PSR_T_BIT); |
265 | u32 vect_offset = 4; | 294 | u32 vect_offset = 4; |
@@ -291,9 +320,9 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu) | |||
291 | */ | 320 | */ |
292 | static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr) | 321 | static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr) |
293 | { | 322 | { |
294 | u32 new_lr_value; | 323 | unsigned long new_lr_value; |
295 | u32 new_spsr_value; | 324 | unsigned long new_spsr_value; |
296 | u32 cpsr = *vcpu_cpsr(vcpu); | 325 | unsigned long cpsr = *vcpu_cpsr(vcpu); |
297 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; | 326 | u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; |
298 | bool is_thumb = (cpsr & PSR_T_BIT); | 327 | bool is_thumb = (cpsr & PSR_T_BIT); |
299 | u32 vect_offset; | 328 | u32 vect_offset; |