|
@@ -21,6 +21,7 @@
|
|
#include <linux/kvm.h>
|
|
#include <linux/kvm.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
+#include <linux/uaccess.h>
|
|
|
|
|
|
#include <linux/irqchip/arm-gic-v3.h>
|
|
#include <linux/irqchip/arm-gic-v3.h>
|
|
|
|
|
|
@@ -84,6 +85,9 @@ static int vgic_its_init_its(struct kvm *kvm, struct vgic_its *its)
|
|
struct vgic_io_device *iodev = &its->iodev;
|
|
struct vgic_io_device *iodev = &its->iodev;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
+ if (its->initialized)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base))
|
|
if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base))
|
|
return -ENXIO;
|
|
return -ENXIO;
|
|
|
|
|
|
@@ -99,5 +103,136 @@ static int vgic_its_init_its(struct kvm *kvm, struct vgic_its *its)
|
|
KVM_VGIC_V3_ITS_SIZE, &iodev->dev);
|
|
KVM_VGIC_V3_ITS_SIZE, &iodev->dev);
|
|
mutex_unlock(&kvm->slots_lock);
|
|
mutex_unlock(&kvm->slots_lock);
|
|
|
|
|
|
|
|
+ if (!ret)
|
|
|
|
+ its->initialized = true;
|
|
|
|
+
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+static int vgic_its_create(struct kvm_device *dev, u32 type)
|
|
|
|
+{
|
|
|
|
+ struct vgic_its *its;
|
|
|
|
+
|
|
|
|
+ if (type != KVM_DEV_TYPE_ARM_VGIC_ITS)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ its = kzalloc(sizeof(struct vgic_its), GFP_KERNEL);
|
|
|
|
+ if (!its)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ its->vgic_its_base = VGIC_ADDR_UNDEF;
|
|
|
|
+
|
|
|
|
+ dev->kvm->arch.vgic.has_its = true;
|
|
|
|
+ its->initialized = false;
|
|
|
|
+ its->enabled = false;
|
|
|
|
+
|
|
|
|
+ dev->private = its;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void vgic_its_destroy(struct kvm_device *kvm_dev)
|
|
|
|
+{
|
|
|
|
+ struct vgic_its *its = kvm_dev->private;
|
|
|
|
+
|
|
|
|
+ kfree(its);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int vgic_its_has_attr(struct kvm_device *dev,
|
|
|
|
+ struct kvm_device_attr *attr)
|
|
|
|
+{
|
|
|
|
+ switch (attr->group) {
|
|
|
|
+ case KVM_DEV_ARM_VGIC_GRP_ADDR:
|
|
|
|
+ switch (attr->attr) {
|
|
|
|
+ case KVM_VGIC_ITS_ADDR_TYPE:
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case KVM_DEV_ARM_VGIC_GRP_CTRL:
|
|
|
|
+ switch (attr->attr) {
|
|
|
|
+ case KVM_DEV_ARM_VGIC_CTRL_INIT:
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return -ENXIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int vgic_its_set_attr(struct kvm_device *dev,
|
|
|
|
+ struct kvm_device_attr *attr)
|
|
|
|
+{
|
|
|
|
+ struct vgic_its *its = dev->private;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ switch (attr->group) {
|
|
|
|
+ case KVM_DEV_ARM_VGIC_GRP_ADDR: {
|
|
|
|
+ u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
|
|
|
+ unsigned long type = (unsigned long)attr->attr;
|
|
|
|
+ u64 addr;
|
|
|
|
+
|
|
|
|
+ if (type != KVM_VGIC_ITS_ADDR_TYPE)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ if (its->initialized)
|
|
|
|
+ return -EBUSY;
|
|
|
|
+
|
|
|
|
+ if (copy_from_user(&addr, uaddr, sizeof(addr)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base,
|
|
|
|
+ addr, SZ_64K);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ its->vgic_its_base = addr;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ case KVM_DEV_ARM_VGIC_GRP_CTRL:
|
|
|
|
+ switch (attr->attr) {
|
|
|
|
+ case KVM_DEV_ARM_VGIC_CTRL_INIT:
|
|
|
|
+ return vgic_its_init_its(dev->kvm, its);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return -ENXIO;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int vgic_its_get_attr(struct kvm_device *dev,
|
|
|
|
+ struct kvm_device_attr *attr)
|
|
|
|
+{
|
|
|
|
+ switch (attr->group) {
|
|
|
|
+ case KVM_DEV_ARM_VGIC_GRP_ADDR: {
|
|
|
|
+ struct vgic_its *its = dev->private;
|
|
|
|
+ u64 addr = its->vgic_its_base;
|
|
|
|
+ u64 __user *uaddr = (u64 __user *)(long)attr->addr;
|
|
|
|
+ unsigned long type = (unsigned long)attr->attr;
|
|
|
|
+
|
|
|
|
+ if (type != KVM_VGIC_ITS_ADDR_TYPE)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ if (copy_to_user(uaddr, &addr, sizeof(addr)))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -ENXIO;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct kvm_device_ops kvm_arm_vgic_its_ops = {
|
|
|
|
+ .name = "kvm-arm-vgic-its",
|
|
|
|
+ .create = vgic_its_create,
|
|
|
|
+ .destroy = vgic_its_destroy,
|
|
|
|
+ .set_attr = vgic_its_set_attr,
|
|
|
|
+ .get_attr = vgic_its_get_attr,
|
|
|
|
+ .has_attr = vgic_its_has_attr,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+int kvm_vgic_register_its_device(void)
|
|
|
|
+{
|
|
|
|
+ return kvm_register_device_ops(&kvm_arm_vgic_its_ops,
|
|
|
|
+ KVM_DEV_TYPE_ARM_VGIC_ITS);
|
|
|
|
+}
|