All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andre Przywara <andre.przywara@arm.com>
To: Christoffer Dall <christoffer.dall@linaro.org>,
	Marc Zyngier <marc.zyngier@arm.com>,
	Eric Auger <eric.auger@linaro.org>
Cc: linux-arm-kernel@lists.infradead.org,
	kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org
Subject: [PATCH v4 06/12] KVM: arm64: implement basic ITS register handlers
Date: Sat, 26 Mar 2016 02:14:04 +0000	[thread overview]
Message-ID: <1458958450-19662-7-git-send-email-andre.przywara@arm.com> (raw)
In-Reply-To: <1458958450-19662-1-git-send-email-andre.przywara@arm.com>

Add emulation for some basic MMIO registers used in the ITS emulation.
This includes:
- GITS_{CTLR,TYPER,IIDR}
- ID registers
- GITS_{CBASER,CREADR,CWRITER}
  those implement the ITS command buffer handling

Most of the handlers are pretty straight forward, but CWRITER goes
some extra miles to allow fine grained locking. The idea here
is to let only the first instance iterate through the command ring
buffer, CWRITER accesses on other VCPUs meanwhile will be picked up
by that first instance and handled as well. The ITS lock is thus only
hold for very small periods of time and is dropped before the actual
command handler is called.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/vgic/vgic.h            |   3 +
 include/linux/irqchip/arm-gic-v3.h |   8 ++
 virt/kvm/arm/vgic/its-emul.c       | 272 ++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic.h           |   6 +
 virt/kvm/arm/vgic/vgic_init.c      |   2 +
 5 files changed, 284 insertions(+), 7 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index c79bed5..bafea11 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -115,6 +115,9 @@ struct vgic_io_device {
 struct vgic_its {
 	bool			enabled;
 	spinlock_t		lock;
+	u64			cbaser;
+	int			creadr;
+	int			cwriter;
 };
 
 struct vgic_dist {
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index a813c3e..7011b98 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -179,15 +179,23 @@
 #define GITS_BASER			0x0100
 #define GITS_IDREGS_BASE		0xffd0
 #define GITS_PIDR2			GICR_PIDR2
+#define GITS_PIDR4			0xffd0
+#define GITS_CIDR0			0xfff0
+#define GITS_CIDR1			0xfff4
+#define GITS_CIDR2			0xfff8
+#define GITS_CIDR3			0xfffc
 
 #define GITS_TRANSLATER			0x10040
 
 #define GITS_CTLR_ENABLE		(1U << 0)
 #define GITS_CTLR_QUIESCENT		(1U << 31)
 
+#define GITS_TYPER_PLPIS		(1UL << 0)
+#define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
 #define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
 #define GITS_TYPER_PTA			(1UL << 19)
+#define GITS_TYPER_HWCOLLCNT_SHIFT	24
 
 #define GITS_CBASER_VALID		(1UL << 63)
 #define GITS_CBASER_nCnB		(0UL << 59)
diff --git a/virt/kvm/arm/vgic/its-emul.c b/virt/kvm/arm/vgic/its-emul.c
index 49dd5e4..de8d360 100644
--- a/virt/kvm/arm/vgic/its-emul.c
+++ b/virt/kvm/arm/vgic/its-emul.c
@@ -31,23 +31,263 @@
 #include "vgic.h"
 #include "vgic_mmio.h"
 
+#define BASER_BASE_ADDRESS(x) ((x) & 0xfffffffff000ULL)
+
+static int vgic_mmio_read_its_ctlr(struct kvm_vcpu *vcpu,
+				   struct kvm_io_device *this,
+				   gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u32 reg;
+
+	reg = GITS_CTLR_QUIESCENT;
+	if (its->enabled)
+		reg |= GITS_CTLR_ENABLE;
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_write_its_ctlr(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, const void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	struct vgic_io_device *iodev = container_of(this,
+						    struct vgic_io_device, dev);
+
+        if (addr - iodev->base_addr == 0)
+		its->enabled = !!(*(u8*)val & GITS_CTLR_ENABLE);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_typer(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, void *val)
+{
+	u64 reg = GITS_TYPER_PLPIS;
+
+	/*
+	 * We use linear CPU numbers for redistributor addressing,
+	 * so GITS_TYPER.PTA is 0.
+	 * To avoid memory waste on the guest side, we keep the
+	 * number of IDBits and DevBits low for the time being.
+	 * This could later be made configurable by userland.
+	 * Since we have all collections in linked list, we claim
+	 * that we can hold all of the collection tables in our
+	 * own memory and that the ITT entry size is 1 byte (the
+	 * smallest possible one).
+	 */
+	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;
+	reg |= 0x0f << GITS_TYPER_DEVBITS_SHIFT;
+	reg |= 0x0f << GITS_TYPER_IDBITS_SHIFT;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_iidr(struct kvm_vcpu *vcpu,
+				   struct kvm_io_device *this,
+				   gpa_t addr, int len, void *val)
+{
+	u32 reg = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_idregs(struct kvm_vcpu *vcpu,
+				     struct kvm_io_device *this,
+				     gpa_t addr, int len, void *val)
+{
+	struct vgic_io_device *iodev = container_of(this,
+						    struct vgic_io_device, dev);
+	u32 reg = 0;
+	int idreg = (addr & ~3) - iodev->base_addr + GITS_IDREGS_BASE;
+
+	switch (idreg) {
+	case GITS_PIDR2:
+		reg = GIC_PIDR2_ARCH_GICv3;
+		break;
+	case GITS_PIDR4:
+		/* This is a 64K software visible page */
+		reg = 0x40;
+		break;
+	/* Those are the ID registers for (any) GIC. */
+	case GITS_CIDR0:
+		reg = 0x0d;
+		break;
+	case GITS_CIDR1:
+		reg = 0xf0;
+		break;
+	case GITS_CIDR2:
+		reg = 0x05;
+		break;
+	case GITS_CIDR3:
+		reg = 0xb1;
+		break;
+	}
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+/*
+ * This function is called with both the ITS and the distributor lock dropped,
+ * so the actual command handlers must take the respective locks when needed.
+ */
+static int vits_handle_command(struct kvm_vcpu *vcpu, u64 *its_cmd)
+{
+	return -ENODEV;
+}
+
+static int vgic_mmio_read_its_cbaser(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+
+	write_mask64(its->cbaser, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_write_its_cbaser(struct kvm_vcpu *vcpu,
+				      struct kvm_io_device *this,
+				      gpa_t addr, int len, const void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+
+	if (its->enabled)
+		return 0;
+
+	its->cbaser = mask64(its->cbaser, addr & 7, len, val);
+	its->creadr = 0;
+
+	return 0;
+}
+
+static int its_cmd_buffer_size(struct kvm *kvm)
+{
+	struct vgic_its *its = &kvm->arch.vgic.its;
+
+	return ((its->cbaser & 0xff) + 1) << 12;
+}
+
+static gpa_t its_cmd_buffer_base(struct kvm *kvm)
+{
+	struct vgic_its *its = &kvm->arch.vgic.its;
+
+	return BASER_BASE_ADDRESS(its->cbaser);
+}
+
+/*
+ * By writing to CWRITER the guest announces new commands to be processed.
+ * Since we cannot read from guest memory inside the ITS spinlock, we
+ * iterate over the command buffer (with the lock dropped) until the read
+ * pointer matches the write pointer. Other VCPUs writing this register in the
+ * meantime will just update the write pointer, leaving the command
+ * processing to the first instance of the function.
+ */
+static int vgic_mmio_write_its_cwriter(struct kvm_vcpu *vcpu,
+				       struct kvm_io_device *this,
+				       gpa_t addr, int len, const void *val)
+{
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	struct vgic_its *its = &dist->its;
+	gpa_t cbaser = its_cmd_buffer_base(vcpu->kvm);
+	u64 cmd_buf[4];
+	u32 reg;
+	bool finished;
+
+	reg = mask64(its->cwriter & 0xfffe0, addr & 7, len, val);
+	reg &= 0xfffe0;
+	if (reg > its_cmd_buffer_size(vcpu->kvm))
+		return 0;
+
+	spin_lock(&its->lock);
+
+	/*
+	 * If there is still another VCPU handling commands, let this
+	 * one pick up the new CWRITER and process "our" new commands as well.
+	 */
+	finished = (its->cwriter != its->creadr);
+	its->cwriter = reg;
+
+	spin_unlock(&its->lock);
+
+	while (!finished) {
+		int ret = kvm_read_guest(vcpu->kvm, cbaser + its->creadr,
+					 cmd_buf, 32);
+		if (ret) {
+			/*
+			 * Gah, we are screwed. Reset CWRITER to that command
+			 * that we have finished processing and return.
+			 */
+			spin_lock(&its->lock);
+			its->cwriter = its->creadr;
+			spin_unlock(&its->lock);
+			break;
+		}
+		vits_handle_command(vcpu, cmd_buf);
+
+		spin_lock(&its->lock);
+		its->creadr += 32;
+		if (its->creadr == its_cmd_buffer_size(vcpu->kvm))
+			its->creadr = 0;
+		finished = (its->creadr == its->cwriter);
+		spin_unlock(&its->lock);
+	}
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_cwriter(struct kvm_vcpu *vcpu,
+				      struct kvm_io_device *this,
+				      gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u64 reg = its->cwriter & 0xfffe0;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_creadr(struct kvm_vcpu *vcpu,
+				     struct kvm_io_device *this,
+				     gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u64 reg = its->creadr & 0xfffe0;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
 struct vgic_register_region its_registers[] = {
 	REGISTER_DESC_WITH_LENGTH(GITS_CTLR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_ctlr, vgic_mmio_write_its_ctlr, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_IIDR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_iidr, vgic_mmio_write_wi, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_TYPER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_typer, vgic_mmio_write_wi, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_CBASER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_cbaser, vgic_mmio_write_its_cbaser, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_CWRITER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_cwriter, vgic_mmio_write_its_cwriter, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_CREADR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_creadr, vgic_mmio_write_wi, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_BASER,
 		vgic_mmio_read_raz, vgic_mmio_write_wi, 0x40),
 	REGISTER_DESC_WITH_LENGTH(GITS_IDREGS_BASE,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 0x30),
+		vgic_mmio_read_its_idregs, vgic_mmio_write_wi, 0x30),
 };
 
 /* This is called on setting the LPI enable bit in the redistributor. */
@@ -59,9 +299,14 @@ int vits_init(struct kvm *kvm)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
 	struct vgic_its *its = &dist->its;
+	int nr_vcpus = atomic_read(&kvm->online_vcpus);
 	struct vgic_io_device *regions;
 	int ret, i;
 
+	dist->pendbaser = kcalloc(nr_vcpus, sizeof(u64), GFP_KERNEL);
+	if (!dist->pendbaser)
+		return -ENOMEM;
+
 	spin_lock_init(&its->lock);
 
 	regions = kmalloc_array(ARRAY_SIZE(its_registers),
@@ -82,3 +327,16 @@ int vits_init(struct kvm *kvm)
 
 	return -ENXIO;
 }
+
+void vits_destroy(struct kvm *kvm)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_its *its = &dist->its;
+
+	if (!vgic_has_its(kvm))
+		return;
+
+	kfree(dist->pendbaser);
+
+	its->enabled = false;
+}
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 4e7dcb8..08f97d1 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -63,6 +63,7 @@ int vgic_register_redist_regions(struct kvm *kvm, gpa_t dist_base_address);
 
 int vits_init(struct kvm *kvm);
 void vgic_enable_lpis(struct kvm_vcpu *vcpu);
+void vits_destroy(struct kvm *kvm);
 #else
 static inline void vgic_v3_irq_change_affinity(struct kvm *kvm, u32 intid,
 					       u64 mpidr)
@@ -137,6 +138,11 @@ static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
 {
 	return;
 }
+
+static inline void vits_destroy(struct kvm *kvm)
+{
+	return;
+}
 #endif
 
 void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
diff --git a/virt/kvm/arm/vgic/vgic_init.c b/virt/kvm/arm/vgic/vgic_init.c
index dcfb93d..e4459e3 100644
--- a/virt/kvm/arm/vgic/vgic_init.c
+++ b/virt/kvm/arm/vgic/vgic_init.c
@@ -298,6 +298,8 @@ void kvm_vgic_destroy(struct kvm *kvm)
 
 	kvm_vgic_dist_destroy(kvm);
 
+	vits_destroy(kvm);
+
 	kvm_for_each_vcpu(i, vcpu, kvm)
 		kvm_vgic_vcpu_destroy(vcpu);
 }
-- 
2.7.3


WARNING: multiple messages have this Message-ID (diff)
From: andre.przywara@arm.com (Andre Przywara)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v4 06/12] KVM: arm64: implement basic ITS register handlers
Date: Sat, 26 Mar 2016 02:14:04 +0000	[thread overview]
Message-ID: <1458958450-19662-7-git-send-email-andre.przywara@arm.com> (raw)
In-Reply-To: <1458958450-19662-1-git-send-email-andre.przywara@arm.com>

Add emulation for some basic MMIO registers used in the ITS emulation.
This includes:
- GITS_{CTLR,TYPER,IIDR}
- ID registers
- GITS_{CBASER,CREADR,CWRITER}
  those implement the ITS command buffer handling

Most of the handlers are pretty straight forward, but CWRITER goes
some extra miles to allow fine grained locking. The idea here
is to let only the first instance iterate through the command ring
buffer, CWRITER accesses on other VCPUs meanwhile will be picked up
by that first instance and handled as well. The ITS lock is thus only
hold for very small periods of time and is dropped before the actual
command handler is called.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/vgic/vgic.h            |   3 +
 include/linux/irqchip/arm-gic-v3.h |   8 ++
 virt/kvm/arm/vgic/its-emul.c       | 272 ++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic.h           |   6 +
 virt/kvm/arm/vgic/vgic_init.c      |   2 +
 5 files changed, 284 insertions(+), 7 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index c79bed5..bafea11 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -115,6 +115,9 @@ struct vgic_io_device {
 struct vgic_its {
 	bool			enabled;
 	spinlock_t		lock;
+	u64			cbaser;
+	int			creadr;
+	int			cwriter;
 };
 
 struct vgic_dist {
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index a813c3e..7011b98 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -179,15 +179,23 @@
 #define GITS_BASER			0x0100
 #define GITS_IDREGS_BASE		0xffd0
 #define GITS_PIDR2			GICR_PIDR2
+#define GITS_PIDR4			0xffd0
+#define GITS_CIDR0			0xfff0
+#define GITS_CIDR1			0xfff4
+#define GITS_CIDR2			0xfff8
+#define GITS_CIDR3			0xfffc
 
 #define GITS_TRANSLATER			0x10040
 
 #define GITS_CTLR_ENABLE		(1U << 0)
 #define GITS_CTLR_QUIESCENT		(1U << 31)
 
+#define GITS_TYPER_PLPIS		(1UL << 0)
+#define GITS_TYPER_IDBITS_SHIFT		8
 #define GITS_TYPER_DEVBITS_SHIFT	13
 #define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
 #define GITS_TYPER_PTA			(1UL << 19)
+#define GITS_TYPER_HWCOLLCNT_SHIFT	24
 
 #define GITS_CBASER_VALID		(1UL << 63)
 #define GITS_CBASER_nCnB		(0UL << 59)
diff --git a/virt/kvm/arm/vgic/its-emul.c b/virt/kvm/arm/vgic/its-emul.c
index 49dd5e4..de8d360 100644
--- a/virt/kvm/arm/vgic/its-emul.c
+++ b/virt/kvm/arm/vgic/its-emul.c
@@ -31,23 +31,263 @@
 #include "vgic.h"
 #include "vgic_mmio.h"
 
+#define BASER_BASE_ADDRESS(x) ((x) & 0xfffffffff000ULL)
+
+static int vgic_mmio_read_its_ctlr(struct kvm_vcpu *vcpu,
+				   struct kvm_io_device *this,
+				   gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u32 reg;
+
+	reg = GITS_CTLR_QUIESCENT;
+	if (its->enabled)
+		reg |= GITS_CTLR_ENABLE;
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_write_its_ctlr(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, const void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	struct vgic_io_device *iodev = container_of(this,
+						    struct vgic_io_device, dev);
+
+        if (addr - iodev->base_addr == 0)
+		its->enabled = !!(*(u8*)val & GITS_CTLR_ENABLE);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_typer(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, void *val)
+{
+	u64 reg = GITS_TYPER_PLPIS;
+
+	/*
+	 * We use linear CPU numbers for redistributor addressing,
+	 * so GITS_TYPER.PTA is 0.
+	 * To avoid memory waste on the guest side, we keep the
+	 * number of IDBits and DevBits low for the time being.
+	 * This could later be made configurable by userland.
+	 * Since we have all collections in linked list, we claim
+	 * that we can hold all of the collection tables in our
+	 * own memory and that the ITT entry size is 1 byte (the
+	 * smallest possible one).
+	 */
+	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;
+	reg |= 0x0f << GITS_TYPER_DEVBITS_SHIFT;
+	reg |= 0x0f << GITS_TYPER_IDBITS_SHIFT;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_iidr(struct kvm_vcpu *vcpu,
+				   struct kvm_io_device *this,
+				   gpa_t addr, int len, void *val)
+{
+	u32 reg = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_idregs(struct kvm_vcpu *vcpu,
+				     struct kvm_io_device *this,
+				     gpa_t addr, int len, void *val)
+{
+	struct vgic_io_device *iodev = container_of(this,
+						    struct vgic_io_device, dev);
+	u32 reg = 0;
+	int idreg = (addr & ~3) - iodev->base_addr + GITS_IDREGS_BASE;
+
+	switch (idreg) {
+	case GITS_PIDR2:
+		reg = GIC_PIDR2_ARCH_GICv3;
+		break;
+	case GITS_PIDR4:
+		/* This is a 64K software visible page */
+		reg = 0x40;
+		break;
+	/* Those are the ID registers for (any) GIC. */
+	case GITS_CIDR0:
+		reg = 0x0d;
+		break;
+	case GITS_CIDR1:
+		reg = 0xf0;
+		break;
+	case GITS_CIDR2:
+		reg = 0x05;
+		break;
+	case GITS_CIDR3:
+		reg = 0xb1;
+		break;
+	}
+
+	write_mask32(reg, addr & 3, len, val);
+
+	return 0;
+}
+
+/*
+ * This function is called with both the ITS and the distributor lock dropped,
+ * so the actual command handlers must take the respective locks when needed.
+ */
+static int vits_handle_command(struct kvm_vcpu *vcpu, u64 *its_cmd)
+{
+	return -ENODEV;
+}
+
+static int vgic_mmio_read_its_cbaser(struct kvm_vcpu *vcpu,
+				    struct kvm_io_device *this,
+				    gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+
+	write_mask64(its->cbaser, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_write_its_cbaser(struct kvm_vcpu *vcpu,
+				      struct kvm_io_device *this,
+				      gpa_t addr, int len, const void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+
+	if (its->enabled)
+		return 0;
+
+	its->cbaser = mask64(its->cbaser, addr & 7, len, val);
+	its->creadr = 0;
+
+	return 0;
+}
+
+static int its_cmd_buffer_size(struct kvm *kvm)
+{
+	struct vgic_its *its = &kvm->arch.vgic.its;
+
+	return ((its->cbaser & 0xff) + 1) << 12;
+}
+
+static gpa_t its_cmd_buffer_base(struct kvm *kvm)
+{
+	struct vgic_its *its = &kvm->arch.vgic.its;
+
+	return BASER_BASE_ADDRESS(its->cbaser);
+}
+
+/*
+ * By writing to CWRITER the guest announces new commands to be processed.
+ * Since we cannot read from guest memory inside the ITS spinlock, we
+ * iterate over the command buffer (with the lock dropped) until the read
+ * pointer matches the write pointer. Other VCPUs writing this register in the
+ * meantime will just update the write pointer, leaving the command
+ * processing to the first instance of the function.
+ */
+static int vgic_mmio_write_its_cwriter(struct kvm_vcpu *vcpu,
+				       struct kvm_io_device *this,
+				       gpa_t addr, int len, const void *val)
+{
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	struct vgic_its *its = &dist->its;
+	gpa_t cbaser = its_cmd_buffer_base(vcpu->kvm);
+	u64 cmd_buf[4];
+	u32 reg;
+	bool finished;
+
+	reg = mask64(its->cwriter & 0xfffe0, addr & 7, len, val);
+	reg &= 0xfffe0;
+	if (reg > its_cmd_buffer_size(vcpu->kvm))
+		return 0;
+
+	spin_lock(&its->lock);
+
+	/*
+	 * If there is still another VCPU handling commands, let this
+	 * one pick up the new CWRITER and process "our" new commands as well.
+	 */
+	finished = (its->cwriter != its->creadr);
+	its->cwriter = reg;
+
+	spin_unlock(&its->lock);
+
+	while (!finished) {
+		int ret = kvm_read_guest(vcpu->kvm, cbaser + its->creadr,
+					 cmd_buf, 32);
+		if (ret) {
+			/*
+			 * Gah, we are screwed. Reset CWRITER to that command
+			 * that we have finished processing and return.
+			 */
+			spin_lock(&its->lock);
+			its->cwriter = its->creadr;
+			spin_unlock(&its->lock);
+			break;
+		}
+		vits_handle_command(vcpu, cmd_buf);
+
+		spin_lock(&its->lock);
+		its->creadr += 32;
+		if (its->creadr == its_cmd_buffer_size(vcpu->kvm))
+			its->creadr = 0;
+		finished = (its->creadr == its->cwriter);
+		spin_unlock(&its->lock);
+	}
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_cwriter(struct kvm_vcpu *vcpu,
+				      struct kvm_io_device *this,
+				      gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u64 reg = its->cwriter & 0xfffe0;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
+static int vgic_mmio_read_its_creadr(struct kvm_vcpu *vcpu,
+				     struct kvm_io_device *this,
+				     gpa_t addr, int len, void *val)
+{
+	struct vgic_its *its = &vcpu->kvm->arch.vgic.its;
+	u64 reg = its->creadr & 0xfffe0;
+
+	write_mask64(reg, addr & 7, len, val);
+
+	return 0;
+}
+
 struct vgic_register_region its_registers[] = {
 	REGISTER_DESC_WITH_LENGTH(GITS_CTLR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_ctlr, vgic_mmio_write_its_ctlr, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_IIDR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_iidr, vgic_mmio_write_wi, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_TYPER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4),
+		vgic_mmio_read_its_typer, vgic_mmio_write_wi, 4),
 	REGISTER_DESC_WITH_LENGTH(GITS_CBASER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_cbaser, vgic_mmio_write_its_cbaser, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_CWRITER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_cwriter, vgic_mmio_write_its_cwriter, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_CREADR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8),
+		vgic_mmio_read_its_creadr, vgic_mmio_write_wi, 8),
 	REGISTER_DESC_WITH_LENGTH(GITS_BASER,
 		vgic_mmio_read_raz, vgic_mmio_write_wi, 0x40),
 	REGISTER_DESC_WITH_LENGTH(GITS_IDREGS_BASE,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 0x30),
+		vgic_mmio_read_its_idregs, vgic_mmio_write_wi, 0x30),
 };
 
 /* This is called on setting the LPI enable bit in the redistributor. */
@@ -59,9 +299,14 @@ int vits_init(struct kvm *kvm)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
 	struct vgic_its *its = &dist->its;
+	int nr_vcpus = atomic_read(&kvm->online_vcpus);
 	struct vgic_io_device *regions;
 	int ret, i;
 
+	dist->pendbaser = kcalloc(nr_vcpus, sizeof(u64), GFP_KERNEL);
+	if (!dist->pendbaser)
+		return -ENOMEM;
+
 	spin_lock_init(&its->lock);
 
 	regions = kmalloc_array(ARRAY_SIZE(its_registers),
@@ -82,3 +327,16 @@ int vits_init(struct kvm *kvm)
 
 	return -ENXIO;
 }
+
+void vits_destroy(struct kvm *kvm)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_its *its = &dist->its;
+
+	if (!vgic_has_its(kvm))
+		return;
+
+	kfree(dist->pendbaser);
+
+	its->enabled = false;
+}
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 4e7dcb8..08f97d1 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -63,6 +63,7 @@ int vgic_register_redist_regions(struct kvm *kvm, gpa_t dist_base_address);
 
 int vits_init(struct kvm *kvm);
 void vgic_enable_lpis(struct kvm_vcpu *vcpu);
+void vits_destroy(struct kvm *kvm);
 #else
 static inline void vgic_v3_irq_change_affinity(struct kvm *kvm, u32 intid,
 					       u64 mpidr)
@@ -137,6 +138,11 @@ static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
 {
 	return;
 }
+
+static inline void vits_destroy(struct kvm *kvm)
+{
+	return;
+}
 #endif
 
 void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
diff --git a/virt/kvm/arm/vgic/vgic_init.c b/virt/kvm/arm/vgic/vgic_init.c
index dcfb93d..e4459e3 100644
--- a/virt/kvm/arm/vgic/vgic_init.c
+++ b/virt/kvm/arm/vgic/vgic_init.c
@@ -298,6 +298,8 @@ void kvm_vgic_destroy(struct kvm *kvm)
 
 	kvm_vgic_dist_destroy(kvm);
 
+	vits_destroy(kvm);
+
 	kvm_for_each_vcpu(i, vcpu, kvm)
 		kvm_vgic_vcpu_destroy(vcpu);
 }
-- 
2.7.3

  parent reply	other threads:[~2016-03-26  2:14 UTC|newest]

Thread overview: 120+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-26  2:13 [PATCH v4 00/12] KVM: arm64: GICv3 ITS emulation Andre Przywara
2016-03-26  2:13 ` Andre Przywara
2016-03-26  2:13 ` [PATCH v4 01/12] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
2016-03-26  2:13   ` Andre Przywara
2016-04-03  9:15   ` Christoffer Dall
2016-04-03  9:15     ` Christoffer Dall
2016-05-25 15:55     ` Andre Przywara
2016-05-25 15:55       ` Andre Przywara
2016-05-25 16:16       ` Marc Zyngier
2016-05-25 16:16         ` Marc Zyngier
2016-05-31 13:05         ` Christoffer Dall
2016-05-31 13:05           ` Christoffer Dall
2016-05-05 17:55   ` Chalamarla, Tirumalesh
2016-05-05 17:55     ` Chalamarla, Tirumalesh
2016-03-26  2:14 ` [PATCH v4 02/12] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-03 10:08   ` Christoffer Dall
2016-04-03 10:08     ` Christoffer Dall
2016-04-05 12:48   ` Eric Auger
2016-04-05 12:48     ` Eric Auger
2016-05-05 18:00   ` Chalamarla, Tirumalesh
2016-05-05 18:00     ` Chalamarla, Tirumalesh
2016-03-26  2:14 ` [PATCH v4 03/12] KVM: arm64: Introduce new MMIO region for the ITS base address Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-03 10:08   ` Christoffer Dall
2016-04-03 10:08     ` Christoffer Dall
2016-04-05 12:47     ` Eric Auger
2016-04-05 12:47       ` Eric Auger
2016-04-07 13:44   ` Marc Zyngier
2016-04-07 13:44     ` Marc Zyngier
2016-05-05 18:08   ` Chalamarla, Tirumalesh
2016-05-05 18:08     ` Chalamarla, Tirumalesh
2016-05-09 15:47     ` Marc Zyngier
2016-05-09 15:47       ` Marc Zyngier
2016-05-09 16:53       ` Chalamarla, Tirumalesh
2016-05-09 16:53         ` Chalamarla, Tirumalesh
2016-05-09 17:09         ` Marc Zyngier
2016-05-09 17:09           ` Marc Zyngier
2016-03-26  2:14 ` [PATCH v4 04/12] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-03 10:08   ` Christoffer Dall
2016-04-03 10:08     ` Christoffer Dall
2016-04-05 12:55     ` Eric Auger
2016-04-05 12:55       ` Eric Auger
2016-04-05 15:17   ` Eric Auger
2016-04-05 15:17     ` Eric Auger
2016-04-07 13:54   ` Marc Zyngier
2016-04-07 13:54     ` Marc Zyngier
2016-04-07 13:58     ` Marc Zyngier
2016-04-07 13:58       ` Marc Zyngier
2016-05-05 18:06       ` Chalamarla, Tirumalesh
2016-05-05 18:06         ` Chalamarla, Tirumalesh
2016-03-26  2:14 ` [PATCH v4 05/12] KVM: arm64: introduce ITS emulation file with stub functions Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-05 16:03   ` Eric Auger
2016-04-05 16:03     ` Eric Auger
2016-04-07 14:04   ` Marc Zyngier
2016-04-07 14:04     ` Marc Zyngier
2016-04-07 14:08     ` Eric Auger
2016-04-07 14:08       ` Eric Auger
2016-04-07 14:48       ` Marc Zyngier
2016-04-07 14:48         ` Marc Zyngier
2016-04-07 15:09         ` Eric Auger
2016-04-07 15:09           ` Eric Auger
2016-04-07 15:19           ` Marc Zyngier
2016-04-07 15:19             ` Marc Zyngier
2016-03-26  2:14 ` Andre Przywara [this message]
2016-03-26  2:14   ` [PATCH v4 06/12] KVM: arm64: implement basic ITS register handlers Andre Przywara
2016-04-03 10:08   ` Christoffer Dall
2016-04-03 10:08     ` Christoffer Dall
2016-04-06  9:36   ` Eric Auger
2016-04-06  9:36     ` Eric Auger
2016-05-25 13:49     ` Andre Przywara
2016-05-25 13:49       ` Andre Przywara
2016-04-07 14:35   ` Marc Zyngier
2016-04-07 14:35     ` Marc Zyngier
2016-05-25 11:37     ` Andre Przywara
2016-05-25 11:37       ` Andre Przywara
2016-05-26  9:10       ` Marc Zyngier
2016-05-26  9:10         ` Marc Zyngier
2016-06-03 15:42         ` Andre Przywara
2016-06-03 15:42           ` Andre Przywara
2016-06-03 16:54           ` Marc Zyngier
2016-06-03 16:54             ` Marc Zyngier
2016-05-05 18:51   ` Chalamarla, Tirumalesh
2016-05-05 18:51     ` Chalamarla, Tirumalesh
2016-03-26  2:14 ` [PATCH v4 07/12] KVM: arm64: add data structures to model ITS interrupt translation Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-06  9:53   ` Eric Auger
2016-04-06  9:53     ` Eric Auger
2016-03-26  2:14 ` [PATCH v4 08/12] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-06 12:00   ` Eric Auger
2016-04-06 12:00     ` Eric Auger
2016-05-05 18:59   ` Chalamarla, Tirumalesh
2016-05-05 18:59     ` Chalamarla, Tirumalesh
2016-03-26  2:14 ` [PATCH v4 09/12] KVM: arm64: sync LPI configuration and pending tables Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-04-06 13:41   ` Eric Auger
2016-04-06 13:41     ` Eric Auger
2016-06-03 14:17     ` Andre Przywara
2016-06-03 14:17       ` Andre Przywara
2016-03-26  2:14 ` [PATCH v4 10/12] KVM: arm64: implement ITS command queue command handlers Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-05-05 19:12   ` Chalamarla, Tirumalesh
2016-05-05 19:12     ` Chalamarla, Tirumalesh
2016-05-25 14:34     ` Andre Przywara
2016-05-25 14:34       ` Andre Przywara
2016-03-26  2:14 ` [PATCH v4 11/12] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-03-26  2:14 ` [PATCH v4 12/12] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
2016-03-26  2:14   ` Andre Przywara
2016-06-03  4:26 ` [PATCH v4 00/12] KVM: arm64: GICv3 ITS emulation Bharat Bhushan
2016-06-03  4:26   ` Bharat Bhushan
2016-06-03 14:32   ` Andre Przywara
2016-06-03 14:32     ` Andre Przywara
2016-06-06  5:29     ` Bharat Bhushan
2016-06-06  5:29       ` Bharat Bhushan
2016-06-07  8:02       ` Christoffer Dall
2016-06-07  8:02         ` Christoffer Dall

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1458958450-19662-7-git-send-email-andre.przywara@arm.com \
    --to=andre.przywara@arm.com \
    --cc=christoffer.dall@linaro.org \
    --cc=eric.auger@linaro.org \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.cs.columbia.edu \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=marc.zyngier@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.