diff options
Diffstat (limited to 'virt/kvm/arm/vgic/vgic-its.c')
-rw-r--r-- | virt/kvm/arm/vgic/vgic-its.c | 135 |
1 files changed, 135 insertions, 0 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 | |||
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 | } | ||