]>
Commit | Line | Data |
---|---|---|
45e96ea6 CD |
1 | /* |
2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | |
3 | * Author: Christoffer Dall <c.dall@virtualopensystems.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License, version 2, as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 | */ | |
18 | ||
19 | #include <linux/kvm_host.h> | |
20 | #include <asm/kvm_mmio.h> | |
21 | #include <asm/kvm_emulate.h> | |
22 | #include <trace/events/kvm.h> | |
23 | ||
24 | #include "trace.h" | |
25 | ||
d5a5a0ef | 26 | void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data) |
6d89d2d9 MZ |
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 | ||
d5a5a0ef | 58 | unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len) |
6d89d2d9 MZ |
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: | |
d5a5a0ef | 69 | data = *(u8 *)buf; |
6d89d2d9 MZ |
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 | ||
45e96ea6 CD |
88 | /** |
89 | * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation | |
83091db9 CD |
90 | * or in-kernel IO emulation |
91 | * | |
45e96ea6 CD |
92 | * @vcpu: The VCPU pointer |
93 | * @run: The VCPU run struct containing the mmio data | |
45e96ea6 CD |
94 | */ |
95 | int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) | |
96 | { | |
6d89d2d9 | 97 | unsigned long data; |
45e96ea6 CD |
98 | unsigned int len; |
99 | int mask; | |
100 | ||
27c126ad AJ |
101 | /* Detect an already handled MMIO return */ |
102 | if (unlikely(!vcpu->mmio_needed)) | |
103 | return 0; | |
104 | ||
105 | vcpu->mmio_needed = 0; | |
106 | ||
45e96ea6 | 107 | if (!run->mmio.is_write) { |
45e96ea6 | 108 | len = run->mmio.len; |
f42798c6 | 109 | if (len > sizeof(unsigned long)) |
45e96ea6 CD |
110 | return -EINVAL; |
111 | ||
d5a5a0ef | 112 | data = kvm_mmio_read_buf(run->mmio.data, len); |
45e96ea6 | 113 | |
f42798c6 MZ |
114 | if (vcpu->arch.mmio_decode.sign_extend && |
115 | len < sizeof(unsigned long)) { | |
45e96ea6 | 116 | mask = 1U << ((len * 8) - 1); |
6d89d2d9 | 117 | data = (data ^ mask) - mask; |
45e96ea6 | 118 | } |
6d89d2d9 | 119 | |
9685444e CD |
120 | if (!vcpu->arch.mmio_decode.sixty_four) |
121 | data = data & 0xffffffff; | |
122 | ||
6d89d2d9 | 123 | trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, |
e39d200f | 124 | &data); |
6d89d2d9 | 125 | data = vcpu_data_host_to_guest(vcpu, data, len); |
bc45a516 | 126 | vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data); |
45e96ea6 CD |
127 | } |
128 | ||
2fd6db04 MR |
129 | /* |
130 | * The MMIO instruction is emulated and should not be re-executed | |
131 | * in the guest. | |
132 | */ | |
133 | kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); | |
134 | ||
45e96ea6 CD |
135 | return 0; |
136 | } | |
137 | ||
950324ab | 138 | static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len) |
45e96ea6 | 139 | { |
2184a60d | 140 | unsigned long rt; |
950324ab AP |
141 | int access_size; |
142 | bool sign_extend; | |
9685444e | 143 | bool sixty_four; |
45e96ea6 | 144 | |
b37670b0 | 145 | if (kvm_vcpu_dabt_iss1tw(vcpu)) { |
45e96ea6 | 146 | /* page table accesses IO mem: tell guest to fix its TTBR */ |
7393b599 | 147 | kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
45e96ea6 CD |
148 | return 1; |
149 | } | |
150 | ||
950324ab AP |
151 | access_size = kvm_vcpu_dabt_get_as(vcpu); |
152 | if (unlikely(access_size < 0)) | |
153 | return access_size; | |
45e96ea6 | 154 | |
950324ab | 155 | *is_write = kvm_vcpu_dabt_iswrite(vcpu); |
7c511b88 | 156 | sign_extend = kvm_vcpu_dabt_issext(vcpu); |
9685444e | 157 | sixty_four = kvm_vcpu_dabt_issf(vcpu); |
d0adf747 | 158 | rt = kvm_vcpu_dabt_get_rd(vcpu); |
45e96ea6 | 159 | |
950324ab | 160 | *len = access_size; |
45e96ea6 CD |
161 | vcpu->arch.mmio_decode.sign_extend = sign_extend; |
162 | vcpu->arch.mmio_decode.rt = rt; | |
9685444e | 163 | vcpu->arch.mmio_decode.sixty_four = sixty_four; |
45e96ea6 | 164 | |
45e96ea6 CD |
165 | return 0; |
166 | } | |
167 | ||
168 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | |
169 | phys_addr_t fault_ipa) | |
170 | { | |
6d89d2d9 | 171 | unsigned long data; |
45e96ea6 CD |
172 | unsigned long rt; |
173 | int ret; | |
950324ab AP |
174 | bool is_write; |
175 | int len; | |
176 | u8 data_buf[8]; | |
45e96ea6 CD |
177 | |
178 | /* | |
950324ab AP |
179 | * Prepare MMIO operation. First decode the syndrome data we get |
180 | * from the CPU. Then try if some in-kernel emulation feels | |
181 | * responsible, otherwise let user space do its magic. | |
45e96ea6 | 182 | */ |
4a1df28a | 183 | if (kvm_vcpu_dabt_isvalid(vcpu)) { |
950324ab | 184 | ret = decode_hsr(vcpu, &is_write, &len); |
45e96ea6 CD |
185 | if (ret) |
186 | return ret; | |
187 | } else { | |
188 | kvm_err("load/store instruction decoding not implemented\n"); | |
189 | return -ENOSYS; | |
190 | } | |
191 | ||
192 | rt = vcpu->arch.mmio_decode.rt; | |
6d89d2d9 | 193 | |
950324ab | 194 | if (is_write) { |
bc45a516 PF |
195 | data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt), |
196 | len); | |
950324ab | 197 | |
e39d200f | 198 | trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data); |
d5a5a0ef | 199 | kvm_mmio_write_buf(data_buf, len, data); |
45e96ea6 | 200 | |
950324ab AP |
201 | ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len, |
202 | data_buf); | |
5100f983 | 203 | } else { |
950324ab | 204 | trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len, |
e39d200f | 205 | fault_ipa, NULL); |
950324ab AP |
206 | |
207 | ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len, | |
208 | data_buf); | |
5100f983 | 209 | } |
45e96ea6 | 210 | |
950324ab AP |
211 | /* Now prepare kvm_run for the potential return to userland. */ |
212 | run->mmio.is_write = is_write; | |
213 | run->mmio.phys_addr = fault_ipa; | |
214 | run->mmio.len = len; | |
27c126ad | 215 | vcpu->mmio_needed = 1; |
950324ab AP |
216 | |
217 | if (!ret) { | |
218 | /* We handled the access successfully in the kernel. */ | |
83091db9 CD |
219 | if (!is_write) |
220 | memcpy(run->mmio.data, data_buf, len); | |
b19e6892 | 221 | vcpu->stat.mmio_exit_kernel++; |
950324ab | 222 | kvm_handle_mmio_return(vcpu, run); |
1a89dd91 | 223 | return 1; |
950324ab | 224 | } |
1a89dd91 | 225 | |
83091db9 CD |
226 | if (is_write) |
227 | memcpy(run->mmio.data, data_buf, len); | |
228 | vcpu->stat.mmio_exit_user++; | |
950324ab | 229 | run->exit_reason = KVM_EXIT_MMIO; |
45e96ea6 CD |
230 | return 0; |
231 | } |