]>
Commit | Line | Data |
---|---|---|
59c5ab40 AP |
1 | /* |
2 | * GICv3 ITS emulation | |
3 | * | |
4 | * Copyright (C) 2015,2016 ARM Ltd. | |
5 | * Author: Andre Przywara <andre.przywara@arm.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <linux/cpu.h> | |
21 | #include <linux/kvm.h> | |
22 | #include <linux/kvm_host.h> | |
23 | #include <linux/interrupt.h> | |
1085fdc6 | 24 | #include <linux/uaccess.h> |
59c5ab40 AP |
25 | |
26 | #include <linux/irqchip/arm-gic-v3.h> | |
27 | ||
28 | #include <asm/kvm_emulate.h> | |
29 | #include <asm/kvm_arm.h> | |
30 | #include <asm/kvm_mmu.h> | |
31 | ||
32 | #include "vgic.h" | |
33 | #include "vgic-mmio.h" | |
34 | ||
35 | #define REGISTER_ITS_DESC(off, rd, wr, length, acc) \ | |
36 | { \ | |
37 | .reg_offset = off, \ | |
38 | .len = length, \ | |
39 | .access_flags = acc, \ | |
40 | .its_read = rd, \ | |
41 | .its_write = wr, \ | |
42 | } | |
43 | ||
44 | static unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its, | |
45 | gpa_t addr, unsigned int len) | |
46 | { | |
47 | return 0; | |
48 | } | |
49 | ||
50 | static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its, | |
51 | gpa_t addr, unsigned int len, unsigned long val) | |
52 | { | |
53 | /* Ignore */ | |
54 | } | |
55 | ||
56 | static struct vgic_register_region its_registers[] = { | |
57 | REGISTER_ITS_DESC(GITS_CTLR, | |
58 | its_mmio_read_raz, its_mmio_write_wi, 4, | |
59 | VGIC_ACCESS_32bit), | |
60 | REGISTER_ITS_DESC(GITS_IIDR, | |
61 | its_mmio_read_raz, its_mmio_write_wi, 4, | |
62 | VGIC_ACCESS_32bit), | |
63 | REGISTER_ITS_DESC(GITS_TYPER, | |
64 | its_mmio_read_raz, its_mmio_write_wi, 8, | |
65 | VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), | |
66 | REGISTER_ITS_DESC(GITS_CBASER, | |
67 | its_mmio_read_raz, its_mmio_write_wi, 8, | |
68 | VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), | |
69 | REGISTER_ITS_DESC(GITS_CWRITER, | |
70 | its_mmio_read_raz, its_mmio_write_wi, 8, | |
71 | VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), | |
72 | REGISTER_ITS_DESC(GITS_CREADR, | |
73 | its_mmio_read_raz, its_mmio_write_wi, 8, | |
74 | VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), | |
75 | REGISTER_ITS_DESC(GITS_BASER, | |
76 | its_mmio_read_raz, its_mmio_write_wi, 0x40, | |
77 | VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), | |
78 | REGISTER_ITS_DESC(GITS_IDREGS_BASE, | |
79 | its_mmio_read_raz, its_mmio_write_wi, 0x30, | |
80 | VGIC_ACCESS_32bit), | |
81 | }; | |
82 | ||
83 | static int vgic_its_init_its(struct kvm *kvm, struct vgic_its *its) | |
84 | { | |
85 | struct vgic_io_device *iodev = &its->iodev; | |
86 | int ret; | |
87 | ||
1085fdc6 AP |
88 | if (its->initialized) |
89 | return 0; | |
90 | ||
59c5ab40 AP |
91 | if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base)) |
92 | return -ENXIO; | |
93 | ||
94 | iodev->regions = its_registers; | |
95 | iodev->nr_regions = ARRAY_SIZE(its_registers); | |
96 | kvm_iodevice_init(&iodev->dev, &kvm_io_gic_ops); | |
97 | ||
98 | iodev->base_addr = its->vgic_its_base; | |
99 | iodev->iodev_type = IODEV_ITS; | |
100 | iodev->its = its; | |
101 | mutex_lock(&kvm->slots_lock); | |
102 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, iodev->base_addr, | |
103 | KVM_VGIC_V3_ITS_SIZE, &iodev->dev); | |
104 | mutex_unlock(&kvm->slots_lock); | |
105 | ||
1085fdc6 AP |
106 | if (!ret) |
107 | its->initialized = true; | |
108 | ||
59c5ab40 AP |
109 | return ret; |
110 | } | |
1085fdc6 AP |
111 | |
112 | static int vgic_its_create(struct kvm_device *dev, u32 type) | |
113 | { | |
114 | struct vgic_its *its; | |
115 | ||
116 | if (type != KVM_DEV_TYPE_ARM_VGIC_ITS) | |
117 | return -ENODEV; | |
118 | ||
119 | its = kzalloc(sizeof(struct vgic_its), GFP_KERNEL); | |
120 | if (!its) | |
121 | return -ENOMEM; | |
122 | ||
123 | its->vgic_its_base = VGIC_ADDR_UNDEF; | |
124 | ||
125 | dev->kvm->arch.vgic.has_its = true; | |
126 | its->initialized = false; | |
127 | its->enabled = false; | |
128 | ||
129 | dev->private = its; | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static void vgic_its_destroy(struct kvm_device *kvm_dev) | |
135 | { | |
136 | struct vgic_its *its = kvm_dev->private; | |
137 | ||
138 | kfree(its); | |
139 | } | |
140 | ||
141 | static int vgic_its_has_attr(struct kvm_device *dev, | |
142 | struct kvm_device_attr *attr) | |
143 | { | |
144 | switch (attr->group) { | |
145 | case KVM_DEV_ARM_VGIC_GRP_ADDR: | |
146 | switch (attr->attr) { | |
147 | case KVM_VGIC_ITS_ADDR_TYPE: | |
148 | return 0; | |
149 | } | |
150 | break; | |
151 | case KVM_DEV_ARM_VGIC_GRP_CTRL: | |
152 | switch (attr->attr) { | |
153 | case KVM_DEV_ARM_VGIC_CTRL_INIT: | |
154 | return 0; | |
155 | } | |
156 | break; | |
157 | } | |
158 | return -ENXIO; | |
159 | } | |
160 | ||
161 | static int vgic_its_set_attr(struct kvm_device *dev, | |
162 | struct kvm_device_attr *attr) | |
163 | { | |
164 | struct vgic_its *its = dev->private; | |
165 | int ret; | |
166 | ||
167 | switch (attr->group) { | |
168 | case KVM_DEV_ARM_VGIC_GRP_ADDR: { | |
169 | u64 __user *uaddr = (u64 __user *)(long)attr->addr; | |
170 | unsigned long type = (unsigned long)attr->attr; | |
171 | u64 addr; | |
172 | ||
173 | if (type != KVM_VGIC_ITS_ADDR_TYPE) | |
174 | return -ENODEV; | |
175 | ||
176 | if (its->initialized) | |
177 | return -EBUSY; | |
178 | ||
179 | if (copy_from_user(&addr, uaddr, sizeof(addr))) | |
180 | return -EFAULT; | |
181 | ||
182 | ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base, | |
183 | addr, SZ_64K); | |
184 | if (ret) | |
185 | return ret; | |
186 | ||
187 | its->vgic_its_base = addr; | |
188 | ||
189 | return 0; | |
190 | } | |
191 | case KVM_DEV_ARM_VGIC_GRP_CTRL: | |
192 | switch (attr->attr) { | |
193 | case KVM_DEV_ARM_VGIC_CTRL_INIT: | |
194 | return vgic_its_init_its(dev->kvm, its); | |
195 | } | |
196 | break; | |
197 | } | |
198 | return -ENXIO; | |
199 | } | |
200 | ||
201 | static int vgic_its_get_attr(struct kvm_device *dev, | |
202 | struct kvm_device_attr *attr) | |
203 | { | |
204 | switch (attr->group) { | |
205 | case KVM_DEV_ARM_VGIC_GRP_ADDR: { | |
206 | struct vgic_its *its = dev->private; | |
207 | u64 addr = its->vgic_its_base; | |
208 | u64 __user *uaddr = (u64 __user *)(long)attr->addr; | |
209 | unsigned long type = (unsigned long)attr->attr; | |
210 | ||
211 | if (type != KVM_VGIC_ITS_ADDR_TYPE) | |
212 | return -ENODEV; | |
213 | ||
214 | if (copy_to_user(uaddr, &addr, sizeof(addr))) | |
215 | return -EFAULT; | |
216 | break; | |
217 | default: | |
218 | return -ENXIO; | |
219 | } | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | static struct kvm_device_ops kvm_arm_vgic_its_ops = { | |
226 | .name = "kvm-arm-vgic-its", | |
227 | .create = vgic_its_create, | |
228 | .destroy = vgic_its_destroy, | |
229 | .set_attr = vgic_its_set_attr, | |
230 | .get_attr = vgic_its_get_attr, | |
231 | .has_attr = vgic_its_has_attr, | |
232 | }; | |
233 | ||
234 | int kvm_vgic_register_its_device(void) | |
235 | { | |
236 | return kvm_register_device_ops(&kvm_arm_vgic_its_ops, | |
237 | KVM_DEV_TYPE_ARM_VGIC_ITS); | |
238 | } |