mmio.c 4.96 KB
Newer Older
Christoffer Dall's avatar
Christoffer Dall committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * Copyright (C) 2012 - Virtual Open Systems and Columbia University
 * Author: Christoffer Dall <c.dall@virtualopensystems.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <linux/kvm_host.h>
#include <asm/kvm_mmio.h>
#include <asm/kvm_emulate.h>
#include <trace/events/kvm.h>

#include "trace.h"

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
static void mmio_write_buf(char *buf, unsigned int len, unsigned long data)
{
	void *datap = NULL;
	union {
		u8	byte;
		u16	hword;
		u32	word;
		u64	dword;
	} tmp;

	switch (len) {
	case 1:
		tmp.byte	= data;
		datap		= &tmp.byte;
		break;
	case 2:
		tmp.hword	= data;
		datap		= &tmp.hword;
		break;
	case 4:
		tmp.word	= data;
		datap		= &tmp.word;
		break;
	case 8:
		tmp.dword	= data;
		datap		= &tmp.dword;
		break;
	}

	memcpy(buf, datap, len);
}

static unsigned long mmio_read_buf(char *buf, unsigned int len)
{
	unsigned long data = 0;
	union {
		u16	hword;
		u32	word;
		u64	dword;
	} tmp;

	switch (len) {
	case 1:
		data = buf[0];
		break;
	case 2:
		memcpy(&tmp.hword, buf, len);
		data = tmp.hword;
		break;
	case 4:
		memcpy(&tmp.word, buf, len);
		data = tmp.word;
		break;
	case 8:
		memcpy(&tmp.dword, buf, len);
		data = tmp.dword;
		break;
	}

	return data;
}

Christoffer Dall's avatar
Christoffer Dall committed
88
89
90
91
92
93
94
95
96
97
/**
 * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
 * @vcpu: The VCPU pointer
 * @run:  The VCPU run struct containing the mmio data
 *
 * This should only be called after returning from userspace for MMIO load
 * emulation.
 */
int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
98
	unsigned long data;
Christoffer Dall's avatar
Christoffer Dall committed
99
100
101
102
103
	unsigned int len;
	int mask;

	if (!run->mmio.is_write) {
		len = run->mmio.len;
104
		if (len > sizeof(unsigned long))
Christoffer Dall's avatar
Christoffer Dall committed
105
106
			return -EINVAL;

107
		data = mmio_read_buf(run->mmio.data, len);
Christoffer Dall's avatar
Christoffer Dall committed
108

109
110
		if (vcpu->arch.mmio_decode.sign_extend &&
		    len < sizeof(unsigned long)) {
Christoffer Dall's avatar
Christoffer Dall committed
111
			mask = 1U << ((len * 8) - 1);
112
			data = (data ^ mask) - mask;
Christoffer Dall's avatar
Christoffer Dall committed
113
		}
114
115
116
117

		trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
			       data);
		data = vcpu_data_host_to_guest(vcpu, data, len);
118
		vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
Christoffer Dall's avatar
Christoffer Dall committed
119
120
121
122
123
	}

	return 0;
}

124
static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
Christoffer Dall's avatar
Christoffer Dall committed
125
{
126
	unsigned long rt;
127
128
	int access_size;
	bool sign_extend;
Christoffer Dall's avatar
Christoffer Dall committed
129

130
	if (kvm_vcpu_dabt_isextabt(vcpu)) {
Christoffer Dall's avatar
Christoffer Dall committed
131
		/* cache operation on I/O addr, tell guest unsupported */
132
		kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
Christoffer Dall's avatar
Christoffer Dall committed
133
134
135
		return 1;
	}

136
	if (kvm_vcpu_dabt_iss1tw(vcpu)) {
Christoffer Dall's avatar
Christoffer Dall committed
137
		/* page table accesses IO mem: tell guest to fix its TTBR */
138
		kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
Christoffer Dall's avatar
Christoffer Dall committed
139
140
141
		return 1;
	}

142
143
144
	access_size = kvm_vcpu_dabt_get_as(vcpu);
	if (unlikely(access_size < 0))
		return access_size;
Christoffer Dall's avatar
Christoffer Dall committed
145

146
	*is_write = kvm_vcpu_dabt_iswrite(vcpu);
Marc Zyngier's avatar
Marc Zyngier committed
147
	sign_extend = kvm_vcpu_dabt_issext(vcpu);
148
	rt = kvm_vcpu_dabt_get_rd(vcpu);
Christoffer Dall's avatar
Christoffer Dall committed
149

150
	*len = access_size;
Christoffer Dall's avatar
Christoffer Dall committed
151
152
153
154
155
156
157
	vcpu->arch.mmio_decode.sign_extend = sign_extend;
	vcpu->arch.mmio_decode.rt = rt;

	/*
	 * The MMIO instruction is emulated and should not be re-executed
	 * in the guest.
	 */
158
	kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
Christoffer Dall's avatar
Christoffer Dall committed
159
160
161
162
163
164
	return 0;
}

int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
		 phys_addr_t fault_ipa)
{
165
	unsigned long data;
Christoffer Dall's avatar
Christoffer Dall committed
166
167
	unsigned long rt;
	int ret;
168
169
170
	bool is_write;
	int len;
	u8 data_buf[8];
Christoffer Dall's avatar
Christoffer Dall committed
171
172

	/*
173
174
175
	 * Prepare MMIO operation. First decode the syndrome data we get
	 * from the CPU. Then try if some in-kernel emulation feels
	 * responsible, otherwise let user space do its magic.
Christoffer Dall's avatar
Christoffer Dall committed
176
	 */
Marc Zyngier's avatar
Marc Zyngier committed
177
	if (kvm_vcpu_dabt_isvalid(vcpu)) {
178
		ret = decode_hsr(vcpu, &is_write, &len);
Christoffer Dall's avatar
Christoffer Dall committed
179
180
181
182
183
184
185
186
		if (ret)
			return ret;
	} else {
		kvm_err("load/store instruction decoding not implemented\n");
		return -ENOSYS;
	}

	rt = vcpu->arch.mmio_decode.rt;
187

188
	if (is_write) {
189
190
		data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
					       len);
191
192
193

		trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
		mmio_write_buf(data_buf, len, data);
Christoffer Dall's avatar
Christoffer Dall committed
194

195
196
		ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
				       data_buf);
197
	} else {
198
		trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
199
			       fault_ipa, 0);
200
201
202

		ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
				      data_buf);
203
	}
Christoffer Dall's avatar
Christoffer Dall committed
204

205
206
207
208
209
210
211
212
213
	/* Now prepare kvm_run for the potential return to userland. */
	run->mmio.is_write	= is_write;
	run->mmio.phys_addr	= fault_ipa;
	run->mmio.len		= len;
	memcpy(run->mmio.data, data_buf, len);

	if (!ret) {
		/* We handled the access successfully in the kernel. */
		kvm_handle_mmio_return(vcpu, run);
214
		return 1;
215
	}
216

217
	run->exit_reason	= KVM_EXIT_MMIO;
Christoffer Dall's avatar
Christoffer Dall committed
218
219
	return 0;
}