diff options
author | Will Deacon <will.deacon@arm.com> | 2013-01-24 05:37:49 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2013-01-24 05:37:49 -0500 |
commit | da141b67d29a05267a9a0d56bd7856b7f3f58d44 (patch) | |
tree | 6b39b7036affbb3af421d105a8dbdf9f30a59079 /arch/arm/kvm/mmio.c | |
parent | 6abc749f635005be78dfcb562c2235511965db6d (diff) | |
parent | a749474de5f0f5902f59acb5c7f4dc6b816ac788 (diff) |
Merge branch 'for-will/kvm/core' of git://github.com/virtualopensystems/linux-kvm-arm into for-rmk/virt/kvm/core
Diffstat (limited to 'arch/arm/kvm/mmio.c')
-rw-r--r-- | arch/arm/kvm/mmio.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c new file mode 100644 index 000000000000..0144baf82904 --- /dev/null +++ b/arch/arm/kvm/mmio.c | |||
@@ -0,0 +1,153 @@ | |||
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 | |||
26 | /** | ||
27 | * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation | ||
28 | * @vcpu: The VCPU pointer | ||
29 | * @run: The VCPU run struct containing the mmio data | ||
30 | * | ||
31 | * This should only be called after returning from userspace for MMIO load | ||
32 | * emulation. | ||
33 | */ | ||
34 | int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
35 | { | ||
36 | __u32 *dest; | ||
37 | unsigned int len; | ||
38 | int mask; | ||
39 | |||
40 | if (!run->mmio.is_write) { | ||
41 | dest = vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt); | ||
42 | memset(dest, 0, sizeof(int)); | ||
43 | |||
44 | len = run->mmio.len; | ||
45 | if (len > 4) | ||
46 | return -EINVAL; | ||
47 | |||
48 | memcpy(dest, run->mmio.data, len); | ||
49 | |||
50 | trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, | ||
51 | *((u64 *)run->mmio.data)); | ||
52 | |||
53 | if (vcpu->arch.mmio_decode.sign_extend && len < 4) { | ||
54 | mask = 1U << ((len * 8) - 1); | ||
55 | *dest = (*dest ^ mask) - mask; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, | ||
63 | struct kvm_exit_mmio *mmio) | ||
64 | { | ||
65 | unsigned long rt, len; | ||
66 | bool is_write, sign_extend; | ||
67 | |||
68 | if ((vcpu->arch.hsr >> 8) & 1) { | ||
69 | /* cache operation on I/O addr, tell guest unsupported */ | ||
70 | kvm_inject_dabt(vcpu, vcpu->arch.hxfar); | ||
71 | return 1; | ||
72 | } | ||
73 | |||
74 | if ((vcpu->arch.hsr >> 7) & 1) { | ||
75 | /* page table accesses IO mem: tell guest to fix its TTBR */ | ||
76 | kvm_inject_dabt(vcpu, vcpu->arch.hxfar); | ||
77 | return 1; | ||
78 | } | ||
79 | |||
80 | switch ((vcpu->arch.hsr >> 22) & 0x3) { | ||
81 | case 0: | ||
82 | len = 1; | ||
83 | break; | ||
84 | case 1: | ||
85 | len = 2; | ||
86 | break; | ||
87 | case 2: | ||
88 | len = 4; | ||
89 | break; | ||
90 | default: | ||
91 | kvm_err("Hardware is weird: SAS 0b11 is reserved\n"); | ||
92 | return -EFAULT; | ||
93 | } | ||
94 | |||
95 | is_write = vcpu->arch.hsr & HSR_WNR; | ||
96 | sign_extend = vcpu->arch.hsr & HSR_SSE; | ||
97 | rt = (vcpu->arch.hsr & HSR_SRT_MASK) >> HSR_SRT_SHIFT; | ||
98 | |||
99 | if (kvm_vcpu_reg_is_pc(vcpu, rt)) { | ||
100 | /* IO memory trying to read/write pc */ | ||
101 | kvm_inject_pabt(vcpu, vcpu->arch.hxfar); | ||
102 | return 1; | ||
103 | } | ||
104 | |||
105 | mmio->is_write = is_write; | ||
106 | mmio->phys_addr = fault_ipa; | ||
107 | mmio->len = len; | ||
108 | vcpu->arch.mmio_decode.sign_extend = sign_extend; | ||
109 | vcpu->arch.mmio_decode.rt = rt; | ||
110 | |||
111 | /* | ||
112 | * The MMIO instruction is emulated and should not be re-executed | ||
113 | * in the guest. | ||
114 | */ | ||
115 | kvm_skip_instr(vcpu, (vcpu->arch.hsr >> 25) & 1); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
120 | phys_addr_t fault_ipa) | ||
121 | { | ||
122 | struct kvm_exit_mmio mmio; | ||
123 | unsigned long rt; | ||
124 | int ret; | ||
125 | |||
126 | /* | ||
127 | * Prepare MMIO operation. First stash it in a private | ||
128 | * structure that we can use for in-kernel emulation. If the | ||
129 | * kernel can't handle it, copy it into run->mmio and let user | ||
130 | * space do its magic. | ||
131 | */ | ||
132 | |||
133 | if (vcpu->arch.hsr & HSR_ISV) { | ||
134 | ret = decode_hsr(vcpu, fault_ipa, &mmio); | ||
135 | if (ret) | ||
136 | return ret; | ||
137 | } else { | ||
138 | kvm_err("load/store instruction decoding not implemented\n"); | ||
139 | return -ENOSYS; | ||
140 | } | ||
141 | |||
142 | rt = vcpu->arch.mmio_decode.rt; | ||
143 | trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE : | ||
144 | KVM_TRACE_MMIO_READ_UNSATISFIED, | ||
145 | mmio.len, fault_ipa, | ||
146 | (mmio.is_write) ? *vcpu_reg(vcpu, rt) : 0); | ||
147 | |||
148 | if (mmio.is_write) | ||
149 | memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len); | ||
150 | |||
151 | kvm_prepare_mmio(run, &mmio); | ||
152 | return 0; | ||
153 | } | ||