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