diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2013-02-06 14:54:04 -0500 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2013-06-12 11:42:15 -0400 |
commit | 27b190bd9fbfee34536cb858f0b5924d294aac38 (patch) | |
tree | d35b3304f685d047bc675f30156902d71f238bc5 /arch/arm64 | |
parent | b547631fc64e249a3c507e6ce854642507fa7c1c (diff) |
arm64: KVM: 32bit conditional execution emulation
As conditional instructions can trap on AArch32, add the thinest
possible emulation layer to keep 32bit guests happy.
Reviewed-by: Christopher Covington <cov@codeaurora.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/include/asm/kvm_emulate.h | 13 | ||||
-rw-r--r-- | arch/arm64/kvm/Makefile | 2 | ||||
-rw-r--r-- | arch/arm64/kvm/emulate.c | 158 |
3 files changed, 170 insertions, 3 deletions
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 20a1a3931d8d..eec073875218 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h | |||
@@ -31,6 +31,9 @@ | |||
31 | unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num); | 31 | unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num); |
32 | unsigned long *vcpu_spsr32(const struct kvm_vcpu *vcpu); | 32 | unsigned long *vcpu_spsr32(const struct kvm_vcpu *vcpu); |
33 | 33 | ||
34 | bool kvm_condition_valid32(const struct kvm_vcpu *vcpu); | ||
35 | void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr); | ||
36 | |||
34 | void kvm_inject_undefined(struct kvm_vcpu *vcpu); | 37 | void kvm_inject_undefined(struct kvm_vcpu *vcpu); |
35 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); | 38 | void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); |
36 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); | 39 | void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); |
@@ -57,12 +60,18 @@ static inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu) | |||
57 | 60 | ||
58 | static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu) | 61 | static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu) |
59 | { | 62 | { |
60 | return true; /* No conditionals on arm64 */ | 63 | if (vcpu_mode_is_32bit(vcpu)) |
64 | return kvm_condition_valid32(vcpu); | ||
65 | |||
66 | return true; | ||
61 | } | 67 | } |
62 | 68 | ||
63 | static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) | 69 | static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) |
64 | { | 70 | { |
65 | *vcpu_pc(vcpu) += 4; | 71 | if (vcpu_mode_is_32bit(vcpu)) |
72 | kvm_skip_instr32(vcpu, is_wide_instr); | ||
73 | else | ||
74 | *vcpu_pc(vcpu) += 4; | ||
66 | } | 75 | } |
67 | 76 | ||
68 | static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) | 77 | static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) |
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index a2169ec8d93b..72a9fd583ad3 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile | |||
@@ -15,7 +15,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o | |||
15 | kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o | 15 | kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o |
16 | kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o | 16 | kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o |
17 | 17 | ||
18 | kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o | 18 | kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o |
19 | kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o | 19 | kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o |
20 | kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o | 20 | kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o |
21 | 21 | ||
diff --git a/arch/arm64/kvm/emulate.c b/arch/arm64/kvm/emulate.c new file mode 100644 index 000000000000..124418d17049 --- /dev/null +++ b/arch/arm64/kvm/emulate.c | |||
@@ -0,0 +1,158 @@ | |||
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> | ||
26 | |||
27 | /* | ||
28 | * stolen from arch/arm/kernel/opcodes.c | ||
29 | * | ||
30 | * condition code lookup table | ||
31 | * index into the table is test code: EQ, NE, ... LT, GT, AL, NV | ||
32 | * | ||
33 | * bit position in short is condition code: NZCV | ||
34 | */ | ||
35 | static const unsigned short cc_map[16] = { | ||
36 | 0xF0F0, /* EQ == Z set */ | ||
37 | 0x0F0F, /* NE */ | ||
38 | 0xCCCC, /* CS == C set */ | ||
39 | 0x3333, /* CC */ | ||
40 | 0xFF00, /* MI == N set */ | ||
41 | 0x00FF, /* PL */ | ||
42 | 0xAAAA, /* VS == V set */ | ||
43 | 0x5555, /* VC */ | ||
44 | 0x0C0C, /* HI == C set && Z clear */ | ||
45 | 0xF3F3, /* LS == C clear || Z set */ | ||
46 | 0xAA55, /* GE == (N==V) */ | ||
47 | 0x55AA, /* LT == (N!=V) */ | ||
48 | 0x0A05, /* GT == (!Z && (N==V)) */ | ||
49 | 0xF5FA, /* LE == (Z || (N!=V)) */ | ||
50 | 0xFFFF, /* AL always */ | ||
51 | 0 /* NV */ | ||
52 | }; | ||
53 | |||
54 | static int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu) | ||
55 | { | ||
56 | u32 esr = kvm_vcpu_get_hsr(vcpu); | ||
57 | |||
58 | if (esr & ESR_EL2_CV) | ||
59 | return (esr & ESR_EL2_COND) >> ESR_EL2_COND_SHIFT; | ||
60 | |||
61 | return -1; | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * Check if a trapped instruction should have been executed or not. | ||
66 | */ | ||
67 | bool kvm_condition_valid32(const struct kvm_vcpu *vcpu) | ||
68 | { | ||
69 | unsigned long cpsr; | ||
70 | u32 cpsr_cond; | ||
71 | int cond; | ||
72 | |||
73 | /* Top two bits non-zero? Unconditional. */ | ||
74 | if (kvm_vcpu_get_hsr(vcpu) >> 30) | ||
75 | return true; | ||
76 | |||
77 | /* Is condition field valid? */ | ||
78 | cond = kvm_vcpu_get_condition(vcpu); | ||
79 | if (cond == 0xE) | ||
80 | return true; | ||
81 | |||
82 | cpsr = *vcpu_cpsr(vcpu); | ||
83 | |||
84 | if (cond < 0) { | ||
85 | /* This can happen in Thumb mode: examine IT state. */ | ||
86 | unsigned long it; | ||
87 | |||
88 | it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3); | ||
89 | |||
90 | /* it == 0 => unconditional. */ | ||
91 | if (it == 0) | ||
92 | return true; | ||
93 | |||
94 | /* The cond for this insn works out as the top 4 bits. */ | ||
95 | cond = (it >> 4); | ||
96 | } | ||
97 | |||
98 | cpsr_cond = cpsr >> 28; | ||
99 | |||
100 | if (!((cc_map[cond] >> cpsr_cond) & 1)) | ||
101 | return false; | ||
102 | |||
103 | return true; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block | ||
108 | * @vcpu: The VCPU pointer | ||
109 | * | ||
110 | * When exceptions occur while instructions are executed in Thumb IF-THEN | ||
111 | * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have | ||
112 | * to do this little bit of work manually. The fields map like this: | ||
113 | * | ||
114 | * IT[7:0] -> CPSR[26:25],CPSR[15:10] | ||
115 | */ | ||
116 | static void kvm_adjust_itstate(struct kvm_vcpu *vcpu) | ||
117 | { | ||
118 | unsigned long itbits, cond; | ||
119 | unsigned long cpsr = *vcpu_cpsr(vcpu); | ||
120 | bool is_arm = !(cpsr & COMPAT_PSR_T_BIT); | ||
121 | |||
122 | BUG_ON(is_arm && (cpsr & COMPAT_PSR_IT_MASK)); | ||
123 | |||
124 | if (!(cpsr & COMPAT_PSR_IT_MASK)) | ||
125 | return; | ||
126 | |||
127 | cond = (cpsr & 0xe000) >> 13; | ||
128 | itbits = (cpsr & 0x1c00) >> (10 - 2); | ||
129 | itbits |= (cpsr & (0x3 << 25)) >> 25; | ||
130 | |||
131 | /* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */ | ||
132 | if ((itbits & 0x7) == 0) | ||
133 | itbits = cond = 0; | ||
134 | else | ||
135 | itbits = (itbits << 1) & 0x1f; | ||
136 | |||
137 | cpsr &= ~COMPAT_PSR_IT_MASK; | ||
138 | cpsr |= cond << 13; | ||
139 | cpsr |= (itbits & 0x1c) << (10 - 2); | ||
140 | cpsr |= (itbits & 0x3) << 25; | ||
141 | *vcpu_cpsr(vcpu) = cpsr; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * kvm_skip_instr - skip a trapped instruction and proceed to the next | ||
146 | * @vcpu: The vcpu pointer | ||
147 | */ | ||
148 | void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr) | ||
149 | { | ||
150 | bool is_thumb; | ||
151 | |||
152 | is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT); | ||
153 | if (is_thumb && !is_wide_instr) | ||
154 | *vcpu_pc(vcpu) += 2; | ||
155 | else | ||
156 | *vcpu_pc(vcpu) += 4; | ||
157 | kvm_adjust_itstate(vcpu); | ||
158 | } | ||