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); | |
118 | *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data; | |
45e96ea6 CD |
119 | } |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, | |
125 | struct kvm_exit_mmio *mmio) | |
126 | { | |
2184a60d CD |
127 | unsigned long rt; |
128 | int len; | |
45e96ea6 CD |
129 | bool is_write, sign_extend; |
130 | ||
78abfcde | 131 | if (kvm_vcpu_dabt_isextabt(vcpu)) { |
45e96ea6 | 132 | /* cache operation on I/O addr, tell guest unsupported */ |
7393b599 | 133 | kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
45e96ea6 CD |
134 | return 1; |
135 | } | |
136 | ||
b37670b0 | 137 | if (kvm_vcpu_dabt_iss1tw(vcpu)) { |
45e96ea6 | 138 | /* page table accesses IO mem: tell guest to fix its TTBR */ |
7393b599 | 139 | kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu)); |
45e96ea6 CD |
140 | return 1; |
141 | } | |
142 | ||
a7123377 MZ |
143 | len = kvm_vcpu_dabt_get_as(vcpu); |
144 | if (unlikely(len < 0)) | |
145 | return len; | |
45e96ea6 | 146 | |
023cc964 | 147 | is_write = kvm_vcpu_dabt_iswrite(vcpu); |
7c511b88 | 148 | sign_extend = kvm_vcpu_dabt_issext(vcpu); |
d0adf747 | 149 | rt = kvm_vcpu_dabt_get_rd(vcpu); |
45e96ea6 | 150 | |
45e96ea6 CD |
151 | mmio->is_write = is_write; |
152 | mmio->phys_addr = fault_ipa; | |
153 | mmio->len = len; | |
154 | vcpu->arch.mmio_decode.sign_extend = sign_extend; | |
155 | vcpu->arch.mmio_decode.rt = rt; | |
156 | ||
157 | /* | |
158 | * The MMIO instruction is emulated and should not be re-executed | |
159 | * in the guest. | |
160 | */ | |
23b415d6 | 161 | kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); |
45e96ea6 CD |
162 | return 0; |
163 | } | |
164 | ||
165 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | |
166 | phys_addr_t fault_ipa) | |
167 | { | |
168 | struct kvm_exit_mmio mmio; | |
6d89d2d9 | 169 | unsigned long data; |
45e96ea6 CD |
170 | unsigned long rt; |
171 | int ret; | |
172 | ||
173 | /* | |
174 | * Prepare MMIO operation. First stash it in a private | |
175 | * structure that we can use for in-kernel emulation. If the | |
176 | * kernel can't handle it, copy it into run->mmio and let user | |
177 | * space do its magic. | |
178 | */ | |
179 | ||
4a1df28a | 180 | if (kvm_vcpu_dabt_isvalid(vcpu)) { |
45e96ea6 CD |
181 | ret = decode_hsr(vcpu, fault_ipa, &mmio); |
182 | if (ret) | |
183 | return ret; | |
184 | } else { | |
185 | kvm_err("load/store instruction decoding not implemented\n"); | |
186 | return -ENOSYS; | |
187 | } | |
188 | ||
189 | rt = vcpu->arch.mmio_decode.rt; | |
6d89d2d9 MZ |
190 | data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), mmio.len); |
191 | ||
45e96ea6 CD |
192 | trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE : |
193 | KVM_TRACE_MMIO_READ_UNSATISFIED, | |
194 | mmio.len, fault_ipa, | |
6d89d2d9 | 195 | (mmio.is_write) ? data : 0); |
45e96ea6 CD |
196 | |
197 | if (mmio.is_write) | |
6d89d2d9 | 198 | mmio_write_buf(mmio.data, mmio.len, data); |
45e96ea6 | 199 | |
1a89dd91 MZ |
200 | if (vgic_handle_mmio(vcpu, run, &mmio)) |
201 | return 1; | |
202 | ||
45e96ea6 CD |
203 | kvm_prepare_mmio(run, &mmio); |
204 | return 0; | |
205 | } |