blob: b333b58de4cb8048caf888b259e7c478d98fe195 [file] [log] [blame]
Marc Zyngier1a89dd92013-01-21 19:36:12 -05001/*
2 * Copyright (C) 2012 ARM Ltd.
3 * Author: Marc Zyngier <marc.zyngier@arm.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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19#include <linux/kvm.h>
20#include <linux/kvm_host.h>
21#include <linux/interrupt.h>
22#include <linux/io.h>
23#include <asm/kvm_emulate.h>
24
Christoffer Dall330690c2013-01-21 19:36:13 -050025#define VGIC_ADDR_UNDEF (-1)
26#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
27
Marc Zyngier1a89dd92013-01-21 19:36:12 -050028#define ACCESS_READ_VALUE (1 << 0)
29#define ACCESS_READ_RAZ (0 << 0)
30#define ACCESS_READ_MASK(x) ((x) & (1 << 0))
31#define ACCESS_WRITE_IGNORED (0 << 1)
32#define ACCESS_WRITE_SETBIT (1 << 1)
33#define ACCESS_WRITE_CLEARBIT (2 << 1)
34#define ACCESS_WRITE_VALUE (3 << 1)
35#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
36
37static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask)
38{
39 return *((u32 *)mmio->data) & mask;
40}
41
42static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value)
43{
44 *((u32 *)mmio->data) = value & mask;
45}
46
47/**
48 * vgic_reg_access - access vgic register
49 * @mmio: pointer to the data describing the mmio access
50 * @reg: pointer to the virtual backing of vgic distributor data
51 * @offset: least significant 2 bits used for word offset
52 * @mode: ACCESS_ mode (see defines above)
53 *
54 * Helper to make vgic register access easier using one of the access
55 * modes defined for vgic register access
56 * (read,raz,write-ignored,setbit,clearbit,write)
57 */
58static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
59 phys_addr_t offset, int mode)
60{
61 int word_offset = (offset & 3) * 8;
62 u32 mask = (1UL << (mmio->len * 8)) - 1;
63 u32 regval;
64
65 /*
66 * Any alignment fault should have been delivered to the guest
67 * directly (ARM ARM B3.12.7 "Prioritization of aborts").
68 */
69
70 if (reg) {
71 regval = *reg;
72 } else {
73 BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
74 regval = 0;
75 }
76
77 if (mmio->is_write) {
78 u32 data = mmio_data_read(mmio, mask) << word_offset;
79 switch (ACCESS_WRITE_MASK(mode)) {
80 case ACCESS_WRITE_IGNORED:
81 return;
82
83 case ACCESS_WRITE_SETBIT:
84 regval |= data;
85 break;
86
87 case ACCESS_WRITE_CLEARBIT:
88 regval &= ~data;
89 break;
90
91 case ACCESS_WRITE_VALUE:
92 regval = (regval & ~(mask << word_offset)) | data;
93 break;
94 }
95 *reg = regval;
96 } else {
97 switch (ACCESS_READ_MASK(mode)) {
98 case ACCESS_READ_RAZ:
99 regval = 0;
100 /* fall through */
101
102 case ACCESS_READ_VALUE:
103 mmio_data_write(mmio, mask, regval >> word_offset);
104 }
105 }
106}
107
108/*
109 * I would have liked to use the kvm_bus_io_*() API instead, but it
110 * cannot cope with banked registers (only the VM pointer is passed
111 * around, and we need the vcpu). One of these days, someone please
112 * fix it!
113 */
114struct mmio_range {
115 phys_addr_t base;
116 unsigned long len;
117 bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
118 phys_addr_t offset);
119};
120
121static const struct mmio_range vgic_ranges[] = {
122 {}
123};
124
125static const
126struct mmio_range *find_matching_range(const struct mmio_range *ranges,
127 struct kvm_exit_mmio *mmio,
128 phys_addr_t base)
129{
130 const struct mmio_range *r = ranges;
131 phys_addr_t addr = mmio->phys_addr - base;
132
133 while (r->len) {
134 if (addr >= r->base &&
135 (addr + mmio->len) <= (r->base + r->len))
136 return r;
137 r++;
138 }
139
140 return NULL;
141}
142
143/**
144 * vgic_handle_mmio - handle an in-kernel MMIO access
145 * @vcpu: pointer to the vcpu performing the access
146 * @run: pointer to the kvm_run structure
147 * @mmio: pointer to the data describing the access
148 *
149 * returns true if the MMIO access has been performed in kernel space,
150 * and false if it needs to be emulated in user space.
151 */
152bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
153 struct kvm_exit_mmio *mmio)
154{
155 return KVM_EXIT_MMIO;
156}
Christoffer Dall330690c2013-01-21 19:36:13 -0500157
158static bool vgic_ioaddr_overlap(struct kvm *kvm)
159{
160 phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
161 phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
162
163 if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
164 return 0;
165 if ((dist <= cpu && dist + KVM_VGIC_V2_DIST_SIZE > cpu) ||
166 (cpu <= dist && cpu + KVM_VGIC_V2_CPU_SIZE > dist))
167 return -EBUSY;
168 return 0;
169}
170
171static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
172 phys_addr_t addr, phys_addr_t size)
173{
174 int ret;
175
176 if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
177 return -EEXIST;
178 if (addr + size < addr)
179 return -EINVAL;
180
181 ret = vgic_ioaddr_overlap(kvm);
182 if (ret)
183 return ret;
184 *ioaddr = addr;
185 return ret;
186}
187
188int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
189{
190 int r = 0;
191 struct vgic_dist *vgic = &kvm->arch.vgic;
192
193 if (addr & ~KVM_PHYS_MASK)
194 return -E2BIG;
195
196 if (addr & ~PAGE_MASK)
197 return -EINVAL;
198
199 mutex_lock(&kvm->lock);
200 switch (type) {
201 case KVM_VGIC_V2_ADDR_TYPE_DIST:
202 r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,
203 addr, KVM_VGIC_V2_DIST_SIZE);
204 break;
205 case KVM_VGIC_V2_ADDR_TYPE_CPU:
206 r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
207 addr, KVM_VGIC_V2_CPU_SIZE);
208 break;
209 default:
210 r = -ENODEV;
211 }
212
213 mutex_unlock(&kvm->lock);
214 return r;
215}