aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/kvm_arm.h3
-rw-r--r--arch/arm/include/asm/kvm_emulate.h6
-rw-r--r--arch/arm/include/asm/kvm_host.h4
-rw-r--r--arch/arm/include/asm/kvm_mmio.h56
-rw-r--r--arch/arm/kvm/Makefile2
-rw-r--r--arch/arm/kvm/arm.c6
-rw-r--r--arch/arm/kvm/mmio.c153
-rw-r--r--arch/arm/kvm/mmu.c7
-rw-r--r--arch/arm/kvm/trace.h21
9 files changed, 255 insertions, 3 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index 9a34c20d41ec..7c3d813e15df 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -173,8 +173,11 @@
173#define HSR_ISS (HSR_IL - 1) 173#define HSR_ISS (HSR_IL - 1)
174#define HSR_ISV_SHIFT (24) 174#define HSR_ISV_SHIFT (24)
175#define HSR_ISV (1U << HSR_ISV_SHIFT) 175#define HSR_ISV (1U << HSR_ISV_SHIFT)
176#define HSR_SRT_SHIFT (16)
177#define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT)
176#define HSR_FSC (0x3f) 178#define HSR_FSC (0x3f)
177#define HSR_FSC_TYPE (0x3c) 179#define HSR_FSC_TYPE (0x3c)
180#define HSR_SSE (1 << 21)
178#define HSR_WNR (1 << 6) 181#define HSR_WNR (1 << 6)
179#define HSR_CV_SHIFT (24) 182#define HSR_CV_SHIFT (24)
180#define HSR_CV (1U << HSR_CV_SHIFT) 183#define HSR_CV (1U << HSR_CV_SHIFT)
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
index 01a755b80632..4c1a073280be 100644
--- a/arch/arm/include/asm/kvm_emulate.h
+++ b/arch/arm/include/asm/kvm_emulate.h
@@ -21,6 +21,7 @@
21 21
22#include <linux/kvm_host.h> 22#include <linux/kvm_host.h>
23#include <asm/kvm_asm.h> 23#include <asm/kvm_asm.h>
24#include <asm/kvm_mmio.h>
24 25
25u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); 26u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
26u32 *vcpu_spsr(struct kvm_vcpu *vcpu); 27u32 *vcpu_spsr(struct kvm_vcpu *vcpu);
@@ -53,4 +54,9 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu)
53 return cpsr_mode > USR_MODE;; 54 return cpsr_mode > USR_MODE;;
54} 55}
55 56
57static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg)
58{
59 return reg == 15;
60}
61
56#endif /* __ARM_KVM_EMULATE_H__ */ 62#endif /* __ARM_KVM_EMULATE_H__ */
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index ed79043d7921..e65fc967a71d 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -21,6 +21,7 @@
21 21
22#include <asm/kvm.h> 22#include <asm/kvm.h>
23#include <asm/kvm_asm.h> 23#include <asm/kvm_asm.h>
24#include <asm/kvm_mmio.h>
24#include <asm/fpstate.h> 25#include <asm/fpstate.h>
25 26
26#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS 27#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
@@ -99,6 +100,9 @@ struct kvm_vcpu_arch {
99 int last_pcpu; 100 int last_pcpu;
100 cpumask_t require_dcache_flush; 101 cpumask_t require_dcache_flush;
101 102
103 /* IO related fields */
104 struct kvm_decode mmio_decode;
105
102 /* Interrupt related fields */ 106 /* Interrupt related fields */
103 u32 irq_lines; /* IRQ and FIQ levels */ 107 u32 irq_lines; /* IRQ and FIQ levels */
104 108
diff --git a/arch/arm/include/asm/kvm_mmio.h b/arch/arm/include/asm/kvm_mmio.h
new file mode 100644
index 000000000000..adcc0d7d3175
--- /dev/null
+++ b/arch/arm/include/asm/kvm_mmio.h
@@ -0,0 +1,56 @@
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#ifndef __ARM_KVM_MMIO_H__
20#define __ARM_KVM_MMIO_H__
21
22#include <linux/kvm_host.h>
23#include <asm/kvm_asm.h>
24#include <asm/kvm_arm.h>
25
26struct kvm_decode {
27 unsigned long rt;
28 bool sign_extend;
29};
30
31/*
32 * The in-kernel MMIO emulation code wants to use a copy of run->mmio,
33 * which is an anonymous type. Use our own type instead.
34 */
35struct kvm_exit_mmio {
36 phys_addr_t phys_addr;
37 u8 data[8];
38 u32 len;
39 bool is_write;
40};
41
42static inline void kvm_prepare_mmio(struct kvm_run *run,
43 struct kvm_exit_mmio *mmio)
44{
45 run->mmio.phys_addr = mmio->phys_addr;
46 run->mmio.len = mmio->len;
47 run->mmio.is_write = mmio->is_write;
48 memcpy(run->mmio.data, mmio->data, mmio->len);
49 run->exit_reason = KVM_EXIT_MMIO;
50}
51
52int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
53int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
54 phys_addr_t fault_ipa);
55
56#endif /* __ARM_KVM_MMIO_H__ */
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index 88edce6c97d4..1e45cd97a7fc 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -18,4 +18,4 @@ kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
18 18
19obj-y += kvm-arm.o init.o interrupts.o 19obj-y += kvm-arm.o init.o interrupts.o
20obj-y += arm.o guest.o mmu.o emulate.o reset.o 20obj-y += arm.o guest.o mmu.o emulate.o reset.o
21obj-y += coproc.o coproc_a15.o 21obj-y += coproc.o coproc_a15.o mmio.o
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index be06c5de51e3..8680b9ffd2ae 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -616,6 +616,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
616 if (ret) 616 if (ret)
617 return ret; 617 return ret;
618 618
619 if (run->exit_reason == KVM_EXIT_MMIO) {
620 ret = kvm_handle_mmio_return(vcpu, vcpu->run);
621 if (ret)
622 return ret;
623 }
624
619 if (vcpu->sigset_active) 625 if (vcpu->sigset_active)
620 sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); 626 sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
621 627
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 */
34int 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
62static 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
119int 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}
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index a4b7b0f900e5..f30e13163a96 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -19,11 +19,13 @@
19#include <linux/mman.h> 19#include <linux/mman.h>
20#include <linux/kvm_host.h> 20#include <linux/kvm_host.h>
21#include <linux/io.h> 21#include <linux/io.h>
22#include <trace/events/kvm.h>
22#include <asm/idmap.h> 23#include <asm/idmap.h>
23#include <asm/pgalloc.h> 24#include <asm/pgalloc.h>
24#include <asm/cacheflush.h> 25#include <asm/cacheflush.h>
25#include <asm/kvm_arm.h> 26#include <asm/kvm_arm.h>
26#include <asm/kvm_mmu.h> 27#include <asm/kvm_mmu.h>
28#include <asm/kvm_mmio.h>
27#include <asm/kvm_asm.h> 29#include <asm/kvm_asm.h>
28#include <asm/kvm_emulate.h> 30#include <asm/kvm_emulate.h>
29#include <asm/mach/map.h> 31#include <asm/mach/map.h>
@@ -624,8 +626,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
624 goto out_unlock; 626 goto out_unlock;
625 } 627 }
626 628
627 kvm_pr_unimpl("I/O address abort..."); 629 /* Adjust page offset */
628 ret = 0; 630 fault_ipa |= vcpu->arch.hxfar & ~PAGE_MASK;
631 ret = io_mem_abort(vcpu, run, fault_ipa);
629 goto out_unlock; 632 goto out_unlock;
630 } 633 }
631 634
diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h
index 624b5a4e8fad..a8e73ed5ad5b 100644
--- a/arch/arm/kvm/trace.h
+++ b/arch/arm/kvm/trace.h
@@ -90,6 +90,27 @@ TRACE_EVENT(kvm_irq_line,
90 __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) 90 __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level)
91); 91);
92 92
93TRACE_EVENT(kvm_mmio_emulate,
94 TP_PROTO(unsigned long vcpu_pc, unsigned long instr,
95 unsigned long cpsr),
96 TP_ARGS(vcpu_pc, instr, cpsr),
97
98 TP_STRUCT__entry(
99 __field( unsigned long, vcpu_pc )
100 __field( unsigned long, instr )
101 __field( unsigned long, cpsr )
102 ),
103
104 TP_fast_assign(
105 __entry->vcpu_pc = vcpu_pc;
106 __entry->instr = instr;
107 __entry->cpsr = cpsr;
108 ),
109
110 TP_printk("Emulate MMIO at: 0x%08lx (instr: %08lx, cpsr: %08lx)",
111 __entry->vcpu_pc, __entry->instr, __entry->cpsr)
112);
113
93/* Architecturally implementation defined CP15 register access */ 114/* Architecturally implementation defined CP15 register access */
94TRACE_EVENT(kvm_emulate_cp15_imp, 115TRACE_EVENT(kvm_emulate_cp15_imp,
95 TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn, 116 TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn,