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 | ||
101 | if (!run->mmio.is_write) { | |
45e96ea6 | 102 | len = run->mmio.len; |
f42798c6 | 103 | if (len > sizeof(unsigned long)) |
45e96ea6 CD |
104 | return -EINVAL; |
105 | ||
d5a5a0ef | 106 | data = kvm_mmio_read_buf(run->mmio.data, len); |
45e96ea6 | 107 | |
f42798c6 MZ |
108 | if (vcpu->arch.mmio_decode.sign_extend && |
109 | len < sizeof(unsigned long)) { | |
45e96ea6 | 110 | mask = 1U << ((len * 8) - 1); |
6d89d2d9 | 111 | data = (data ^ mask) - mask; |
45e96ea6 | 112 | } |
6d89d2d9 MZ |
113 | |
114 | trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, | |
115 | data); | |
116 | data = vcpu_data_host_to_guest(vcpu, data, len); | |
bc45a516 | 117 | vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data); |
45e96ea6 CD |
118 | } |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
950324ab | 123 | static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len) |
45e96ea6 | 124 | { |
2184a60d | 125 | unsigned long rt; |
950324ab AP |
126 | int access_size; |
127 | bool sign_extend; | |
45e96ea6 | 128 | |
78abfcde | 129 | if (kvm_vcpu_dabt_isextabt(vcpu)) { |
45e96ea6 | 130 | /* cache operation on I/O addr, tell guest unsupported */ |
7393b599 | 131 | kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
45e96ea6 CD |
132 | return 1; |
133 | } | |
134 | ||
b37670b0 | 135 | if (kvm_vcpu_dabt_iss1tw(vcpu)) { |
45e96ea6 | 136 | /* page table accesses IO mem: tell guest to fix its TTBR */ |
7393b599 | 137 | kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
45e96ea6 CD |
138 | return 1; |
139 | } | |
140 | ||
950324ab AP |
141 | access_size = kvm_vcpu_dabt_get_as(vcpu); |
142 | if (unlikely(access_size < 0)) | |
143 | return access_size; | |
45e96ea6 | 144 | |
950324ab | 145 | *is_write = kvm_vcpu_dabt_iswrite(vcpu); |
7c511b88 | 146 | sign_extend = kvm_vcpu_dabt_issext(vcpu); |
d0adf747 | 147 | rt = kvm_vcpu_dabt_get_rd(vcpu); |
45e96ea6 | 148 | |
950324ab | 149 | *len = access_size; |
45e96ea6 CD |
150 | vcpu->arch.mmio_decode.sign_extend = sign_extend; |
151 | vcpu->arch.mmio_decode.rt = rt; | |
152 | ||
153 | /* | |
154 | * The MMIO instruction is emulated and should not be re-executed | |
155 | * in the guest. | |
156 | */ | |
23b415d6 | 157 | kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); |
45e96ea6 CD |
158 | return 0; |
159 | } | |
160 | ||
161 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | |
162 | phys_addr_t fault_ipa) | |
163 | { | |
6d89d2d9 | 164 | unsigned long data; |
45e96ea6 CD |
165 | unsigned long rt; |
166 | int ret; | |
950324ab AP |
167 | bool is_write; |
168 | int len; | |
169 | u8 data_buf[8]; | |
45e96ea6 CD |
170 | |
171 | /* | |
950324ab AP |
172 | * Prepare MMIO operation. First decode the syndrome data we get |
173 | * from the CPU. Then try if some in-kernel emulation feels | |
174 | * responsible, otherwise let user space do its magic. | |
45e96ea6 | 175 | */ |
4a1df28a | 176 | if (kvm_vcpu_dabt_isvalid(vcpu)) { |
950324ab | 177 | ret = decode_hsr(vcpu, &is_write, &len); |
45e96ea6 CD |
178 | if (ret) |
179 | return ret; | |
180 | } else { | |
181 | kvm_err("load/store instruction decoding not implemented\n"); | |
182 | return -ENOSYS; | |
183 | } | |
184 | ||
185 | rt = vcpu->arch.mmio_decode.rt; | |
6d89d2d9 | 186 | |
950324ab | 187 | if (is_write) { |
bc45a516 PF |
188 | data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt), |
189 | len); | |
950324ab AP |
190 | |
191 | trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data); | |
d5a5a0ef | 192 | kvm_mmio_write_buf(data_buf, len, data); |
45e96ea6 | 193 | |
950324ab AP |
194 | ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len, |
195 | data_buf); | |
5100f983 | 196 | } else { |
950324ab | 197 | trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len, |
5100f983 | 198 | fault_ipa, 0); |
950324ab AP |
199 | |
200 | ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len, | |
201 | data_buf); | |
5100f983 | 202 | } |
45e96ea6 | 203 | |
950324ab AP |
204 | /* Now prepare kvm_run for the potential return to userland. */ |
205 | run->mmio.is_write = is_write; | |
206 | run->mmio.phys_addr = fault_ipa; | |
207 | run->mmio.len = len; | |
950324ab AP |
208 | |
209 | if (!ret) { | |
210 | /* We handled the access successfully in the kernel. */ | |
83091db9 CD |
211 | if (!is_write) |
212 | memcpy(run->mmio.data, data_buf, len); | |
b19e6892 | 213 | vcpu->stat.mmio_exit_kernel++; |
950324ab | 214 | kvm_handle_mmio_return(vcpu, run); |
1a89dd91 | 215 | return 1; |
950324ab | 216 | } |
1a89dd91 | 217 | |
83091db9 CD |
218 | if (is_write) |
219 | memcpy(run->mmio.data, data_buf, len); | |
220 | vcpu->stat.mmio_exit_user++; | |
950324ab | 221 | run->exit_reason = KVM_EXIT_MMIO; |
45e96ea6 CD |
222 | return 0; |
223 | } |