aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2017-04-25 15:39:26 +0100
committerWill Deacon <will.deacon@arm.com>2017-06-09 11:16:47 +0100
commit12ca14010ba43fc76c3a00bee4c594d417b729e6 (patch)
treecefdf6b1c0d777d2043b66ede21bd2e271502e57
parent04ecf98aa44f5b9c9cab82168db8bd69239a382a (diff)
downloadkvmtool-12ca14010ba43fc76c3a00bee4c594d417b729e6.tar.gz
arm: allow creation of an MSI register frame region
The GICv3 ITS expects a separate 64K page to hold ITS registers. Add a function to reserve such a page in the guest's I/O memory and use that for the ITS vGIC type. To cover the 64K page with the MSI doorbell (which directly follows the page with the register frames), we reserve this as well, although the guest is never expected to write into this. Acked-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arm/gic.c64
-rw-r--r--arm/include/arm-common/gic.h1
2 files changed, 65 insertions, 0 deletions
diff --git a/arm/gic.c b/arm/gic.c
index 8479b89a..95e26a9c 100644
--- a/arm/gic.c
+++ b/arm/gic.c
@@ -8,10 +8,13 @@
#include <linux/byteorder.h>
#include <linux/kernel.h>
#include <linux/kvm.h>
+#include <linux/sizes.h>
static int gic_fd = -1;
static u64 gic_redists_base;
static u64 gic_redists_size;
+static u64 gic_msi_base;
+static u64 gic_msi_size = 0;
int irqchip_parser(const struct option *opt, const char *arg, int unset)
{
@@ -29,6 +32,56 @@ int irqchip_parser(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int gic__create_its_frame(struct kvm *kvm, u64 its_frame_addr)
+{
+ struct kvm_create_device its_device = {
+ .type = KVM_DEV_TYPE_ARM_VGIC_ITS,
+ .flags = 0,
+ };
+ struct kvm_device_attr its_attr = {
+ .group = KVM_DEV_ARM_VGIC_GRP_ADDR,
+ .attr = KVM_VGIC_ITS_ADDR_TYPE,
+ .addr = (u64)(unsigned long)&its_frame_addr,
+ };
+ struct kvm_device_attr its_init_attr = {
+ .group = KVM_DEV_ARM_VGIC_GRP_CTRL,
+ .attr = KVM_DEV_ARM_VGIC_CTRL_INIT,
+ };
+ int err;
+
+ err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &its_device);
+ if (err) {
+ fprintf(stderr,
+ "GICv3 ITS requested, but kernel does not support it.\n");
+ fprintf(stderr, "Try --irqchip=gicv3 instead\n");
+ return err;
+ }
+
+ err = ioctl(its_device.fd, KVM_HAS_DEVICE_ATTR, &its_attr);
+ if (err) {
+ close(its_device.fd);
+ its_device.fd = -1;
+ return err;
+ }
+
+ err = ioctl(its_device.fd, KVM_SET_DEVICE_ATTR, &its_attr);
+ if (err)
+ return err;
+
+ return ioctl(its_device.fd, KVM_SET_DEVICE_ATTR, &its_init_attr);
+}
+
+static int gic__create_msi_frame(struct kvm *kvm, enum irqchip_type type,
+ u64 msi_frame_addr)
+{
+ switch (type) {
+ case IRQCHIP_GICV3_ITS:
+ return gic__create_its_frame(kvm, msi_frame_addr);
+ default: /* No MSI frame needed */
+ return 0;
+ }
+}
+
static int gic__create_device(struct kvm *kvm, enum irqchip_type type)
{
int err;
@@ -58,6 +111,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type)
dist_attr.attr = KVM_VGIC_V2_ADDR_TYPE_DIST;
break;
case IRQCHIP_GICV3:
+ case IRQCHIP_GICV3_ITS:
gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V3;
dist_attr.attr = KVM_VGIC_V3_ADDR_TYPE_DIST;
break;
@@ -73,6 +127,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type)
case IRQCHIP_GICV2:
err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &cpu_if_attr);
break;
+ case IRQCHIP_GICV3_ITS:
case IRQCHIP_GICV3:
err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &redist_attr);
break;
@@ -84,6 +139,10 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type)
if (err)
goto out_err;
+ err = gic__create_msi_frame(kvm, type, gic_msi_base);
+ if (err)
+ goto out_err;
+
return 0;
out_err:
@@ -127,9 +186,14 @@ int gic__create(struct kvm *kvm, enum irqchip_type type)
switch (type) {
case IRQCHIP_GICV2:
break;
+ case IRQCHIP_GICV3_ITS:
+ /* We reserve the 64K page with the doorbell as well. */
+ gic_msi_size = KVM_VGIC_V3_ITS_SIZE + SZ_64K;
+ /* fall through */
case IRQCHIP_GICV3:
gic_redists_size = kvm->cfg.nrcpus * ARM_GIC_REDIST_SIZE;
gic_redists_base = ARM_GIC_DIST_BASE - gic_redists_size;
+ gic_msi_base = gic_redists_base - gic_msi_size;
break;
default:
return -ENODEV;
diff --git a/arm/include/arm-common/gic.h b/arm/include/arm-common/gic.h
index b43a180a..433dd237 100644
--- a/arm/include/arm-common/gic.h
+++ b/arm/include/arm-common/gic.h
@@ -24,6 +24,7 @@
enum irqchip_type {
IRQCHIP_GICV2,
IRQCHIP_GICV3,
+ IRQCHIP_GICV3_ITS,
};
struct kvm;