aboutsummaryrefslogtreecommitdiffstats
path: root/virt/kvm
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2016-07-15 07:43:31 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2016-07-18 13:14:35 -0400
commit1085fdc68c6097244627a02a56bd2d8fe58a1a9c (patch)
tree6a26676301cddc817aa4a4b0ba8eee7e841383f4 /virt/kvm
parent59c5ab40989afa5aba9c4a0918a5ed910a917422 (diff)
KVM: arm64: vgic-its: Introduce new KVM ITS device
Introduce a new KVM device that represents an ARM Interrupt Translation Service (ITS) controller. Since there can be multiple of this per guest, we can't piggy back on the existing GICv3 distributor device, but create a new type of KVM device. On the KVM_CREATE_DEVICE ioctl we allocate and initialize the ITS data structure and store the pointer in the kvm_device data. Upon an explicit init ioctl from userland (after having setup the MMIO address) we register the handlers with the kvm_io_bus framework. Any reference to an ITS thus has to go via this interface. Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'virt/kvm')
-rw-r--r--virt/kvm/arm/vgic/vgic-its.c135
-rw-r--r--virt/kvm/arm/vgic/vgic-kvm-device.c4
-rw-r--r--virt/kvm/arm/vgic/vgic-mmio-v3.c2
-rw-r--r--virt/kvm/arm/vgic/vgic.h3
4 files changed, 141 insertions, 3 deletions
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 4654d6edf6a6..6b47b3674690 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -21,6 +21,7 @@
21#include <linux/kvm.h> 21#include <linux/kvm.h>
22#include <linux/kvm_host.h> 22#include <linux/kvm_host.h>
23#include <linux/interrupt.h> 23#include <linux/interrupt.h>
24#include <linux/uaccess.h>
24 25
25#include <linux/irqchip/arm-gic-v3.h> 26#include <linux/irqchip/arm-gic-v3.h>
26 27
@@ -84,6 +85,9 @@ static int vgic_its_init_its(struct kvm *kvm, struct vgic_its *its)
84 struct vgic_io_device *iodev = &its->iodev; 85 struct vgic_io_device *iodev = &its->iodev;
85 int ret; 86 int ret;
86 87
88 if (its->initialized)
89 return 0;
90
87 if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base)) 91 if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base))
88 return -ENXIO; 92 return -ENXIO;
89 93
@@ -99,5 +103,136 @@ static int vgic_its_init_its(struct kvm *kvm, struct vgic_its *its)
99 KVM_VGIC_V3_ITS_SIZE, &iodev->dev); 103 KVM_VGIC_V3_ITS_SIZE, &iodev->dev);
100 mutex_unlock(&kvm->slots_lock); 104 mutex_unlock(&kvm->slots_lock);
101 105
106 if (!ret)
107 its->initialized = true;
108
102 return ret; 109 return ret;
103} 110}
111
112static 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
134static void vgic_its_destroy(struct kvm_device *kvm_dev)
135{
136 struct vgic_its *its = kvm_dev->private;
137
138 kfree(its);
139}
140
141static 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
161static 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
201static 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
225static 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
234int 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}
diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
index 2f24f13c6c90..561d2ba96a4f 100644
--- a/virt/kvm/arm/vgic/vgic-kvm-device.c
+++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
@@ -21,8 +21,8 @@
21 21
22/* common helpers */ 22/* common helpers */
23 23
24static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr, 24int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
25 phys_addr_t addr, phys_addr_t alignment) 25 phys_addr_t addr, phys_addr_t alignment)
26{ 26{
27 if (addr & ~KVM_PHYS_MASK) 27 if (addr & ~KVM_PHYS_MASK)
28 return -E2BIG; 28 return -E2BIG;
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index b92b7d6cabe6..a5c35050c786 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -49,7 +49,7 @@ bool vgic_has_its(struct kvm *kvm)
49 if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3) 49 if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
50 return false; 50 return false;
51 51
52 return false; 52 return dist->has_its;
53} 53}
54 54
55static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu, 55static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 31807c166d2a..8192a293f119 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -42,6 +42,9 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
42bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq); 42bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
43void vgic_kick_vcpus(struct kvm *kvm); 43void vgic_kick_vcpus(struct kvm *kvm);
44 44
45int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
46 phys_addr_t addr, phys_addr_t alignment);
47
45void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu); 48void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
46void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu); 49void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
47void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr); 50void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);