aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2013-02-12 07:40:22 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2013-11-07 14:09:04 -0500
commit6d89d2d9b5bac9dbe40ee106ceda9307b6265234 (patch)
treedc2f8fad0be6afabbbfccca1311be40298449cd2
parentd241aac798eb042e605f78c31a4122e583b2cd13 (diff)
arm/arm64: KVM: MMIO support for BE guest
Do the necessary byteswap when host and guest have different views of the universe. Actually, the only case we need to take care of is when the guest is BE. All the other cases are naturally handled. Also be careful about endianness when the data is being memcopy-ed from/to the run buffer. Acked-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--arch/arm/include/asm/kvm_emulate.h41
-rw-r--r--arch/arm/kvm/mmio.c86
-rw-r--r--arch/arm64/include/asm/kvm_emulate.h48
3 files changed, 164 insertions, 11 deletions
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
index a464e8d7b6c5..8a6be05a46d7 100644
--- a/arch/arm/include/asm/kvm_emulate.h
+++ b/arch/arm/include/asm/kvm_emulate.h
@@ -157,4 +157,45 @@ static inline u32 kvm_vcpu_hvc_get_imm(struct kvm_vcpu *vcpu)
157 return kvm_vcpu_get_hsr(vcpu) & HSR_HVC_IMM_MASK; 157 return kvm_vcpu_get_hsr(vcpu) & HSR_HVC_IMM_MASK;
158} 158}
159 159
160static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
161{
162 return !!(*vcpu_cpsr(vcpu) & PSR_E_BIT);
163}
164
165static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
166 unsigned long data,
167 unsigned int len)
168{
169 if (kvm_vcpu_is_be(vcpu)) {
170 switch (len) {
171 case 1:
172 return data & 0xff;
173 case 2:
174 return be16_to_cpu(data & 0xffff);
175 default:
176 return be32_to_cpu(data);
177 }
178 }
179
180 return data; /* Leave LE untouched */
181}
182
183static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
184 unsigned long data,
185 unsigned int len)
186{
187 if (kvm_vcpu_is_be(vcpu)) {
188 switch (len) {
189 case 1:
190 return data & 0xff;
191 case 2:
192 return cpu_to_be16(data & 0xffff);
193 default:
194 return cpu_to_be32(data);
195 }
196 }
197
198 return data; /* Leave LE untouched */
199}
200
160#endif /* __ARM_KVM_EMULATE_H__ */ 201#endif /* __ARM_KVM_EMULATE_H__ */
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
index 0c25d9487d53..4cb5a93182e9 100644
--- a/arch/arm/kvm/mmio.c
+++ b/arch/arm/kvm/mmio.c
@@ -23,6 +23,68 @@
23 23
24#include "trace.h" 24#include "trace.h"
25 25
26static void mmio_write_buf(char *buf, unsigned int len, unsigned long data)
27{
28 void *datap = NULL;
29 union {
30 u8 byte;
31 u16 hword;
32 u32 word;
33 u64 dword;
34 } tmp;
35
36 switch (len) {
37 case 1:
38 tmp.byte = data;
39 datap = &tmp.byte;
40 break;
41 case 2:
42 tmp.hword = data;
43 datap = &tmp.hword;
44 break;
45 case 4:
46 tmp.word = data;
47 datap = &tmp.word;
48 break;
49 case 8:
50 tmp.dword = data;
51 datap = &tmp.dword;
52 break;
53 }
54
55 memcpy(buf, datap, len);
56}
57
58static unsigned long mmio_read_buf(char *buf, unsigned int len)
59{
60 unsigned long data = 0;
61 union {
62 u16 hword;
63 u32 word;
64 u64 dword;
65 } tmp;
66
67 switch (len) {
68 case 1:
69 data = buf[0];
70 break;
71 case 2:
72 memcpy(&tmp.hword, buf, len);
73 data = tmp.hword;
74 break;
75 case 4:
76 memcpy(&tmp.word, buf, len);
77 data = tmp.word;
78 break;
79 case 8:
80 memcpy(&tmp.dword, buf, len);
81 data = tmp.dword;
82 break;
83 }
84
85 return data;
86}
87
26/** 88/**
27 * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation 89 * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
28 * @vcpu: The VCPU pointer 90 * @vcpu: The VCPU pointer
@@ -33,28 +95,27 @@
33 */ 95 */
34int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) 96int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
35{ 97{
36 unsigned long *dest; 98 unsigned long data;
37 unsigned int len; 99 unsigned int len;
38 int mask; 100 int mask;
39 101
40 if (!run->mmio.is_write) { 102 if (!run->mmio.is_write) {
41 dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt);
42 *dest = 0;
43
44 len = run->mmio.len; 103 len = run->mmio.len;
45 if (len > sizeof(unsigned long)) 104 if (len > sizeof(unsigned long))
46 return -EINVAL; 105 return -EINVAL;
47 106
48 memcpy(dest, run->mmio.data, len); 107 data = mmio_read_buf(run->mmio.data, len);
49
50 trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
51 *((u64 *)run->mmio.data));
52 108
53 if (vcpu->arch.mmio_decode.sign_extend && 109 if (vcpu->arch.mmio_decode.sign_extend &&
54 len < sizeof(unsigned long)) { 110 len < sizeof(unsigned long)) {
55 mask = 1U << ((len * 8) - 1); 111 mask = 1U << ((len * 8) - 1);
56 *dest = (*dest ^ mask) - mask; 112 data = (data ^ mask) - mask;
57 } 113 }
114
115 trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
116 data);
117 data = vcpu_data_host_to_guest(vcpu, data, len);
118 *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data;
58 } 119 }
59 120
60 return 0; 121 return 0;
@@ -105,6 +166,7 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
105 phys_addr_t fault_ipa) 166 phys_addr_t fault_ipa)
106{ 167{
107 struct kvm_exit_mmio mmio; 168 struct kvm_exit_mmio mmio;
169 unsigned long data;
108 unsigned long rt; 170 unsigned long rt;
109 int ret; 171 int ret;
110 172
@@ -125,13 +187,15 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
125 } 187 }
126 188
127 rt = vcpu->arch.mmio_decode.rt; 189 rt = vcpu->arch.mmio_decode.rt;
190 data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), mmio.len);
191
128 trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE : 192 trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE :
129 KVM_TRACE_MMIO_READ_UNSATISFIED, 193 KVM_TRACE_MMIO_READ_UNSATISFIED,
130 mmio.len, fault_ipa, 194 mmio.len, fault_ipa,
131 (mmio.is_write) ? *vcpu_reg(vcpu, rt) : 0); 195 (mmio.is_write) ? data : 0);
132 196
133 if (mmio.is_write) 197 if (mmio.is_write)
134 memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len); 198 mmio_write_buf(mmio.data, mmio.len, data);
135 199
136 if (vgic_handle_mmio(vcpu, run, &mmio)) 200 if (vgic_handle_mmio(vcpu, run, &mmio))
137 return 1; 201 return 1;
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index eec073875218..b016577e37a4 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -177,4 +177,52 @@ static inline u8 kvm_vcpu_trap_get_fault(const struct kvm_vcpu *vcpu)
177 return kvm_vcpu_get_hsr(vcpu) & ESR_EL2_FSC_TYPE; 177 return kvm_vcpu_get_hsr(vcpu) & ESR_EL2_FSC_TYPE;
178} 178}
179 179
180static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu)
181{
182 if (vcpu_mode_is_32bit(vcpu))
183 return !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_E_BIT);
184
185 return !!(vcpu_sys_reg(vcpu, SCTLR_EL1) & (1 << 25));
186}
187
188static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu,
189 unsigned long data,
190 unsigned int len)
191{
192 if (kvm_vcpu_is_be(vcpu)) {
193 switch (len) {
194 case 1:
195 return data & 0xff;
196 case 2:
197 return be16_to_cpu(data & 0xffff);
198 case 4:
199 return be32_to_cpu(data & 0xffffffff);
200 default:
201 return be64_to_cpu(data);
202 }
203 }
204
205 return data; /* Leave LE untouched */
206}
207
208static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
209 unsigned long data,
210 unsigned int len)
211{
212 if (kvm_vcpu_is_be(vcpu)) {
213 switch (len) {
214 case 1:
215 return data & 0xff;
216 case 2:
217 return cpu_to_be16(data & 0xffff);
218 case 4:
219 return cpu_to_be32(data & 0xffffffff);
220 default:
221 return cpu_to_be64(data);
222 }
223 }
224
225 return data; /* Leave LE untouched */
226}
227
180#endif /* __ARM64_KVM_EMULATE_H__ */ 228#endif /* __ARM64_KVM_EMULATE_H__ */