linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation
@ 2016-07-13  1:58 Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
                   ` (18 more replies)
  0 siblings, 19 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

this series allows those KVM guests that use an emulated GICv3 to use LPIs
as well, though in the moment this is limited to emulated PCI devices.
This is based on kvmarm/queue, which now only features the new VGIC
implementation.

Another round of changes after another round of review: some bugs fixed,
some reworks in various places. The register sanitisation has been changed
to be more readable and to add some missing masking.
The MMIO dispatching has been simplified (region type no longer in each
register description). Also the its_lock is now taken already in the
command dispatching function instead of each command handler, which
removes quite some "goto out_unlock;" and return value variables.
Also it turned out we can get rid of the special vgic_put_irq_locked()
variant. The changelog below holds a more detailed list.

You can find all of this code (and the prerequisites) in the
its-emul/v9 branch of my repository [1].
This has been briefly tested on the model and on GICv3 hardware, with
lockdep debugging enabled. If you have GICv3 capable hardware, please
test it on your setup.
Also of course any review comments are very welcome!

Cheers,
Andre.

Changelog v8..v9:
- MMIO dispatching now no longer holds iodev type in each register
- indirect bit is now cleared for collections
- sanitisation functions reworked (no extra masking, taking
  register specific masks)
- make PROPBASER/PENDBASER updates atomic
- move vgic_its_get_lpi to vgic.c
- fix potential race in vgic_add_lpi
- add comments on puts and gets if not obviously symmetric
- simplify copy_lpi_list (no get/put anymore)
- remove vgic_put_irq_locked()
- remove update_lpi_config_filtered() wrapper
- take its_lock in vgic_its_handle_command() already
- move PENDBASER/PROPBASER RES0 masking into sanitise functions
- properly mask *BASER registers to only support 48 bits
- cache last read byte in its_sync_lpi_pending_table()
- fix memory leak with LPI list copying

Changelog v7..v8:
- rebase on old-VGIC removal patch
- add missing vgic_put_irq()s
- check and ensure proper ITS initialisation sequence
- avoid double collection mapping
- renaming vits_ function prefixes to vgic_its_
- properly setup PENDBASER (for new VGIC now)
- change vgic_v2_probe init order to allow clean exit

Changelog v6..v7:
- use kref reference counting
- remove RCU usage from lpi_list, use spinlock instead
- copy list of LPIs before accessing guest memory
- introduce kvm_io_bus_get_dev()
- refactor parts of arm-gic-v3.h header file
- provide proper initial values for redistributor and ITS base registers
- rework sanitisation of base registers
- rework VGIC MMIO dispatching to differentiate between VGIC parts
- smaller fixes, also comments and commit messages amended

Changelog v5..v6:
- remove its_list from VGIC code
- add lpi_list and accessor functions
- introduce reference counting to struct vgic_irq
- replace its_lock spinlock with its_cmd and its_lock mutexes
- simplify guest memory accesses (due to the new mutexes)
- avoid unnecessary affinity updates
- refine base register address masking
- introduce sanity checks for PROPBASER and PENDBASER
- implement BASER<n> registers
- pass struct vgic_its directly into the MMIO handlers
- convert KVM_SIGNAL_MSI ioctl into an MMIO write
- add explicit INIT ioctl to the ITS KVM device
- adjusting comments and commit messages

Changelog v4..v5:
- adapting to final new VGIC (MMIO handlers, etc.)
- new KVM device to model an ITS, multiple instances allowed
- move redistributor data into struct vgic_cpu
- separate distributor and ITS(es)
- various bug fixes and amended comments after review comments

Changelog v3..v4:
- adapting to new VGIC (changes in IRQ injection mechanism)

Changelog v2..v3:
- adapt to 4.3-rc and Christoffer's timer rework
- adapt spin locks on handling PROPBASER/PENDBASER registers
- rework locking in ITS command handling (dropping dist where needed)
- only clear LPI pending bit if LPI could actually be queued
- simplify GICR_CTLR handling
- properly free ITTEs (including our pending bitmap)
- fix corner cases with unmapped collections
- keep retire_lr() around
- rename vgic_handle_base_register to vgic_reg64_access()
- use kcalloc instead of kmalloc
- minor fixes, renames and added comments

Changelog v1..v2
- fix issues when using non-ITS GICv3 emulation
- streamline frame address initialization (new patch 05/15)
- preallocate buffer memory for reading from guest's memory
- move locking into the actual command handlers
-   preallocate memory for new structures if needed
- use non-atomic __set_bit() and __clear_bit() when under the lock
- add INT command handler to allow LPI injection from the guest
- rewrite CWRITER handler to align with new locking scheme
- remove unneeded CONFIG_HAVE_KVM_MSI #ifdefs
- check memory table size against our LPI limit (65536 interrupts)
- observe initial gap of 1024 interrupts in pending table
- use term "configuration table" to be in line with the spec
- clarify and extend documentation on API extensions
- introduce new KVM_CAP_MSI_DEVID capability to advertise device ID requirement
- update, fix and add many comments
- minor style changes as requested by reviewers

---------------

The GICv3 ITS (Interrupt Translation Service) is a part of the
ARM GICv3 interrupt controller [3] used for implementing MSIs.
It specifies a new kind of interrupts (LPIs), which are mapped to
establish a connection between a device, its MSI payload value and
the target processor the IRQ is eventually delivered to.
In order to allow using MSIs in an ARM64 KVM guest, we emulate this
ITS widget in the kernel.
The ITS works by reading commands written by software (from the guest
in our case) into a (guest allocated) memory region and establishing
the mapping between a device, the MSI payload and the target CPU.
We parse these commands and update our internal data structures to
reflect those changes. On an MSI injection we iterate those
structures to learn the LPI number we have to inject.
For the time being we use simple lists to hold the data, this is
good enough for the small number of entries each of the components
currently have. Should this become a performance bottleneck in the
future, those can be extended to arrays or trees if needed.

Most of the code lives in a separate source file (vgic-its.c), though
there are some changes necessary in the existing VGIC files.

For the time being this series gives us the ability to use emulated
PCI devices that can use MSIs in the guest. Those have to be
triggered by letting the userland device emulation simulate the MSI
write with the KVM_SIGNAL_MSI ioctl. This will be translated into
the proper LPI by the ITS emulation and injected into the guest in
the usual way (just with a higher IRQ number).

This series is based on kvmarm/queue and can be found at the
its-emul/v8 branch of this repository [1].
For this to be used you need a GICv3 host machine (a fast model would
do), though it does not rely on any host ITS bits (neither in hardware
or software).

To test this you can use the kvmtool patches available in the "its-v6"
branch here [2].
Start a guest with: "$ lkvm run --irqchip=gicv3-its --force-pci"
and see the ITS being used for instance by the virtio devices.

[1]: git://linux-arm.org/linux-ap.git
     http://www.linux-arm.org/git?p=linux-ap.git;a=log;h=refs/heads/its-emul/v9
[2]: git://linux-arm.org/kvmtool.git
     http://www.linux-arm.org/git?p=kvmtool.git;a=log;h=refs/heads/its-v6
[3]: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0069b/index.html

Andre Przywara (17):
  KVM: arm/arm64: move redistributor kvm_io_devices
  KVM: arm/arm64: check return value for kvm_register_vgic_device
  KVM: extend struct kvm_msi to hold a 32-bit device ID
  KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities
  KVM: kvm_io_bus: add kvm_io_bus_get_dev() call
  KVM: arm/arm64: VGIC: add refcounting for IRQs
  irqchip: refactor and add GICv3 definitions
  KVM: arm64: handle ITS related GICv3 redistributor registers
  KVM: arm64: introduce ITS emulation file with MMIO framework
  KVM: arm64: introduce new KVM ITS device
  KVM: arm64: implement basic ITS register handlers
  KVM: arm64: connect LPIs to the VGIC emulation
  KVM: arm64: read initial LPI pending table
  KVM: arm64: allow updates of LPI configuration table
  KVM: arm64: implement ITS command queue command handlers
  KVM: arm64: implement MSI injection in ITS emulation
  KVM: arm64: enable ITS emulation as a virtual MSI controller

 Documentation/virtual/kvm/api.txt              |   14 +-
 Documentation/virtual/kvm/devices/arm-vgic.txt |   25 +-
 arch/arm/include/asm/kvm_host.h                |    2 +-
 arch/arm/kvm/arm.c                             |    3 +-
 arch/arm64/include/asm/kvm_host.h              |    2 +-
 arch/arm64/include/uapi/asm/kvm.h              |    2 +
 arch/arm64/kvm/Kconfig                         |    1 +
 arch/arm64/kvm/Makefile                        |    1 +
 arch/arm64/kvm/reset.c                         |    8 +-
 include/kvm/arm_vgic.h                         |   74 +-
 include/linux/irqchip/arm-gic-v3.h             |  174 ++-
 include/linux/kvm_host.h                       |    2 +
 include/uapi/linux/kvm.h                       |    7 +-
 virt/kvm/arm/vgic/vgic-init.c                  |    9 +-
 virt/kvm/arm/vgic/vgic-its.c                   | 1423 ++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-kvm-device.c            |   22 +-
 virt/kvm/arm/vgic/vgic-mmio-v2.c               |    8 +
 virt/kvm/arm/vgic/vgic-mmio-v3.c               |  247 +++-
 virt/kvm/arm/vgic/vgic-mmio.c                  |   62 +-
 virt/kvm/arm/vgic/vgic-mmio.h                  |   31 +-
 virt/kvm/arm/vgic/vgic-v2.c                    |   12 +-
 virt/kvm/arm/vgic/vgic-v3.c                    |   29 +-
 virt/kvm/arm/vgic/vgic.c                       |  132 ++-
 virt/kvm/arm/vgic/vgic.h                       |   30 +-
 virt/kvm/kvm_main.c                            |   24 +
 25 files changed, 2196 insertions(+), 148 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

-- 
2.9.0

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 02/17] KVM: arm/arm64: check return value for kvm_register_vgic_device Andre Przywara
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

Logically a GICv3 redistributor is assigned to a (v)CPU, so we should
aim to keep redistributor related variables out of our struct vgic_dist.

Let's start by replacing the redistributor related kvm_io_device array
with two members in our existing struct vgic_cpu, which are naturally
per-VCPU and thus don't require any allocation / freeing.
So apart from the better fit with the redistributor design this saves
some code as well.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
---
 include/kvm/arm_vgic.h           |  8 +++++++-
 virt/kvm/arm/vgic/vgic-init.c    |  1 -
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 22 ++++++++--------------
 3 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 1264037..5142e2a 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -145,7 +145,6 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
-	struct vgic_io_device	*redist_iodevs;
 };
 
 struct vgic_v2_cpu_if {
@@ -193,6 +192,13 @@ struct vgic_cpu {
 	struct list_head ap_list_head;
 
 	u64 live_lrs;
+
+	/*
+	 * Members below are used with GICv3 emulation only and represent
+	 * parts of the redistributor.
+	 */
+	struct vgic_io_device	rd_iodev;
+	struct vgic_io_device	sgi_iodev;
 };
 
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index a1442f7..90cae48 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -271,7 +271,6 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
 	dist->initialized = false;
 
 	kfree(dist->spis);
-	kfree(dist->redist_iodevs);
 	dist->nr_spis = 0;
 
 	mutex_unlock(&kvm->lock);
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index a0c515a..fc7b6c9 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -285,21 +285,14 @@ unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev)
 
 int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
 {
-	int nr_vcpus = atomic_read(&kvm->online_vcpus);
 	struct kvm_vcpu *vcpu;
-	struct vgic_io_device *devices;
 	int c, ret = 0;
 
-	devices = kmalloc(sizeof(struct vgic_io_device) * nr_vcpus * 2,
-			  GFP_KERNEL);
-	if (!devices)
-		return -ENOMEM;
-
 	kvm_for_each_vcpu(c, vcpu, kvm) {
 		gpa_t rd_base = redist_base_address + c * SZ_64K * 2;
 		gpa_t sgi_base = rd_base + SZ_64K;
-		struct vgic_io_device *rd_dev = &devices[c * 2];
-		struct vgic_io_device *sgi_dev = &devices[c * 2 + 1];
+		struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev;
+		struct vgic_io_device *sgi_dev = &vcpu->arch.vgic_cpu.sgi_iodev;
 
 		kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops);
 		rd_dev->base_addr = rd_base;
@@ -335,14 +328,15 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
 	if (ret) {
 		/* The current c failed, so we start with the previous one. */
 		for (c--; c >= 0; c--) {
+			struct vgic_cpu *vgic_cpu;
+
+			vcpu = kvm_get_vcpu(kvm, c);
+			vgic_cpu = &vcpu->arch.vgic_cpu;
 			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
-						  &devices[c * 2].dev);
+						  &vgic_cpu->rd_iodev.dev);
 			kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
-						  &devices[c * 2 + 1].dev);
+						  &vgic_cpu->sgi_iodev.dev);
 		}
-		kfree(devices);
-	} else {
-		kvm->arch.vgic.redist_iodevs = devices;
 	}
 
 	return ret;
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 02/17] KVM: arm/arm64: check return value for kvm_register_vgic_device
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

kvm_register_device_ops() can return an error, so lets check its return
value and propagate this up the call chain.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-kvm-device.c | 15 +++++++++------
 virt/kvm/arm/vgic/vgic-v2.c         | 11 ++++++++---
 virt/kvm/arm/vgic/vgic-v3.c         | 15 +++++++++++++--
 virt/kvm/arm/vgic/vgic.h            |  2 +-
 4 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
index 0130c4b..2f24f13 100644
--- a/virt/kvm/arm/vgic/vgic-kvm-device.c
+++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
@@ -210,20 +210,24 @@ static void vgic_destroy(struct kvm_device *dev)
 	kfree(dev);
 }
 
-void kvm_register_vgic_device(unsigned long type)
+int kvm_register_vgic_device(unsigned long type)
 {
+	int ret = -ENODEV;
+
 	switch (type) {
 	case KVM_DEV_TYPE_ARM_VGIC_V2:
-		kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
-					KVM_DEV_TYPE_ARM_VGIC_V2);
+		ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
+					      KVM_DEV_TYPE_ARM_VGIC_V2);
 		break;
 #ifdef CONFIG_KVM_ARM_VGIC_V3
 	case KVM_DEV_TYPE_ARM_VGIC_V3:
-		kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
-					KVM_DEV_TYPE_ARM_VGIC_V3);
+		ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
+					      KVM_DEV_TYPE_ARM_VGIC_V3);
 		break;
 #endif
 	}
+
+	return ret;
 }
 
 /** vgic_attr_regs_access: allows user space to read/write VGIC registers
@@ -428,4 +432,3 @@ struct kvm_device_ops kvm_arm_vgic_v3_ops = {
 };
 
 #endif /* CONFIG_KVM_ARM_VGIC_V3 */
-
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index e31405e..079bf67 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -332,20 +332,25 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
 	vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
 	kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
 
+	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
+	if (ret) {
+		kvm_err("Cannot register GICv2 KVM device\n");
+		iounmap(kvm_vgic_global_state.vctrl_base);
+		return ret;
+	}
+
 	ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
 				     kvm_vgic_global_state.vctrl_base +
 					 resource_size(&info->vctrl),
 				     info->vctrl.start);
-
 	if (ret) {
 		kvm_err("Cannot map VCTRL into hyp\n");
+		kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
 		iounmap(kvm_vgic_global_state.vctrl_base);
 		return ret;
 	}
 
 	kvm_vgic_global_state.can_emulate_gicv2 = true;
-	kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
-
 	kvm_vgic_global_state.vcpu_base = info->vcpu.start;
 	kvm_vgic_global_state.type = VGIC_V2;
 	kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS;
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 346b4ad..e48a22e 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -296,6 +296,7 @@ out:
 int vgic_v3_probe(const struct gic_kvm_info *info)
 {
 	u32 ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
+	int ret;
 
 	/*
 	 * The ListRegs field is 5 bits, but there is a architectural
@@ -319,12 +320,22 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
 	} else {
 		kvm_vgic_global_state.vcpu_base = info->vcpu.start;
 		kvm_vgic_global_state.can_emulate_gicv2 = true;
-		kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
+		ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
+		if (ret) {
+			kvm_err("Cannot register GICv2 KVM device.\n");
+			return ret;
+		}
 		kvm_info("vgic-v2@%llx\n", info->vcpu.start);
 	}
+	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
+	if (ret) {
+		kvm_err("Cannot register GICv3 KVM device.\n");
+		kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
+		return ret;
+	}
+
 	if (kvm_vgic_global_state.vcpu_base == 0)
 		kvm_info("disabling GICv2 emulation\n");
-	kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
 
 	kvm_vgic_global_state.vctrl_base = NULL;
 	kvm_vgic_global_state.type = VGIC_V3;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 7b300ca..c752152 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -124,7 +124,7 @@ static inline int vgic_register_redist_iodevs(struct kvm *kvm,
 }
 #endif
 
-void kvm_register_vgic_device(unsigned long type);
+int kvm_register_vgic_device(unsigned long type);
 int vgic_lazy_init(struct kvm *kvm);
 int vgic_init(struct kvm *kvm);
 
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 02/17] KVM: arm/arm64: check return value for kvm_register_vgic_device Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13 10:28   ` Paolo Bonzini
  2016-07-13  1:58 ` [PATCH v9 04/17] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

The ARM GICv3 ITS MSI controller requires a device ID to be able to
assign the proper interrupt vector. On real hardware, this ID is
sampled from the bus. To be able to emulate an ITS controller, extend
the KVM MSI interface to let userspace provide such a device ID. For
PCI devices, the device ID is simply the 16-bit bus-device-function
triplet, which should be easily available to the userland tool.

Also there is a new KVM capability which advertises whether the
current VM requires a device ID to be set along with the MSI data.
This flag is still reported as not available everywhere, later we will
enable it when ITS emulation is used.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <eric.auger@linaro.org>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 Documentation/virtual/kvm/api.txt | 12 ++++++++++--
 include/uapi/linux/kvm.h          |  5 ++++-
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 09efa9e..6551311 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2175,10 +2175,18 @@ struct kvm_msi {
 	__u32 address_hi;
 	__u32 data;
 	__u32 flags;
-	__u8  pad[16];
+	__u32 devid;
+	__u8  pad[12];
 };
 
-No flags are defined so far. The corresponding field must be 0.
+flags: KVM_MSI_VALID_DEVID: devid contains a valid value
+devid: If KVM_MSI_VALID_DEVID is set, contains a unique device identifier
+       for the device that wrote the MSI message.
+       For PCI, this is usually a BFD identifier in the lower 16 bits.
+
+The per-VM KVM_CAP_MSI_DEVID capability advertises the need to provide
+the device ID. If this capability is not set, userland cannot rely on
+the kernel to allow the KVM_MSI_VALID_DEVID flag being set.
 
 
 4.71 KVM_CREATE_PIT2
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 05ebf47..7de96f5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -866,6 +866,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_ARM_PMU_V3 126
 #define KVM_CAP_VCPU_ATTRIBUTES 127
 #define KVM_CAP_MAX_VCPU_ID 128
+#define KVM_CAP_MSI_DEVID 129
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1024,12 +1025,14 @@ struct kvm_one_reg {
 	__u64 addr;
 };
 
+#define KVM_MSI_VALID_DEVID	(1U << 0)
 struct kvm_msi {
 	__u32 address_lo;
 	__u32 address_hi;
 	__u32 data;
 	__u32 flags;
-	__u8  pad[16];
+	__u32 devid;
+	__u8  pad[12];
 };
 
 struct kvm_arm_device_addr {
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 04/17] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (2 preceding siblings ...)
  2016-07-13  1:58 ` [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13  1:58 ` [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call Andre Przywara
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

KVM capabilities can be a per-VM property, though ARM/ARM64 currently
does not pass on the VM pointer to the architecture specific
capability handlers.
Add a "struct kvm*" parameter to those function to later allow proper
per-VM capability reporting.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <eric.auger@linaro.org>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 arch/arm/include/asm/kvm_host.h   | 2 +-
 arch/arm/kvm/arm.c                | 2 +-
 arch/arm64/include/asm/kvm_host.h | 2 +-
 arch/arm64/kvm/reset.c            | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 96387d4..3c40facd 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -281,7 +281,7 @@ static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr,
 	 */
 }
 
-static inline int kvm_arch_dev_ioctl_check_extension(long ext)
+static inline int kvm_arch_dev_ioctl_check_extension(struct kvm *kvm, long ext)
 {
 	return 0;
 }
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index c74483f..557e390 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -201,7 +201,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		r = KVM_MAX_VCPUS;
 		break;
 	default:
-		r = kvm_arch_dev_ioctl_check_extension(ext);
+		r = kvm_arch_dev_ioctl_check_extension(kvm, ext);
 		break;
 	}
 	return r;
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 49095fc..ebe8904 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -47,7 +47,7 @@
 
 int __attribute_const__ kvm_target_cpu(void);
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
-int kvm_arch_dev_ioctl_check_extension(long ext);
+int kvm_arch_dev_ioctl_check_extension(struct kvm *kvm, long ext);
 unsigned long kvm_hyp_reset_entry(void);
 void __extended_idmap_trampoline(phys_addr_t boot_pgd, phys_addr_t idmap_start);
 
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index 7be24f2..3989833 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -65,7 +65,7 @@ static bool cpu_has_32bit_el1(void)
  * We currently assume that the number of HW registers is uniform
  * across all CPUs (see cpuinfo_sanity_check).
  */
-int kvm_arch_dev_ioctl_check_extension(long ext)
+int kvm_arch_dev_ioctl_check_extension(struct kvm *kvm, long ext)
 {
 	int r;
 
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (3 preceding siblings ...)
  2016-07-13  1:58 ` [PATCH v9 04/17] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13 10:29   ` Paolo Bonzini
  2016-07-13  1:58 ` [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

The kvm_io_bus framework is a nice place of holding information about
various MMIO regions for kernel emulated devices.
Add a call to retrieve the kvm_io_device structure which is associated
with a certain MMIO address. This avoids to duplicate kvm_io_bus'
knowledge of MMIO regions without having to fake MMIO calls if a user
needs the device a certain MMIO address belongs to.
This will be used by the ITS emulation to get the associated ITS device
when someone triggers an MSI via an ioctl from userspace.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 include/linux/kvm_host.h |  2 ++
 virt/kvm/kvm_main.c      | 24 ++++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 0640ee9..614a981 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -164,6 +164,8 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
 			    int len, struct kvm_io_device *dev);
 int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
 			      struct kvm_io_device *dev);
+struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+					 gpa_t addr);
 
 #ifdef CONFIG_KVM_ASYNC_PF
 struct kvm_async_pf {
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index ef54b4c..bd2eb92 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3496,6 +3496,30 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
 	return r;
 }
 
+struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+					 gpa_t addr)
+{
+	struct kvm_io_bus *bus;
+	int dev_idx, srcu_idx;
+	struct kvm_io_device *iodev = NULL;
+
+	srcu_idx = srcu_read_lock(&kvm->srcu);
+
+	bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
+
+	dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1);
+	if (dev_idx < 0)
+		goto out_unlock;
+
+	iodev = bus->range[dev_idx].dev;
+
+out_unlock:
+	srcu_read_unlock(&kvm->srcu, srcu_idx);
+
+	return iodev;
+}
+EXPORT_SYMBOL_GPL(kvm_io_bus_get_dev);
+
 static struct notifier_block kvm_cpu_notifier = {
 	.notifier_call = kvm_cpu_hotplug,
 };
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (4 preceding siblings ...)
  2016-07-13  1:58 ` [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13 12:15   ` Marc Zyngier
  2016-07-13  1:58 ` [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions Andre Przywara
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

In the moment our struct vgic_irq's are statically allocated at guest
creation time. So getting a pointer to an IRQ structure is trivial and
safe. LPIs are more dynamic, they can be mapped and unmapped at any time
during the guest's _runtime_.
In preparation for supporting LPIs we introduce reference counting for
those structures using the kernel's kref infrastructure.
Since private IRQs and SPIs are statically allocated, the refcount never
drops to 0 at the moment, but we increase it when an IRQ gets onto a VCPU
list and decrease it when it gets removed.
This introduces vgic_put_irq(), which wraps kref_put and hides the
release function from the callers.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/arm_vgic.h           |  1 +
 virt/kvm/arm/vgic/vgic-init.c    |  2 ++
 virt/kvm/arm/vgic/vgic-mmio-v2.c |  8 ++++++
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 20 +++++++++------
 virt/kvm/arm/vgic/vgic-mmio.c    | 25 ++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-v2.c      |  1 +
 virt/kvm/arm/vgic/vgic-v3.c      |  1 +
 virt/kvm/arm/vgic/vgic.c         | 53 ++++++++++++++++++++++++++++++++--------
 virt/kvm/arm/vgic/vgic.h         |  1 +
 9 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 5142e2a..450b4da 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -96,6 +96,7 @@ struct vgic_irq {
 	bool active;			/* not used for LPIs */
 	bool enabled;
 	bool hw;			/* Tied to HW IRQ */
+	struct kref refcount;		/* Used for LPIs */
 	u32 hwintid;			/* HW INTID number */
 	union {
 		u8 targets;			/* GICv2 target VCPUs mask */
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 90cae48..ac3c1a5 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -177,6 +177,7 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
 		spin_lock_init(&irq->irq_lock);
 		irq->vcpu = NULL;
 		irq->target_vcpu = vcpu0;
+		kref_init(&irq->refcount);
 		if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
 			irq->targets = 0;
 		else
@@ -211,6 +212,7 @@ static void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 		irq->vcpu = NULL;
 		irq->target_vcpu = vcpu;
 		irq->targets = 1U << vcpu->vcpu_id;
+		kref_init(&irq->refcount);
 		if (vgic_irq_is_sgi(i)) {
 			/* SGIs */
 			irq->enabled = 1;
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
index a213936..4152348 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
@@ -102,6 +102,7 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
 		irq->source |= 1U << source_vcpu->vcpu_id;
 
 		vgic_queue_irq_unlock(source_vcpu->kvm, irq);
+		vgic_put_irq(source_vcpu->kvm, irq);
 	}
 }
 
@@ -116,6 +117,8 @@ static unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu,
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
 		val |= (u64)irq->targets << (i * 8);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return val;
@@ -143,6 +146,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
 		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -157,6 +161,8 @@ static unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu,
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
 		val |= (u64)irq->source << (i * 8);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 	return val;
 }
@@ -178,6 +184,7 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
 			irq->pending = false;
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -201,6 +208,7 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
 		} else {
 			spin_unlock(&irq->irq_lock);
 		}
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index fc7b6c9..bfcafbd 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -80,15 +80,17 @@ static unsigned long vgic_mmio_read_irouter(struct kvm_vcpu *vcpu,
 {
 	int intid = VGIC_ADDR_TO_INTID(addr, 64);
 	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid);
+	unsigned long ret = 0;
 
 	if (!irq)
 		return 0;
 
 	/* The upper word is RAZ for us. */
-	if (addr & 4)
-		return 0;
+	if (!(addr & 4))
+		ret = extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len);
 
-	return extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len);
+	vgic_put_irq(vcpu->kvm, irq);
+	return ret;
 }
 
 static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
@@ -96,15 +98,17 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 				    unsigned long val)
 {
 	int intid = VGIC_ADDR_TO_INTID(addr, 64);
-	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid);
-
-	if (!irq)
-		return;
+	struct vgic_irq *irq;
 
 	/* The upper word is WI for us since we don't implement Aff3. */
 	if (addr & 4)
 		return;
 
+	irq = vgic_get_irq(vcpu->kvm, NULL, intid);
+
+	if (!irq)
+		return;
+
 	spin_lock(&irq->irq_lock);
 
 	/* We only care about and preserve Aff0, Aff1 and Aff2. */
@@ -112,6 +116,7 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 	irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
 
 	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(vcpu->kvm, irq);
 }
 
 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
@@ -445,5 +450,6 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
 		irq->pending = true;
 
 		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 9f6fab7..5e79e01 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -56,6 +56,8 @@ unsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu,
 
 		if (irq->enabled)
 			value |= (1U << i);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return value;
@@ -74,6 +76,8 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
 		spin_lock(&irq->irq_lock);
 		irq->enabled = true;
 		vgic_queue_irq_unlock(vcpu->kvm, irq);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -92,6 +96,7 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
 		irq->enabled = false;
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -108,6 +113,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
 
 		if (irq->pending)
 			value |= (1U << i);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return value;
@@ -129,6 +136,7 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
 			irq->soft_pending = true;
 
 		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -152,6 +160,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
 		}
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -168,6 +177,8 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
 
 		if (irq->active)
 			value |= (1U << i);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return value;
@@ -242,6 +253,7 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 		vgic_mmio_change_active(vcpu, irq, false);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -257,6 +269,7 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 		vgic_mmio_change_active(vcpu, irq, true);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -272,6 +285,8 @@ unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu,
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
 		val |= (u64)irq->priority << (i * 8);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return val;
@@ -298,6 +313,8 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
 		/* Narrow the priority range to what we actually support */
 		irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS);
 		spin_unlock(&irq->irq_lock);
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
@@ -313,6 +330,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
 
 		if (irq->config == VGIC_CONFIG_EDGE)
 			value |= (2U << (i * 2));
+
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 
 	return value;
@@ -326,7 +345,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
 	int i;
 
 	for (i = 0; i < len * 4; i++) {
-		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
+		struct vgic_irq *irq;
 
 		/*
 		 * The configuration cannot be changed for SGIs in general,
@@ -337,14 +356,18 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
 		if (intid + i < VGIC_NR_PRIVATE_IRQS)
 			continue;
 
+		irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 		spin_lock(&irq->irq_lock);
+
 		if (test_bit(i * 2 + 1, &val)) {
 			irq->config = VGIC_CONFIG_EDGE;
 		} else {
 			irq->config = VGIC_CONFIG_LEVEL;
 			irq->pending = irq->line_level | irq->soft_pending;
 		}
+
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 079bf67..0bf6709 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -124,6 +124,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 		}
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index e48a22e..f0ac064 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -113,6 +113,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 		}
 
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(vcpu->kvm, irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 69b61ab..1fb2693 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -48,13 +48,20 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid)
 {
-	/* SGIs and PPIs */
-	if (intid <= VGIC_MAX_PRIVATE)
-		return &vcpu->arch.vgic_cpu.private_irqs[intid];
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq;
 
-	/* SPIs */
-	if (intid <= VGIC_MAX_SPI)
-		return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
+	if (intid <= VGIC_MAX_PRIVATE) {        /* SGIs and PPIs */
+		irq = &vcpu->arch.vgic_cpu.private_irqs[intid];
+		kref_get(&irq->refcount);
+		return irq;
+	}
+
+	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
+		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
+		kref_get(&irq->refcount);
+		return irq;
+	}
 
 	/* LPIs are not yet covered */
 	if (intid >= VGIC_MIN_LPI)
@@ -64,6 +71,17 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 	return NULL;
 }
 
+/* The refcount should never drop to 0 at the moment. */
+static void vgic_irq_release(struct kref *ref)
+{
+	WARN_ON(1);
+}
+
+void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
+{
+	kref_put(&irq->refcount, vgic_irq_release);
+}
+
 /**
  * kvm_vgic_target_oracle - compute the target vcpu for an irq
  *
@@ -236,6 +254,7 @@ retry:
 		goto retry;
 	}
 
+	kref_get(&irq->refcount);
 	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
 	irq->vcpu = vcpu;
 
@@ -269,14 +288,17 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 	if (!irq)
 		return -EINVAL;
 
-	if (irq->hw != mapped_irq)
+	if (irq->hw != mapped_irq) {
+		vgic_put_irq(kvm, irq);
 		return -EINVAL;
+	}
 
 	spin_lock(&irq->irq_lock);
 
 	if (!vgic_validate_injection(irq, level)) {
 		/* Nothing to see here, move along... */
 		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(kvm, irq);
 		return 0;
 	}
 
@@ -288,6 +310,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 	}
 
 	vgic_queue_irq_unlock(kvm, irq);
+	vgic_put_irq(kvm, irq);
 
 	return 0;
 }
@@ -330,25 +353,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
 	irq->hwintid = phys_irq;
 
 	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(vcpu->kvm, irq);
 
 	return 0;
 }
 
 int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq)
 {
-	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
-
-	BUG_ON(!irq);
+	struct vgic_irq *irq;
 
 	if (!vgic_initialized(vcpu->kvm))
 		return -EAGAIN;
 
+	irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
+	BUG_ON(!irq);
+
 	spin_lock(&irq->irq_lock);
 
 	irq->hw = false;
 	irq->hwintid = 0;
 
 	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(vcpu->kvm, irq);
 
 	return 0;
 }
@@ -386,6 +412,12 @@ retry:
 			list_del(&irq->ap_list);
 			irq->vcpu = NULL;
 			spin_unlock(&irq->irq_lock);
+
+			/*
+			 * This put matches the get when we added the LPI to
+			 * the ap_list. We now drop the reference from the list.
+			 */
+			vgic_put_irq(vcpu->kvm, irq);
 			continue;
 		}
 
@@ -614,6 +646,7 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
 	spin_lock(&irq->irq_lock);
 	map_is_active = irq->hw && irq->active;
 	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(vcpu->kvm, irq);
 
 	return map_is_active;
 }
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index c752152..5b79c34 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -38,6 +38,7 @@ struct vgic_vmcr {
 
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid);
+void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
 bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
 void vgic_kick_vcpus(struct kvm *kvm);
 
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (5 preceding siblings ...)
  2016-07-13  1:58 ` [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
@ 2016-07-13  1:58 ` Andre Przywara
  2016-07-13 12:28   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 08/17] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:58 UTC (permalink / raw)
  To: linux-arm-kernel

arm-gic-v3.h contains bit and register definitions for the GICv3 and ITS,
at least for the bits the we currently care about.
The ITS emulation needs more definitions, so add them and refactor
the memory attribute #defines to be more universally usable.
To avoid changing all users, we still provide some of the old definitons
defined with the help of the new macros.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/linux/irqchip/arm-gic-v3.h | 174 ++++++++++++++++++++++++-------------
 1 file changed, 114 insertions(+), 60 deletions(-)

diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index bfbd707..ed61647 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -112,34 +112,58 @@
 #define GICR_WAKER_ProcessorSleep	(1U << 1)
 #define GICR_WAKER_ChildrenAsleep	(1U << 2)
 
-#define GICR_PROPBASER_NonShareable	(0U << 10)
-#define GICR_PROPBASER_InnerShareable	(1U << 10)
-#define GICR_PROPBASER_OuterShareable	(2U << 10)
-#define GICR_PROPBASER_SHAREABILITY_MASK (3UL << 10)
-#define GICR_PROPBASER_nCnB		(0U << 7)
-#define GICR_PROPBASER_nC		(1U << 7)
-#define GICR_PROPBASER_RaWt		(2U << 7)
-#define GICR_PROPBASER_RaWb		(3U << 7)
-#define GICR_PROPBASER_WaWt		(4U << 7)
-#define GICR_PROPBASER_WaWb		(5U << 7)
-#define GICR_PROPBASER_RaWaWt		(6U << 7)
-#define GICR_PROPBASER_RaWaWb		(7U << 7)
-#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
-#define GICR_PROPBASER_IDBITS_MASK	(0x1f)
-
-#define GICR_PENDBASER_NonShareable	(0U << 10)
-#define GICR_PENDBASER_InnerShareable	(1U << 10)
-#define GICR_PENDBASER_OuterShareable	(2U << 10)
-#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10)
-#define GICR_PENDBASER_nCnB		(0U << 7)
-#define GICR_PENDBASER_nC		(1U << 7)
-#define GICR_PENDBASER_RaWt		(2U << 7)
-#define GICR_PENDBASER_RaWb		(3U << 7)
-#define GICR_PENDBASER_WaWt		(4U << 7)
-#define GICR_PENDBASER_WaWb		(5U << 7)
-#define GICR_PENDBASER_RaWaWt		(6U << 7)
-#define GICR_PENDBASER_RaWaWb		(7U << 7)
-#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7)
+#define GIC_BASER_CACHE_nCnB		0ULL
+#define GIC_BASER_CACHE_SameAsInner	0ULL
+#define GIC_BASER_CACHE_nC		1ULL
+#define GIC_BASER_CACHE_RaWt		2ULL
+#define GIC_BASER_CACHE_RaWb		3ULL
+#define GIC_BASER_CACHE_WaWt		4ULL
+#define GIC_BASER_CACHE_WaWb		5ULL
+#define GIC_BASER_CACHE_RaWaWt		6ULL
+#define GIC_BASER_CACHE_RaWaWb		7ULL
+#define GIC_BASER_CACHE_MASK		7ULL
+#define GIC_BASER_NonShareable		0ULL
+#define GIC_BASER_InnerShareable	1ULL
+#define GIC_BASER_OuterShareable	2ULL
+#define GIC_BASER_SHAREABILITY_MASK	3ULL
+
+#define GIC_BASER_CACHEABILITY(reg, inner_outer, type)			\
+	(GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT)
+
+#define GIC_BASER_SHAREABILITY(reg, type)				\
+	(GIC_BASER_##type << reg##_SHAREABILITY_SHIFT)
+
+#define GICR_PROPBASER_SHAREABILITY_SHIFT		(10)
+#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT		(7)
+#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT		(56)
+#define GICR_PROPBASER_SHAREABILITY_MASK				\
+	GIC_BASER_SHAREABILITY(GICR_PROPBASER, SHAREABILITY_MASK)
+#define GICR_PROPBASER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, MASK)
+#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, MASK)
+
+#define GICR_PROPBASER_InnerShareable					\
+	GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable)
+#define GICR_PROPBASER_nC GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, nC)
+#define GICR_PROPBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, WaWb)
+#define GICR_PROPBASER_IDBITS_MASK			(0x1f)
+
+#define GICR_PENDBASER_SHAREABILITY_SHIFT		(10)
+#define GICR_PENDBASER_INNER_CACHEABILITY_SHIFT		(7)
+#define GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT		(56)
+#define GICR_PENDBASER_SHAREABILITY_MASK				\
+	GIC_BASER_SHAREABILITY(GICR_PENDBASER, SHAREABILITY_MASK)
+#define GICR_PENDBASER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, MASK)
+#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, MASK)
+
+#define GICR_PENDBASER_InnerShareable					\
+	GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable)
+#define GICR_PENDBASER_nC GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, nC)
+#define GICR_PENDBASER_WaWb GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, WaWb)
+#define GICR_PENDBASER_PTZ				BIT_ULL(62)
 
 /*
  * Re-Distributor registers, offsets from SGI_base
@@ -175,53 +199,66 @@
 #define GITS_CWRITER			0x0088
 #define GITS_CREADR			0x0090
 #define GITS_BASER			0x0100
+#define GITS_IDREGS_BASE		0xffd0
+#define GITS_PIDR0			0xffe0
+#define GITS_PIDR1			0xffe4
 #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_CBASER_VALID		(1UL << 63)
-#define GITS_CBASER_nCnB		(0UL << 59)
-#define GITS_CBASER_nC			(1UL << 59)
-#define GITS_CBASER_RaWt		(2UL << 59)
-#define GITS_CBASER_RaWb		(3UL << 59)
-#define GITS_CBASER_WaWt		(4UL << 59)
-#define GITS_CBASER_WaWb		(5UL << 59)
-#define GITS_CBASER_RaWaWt		(6UL << 59)
-#define GITS_CBASER_RaWaWb		(7UL << 59)
-#define GITS_CBASER_CACHEABILITY_MASK	(7UL << 59)
-#define GITS_CBASER_NonShareable	(0UL << 10)
-#define GITS_CBASER_InnerShareable	(1UL << 10)
-#define GITS_CBASER_OuterShareable	(2UL << 10)
-#define GITS_CBASER_SHAREABILITY_MASK	(3UL << 10)
+#define GITS_TYPER_HWCOLLCNT_SHIFT	24
+
+#define GITS_CBASER_VALID			(1UL << 63)
+#define GITS_CBASER_SHAREABILITY_SHIFT		(10)
+#define GITS_CBASER_INNER_CACHEABILITY_SHIFT	(59)
+#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT	(53)
+#define GITS_CBASER_SHAREABILITY_MASK					\
+	GIC_BASER_SHAREABILITY(GITS_CBASER, SHAREABILITY_MASK)
+#define GITS_CBASER_CACHEABILITY_MASK					\
+	GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, MASK)
+#define GITS_CBASER_OUTER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GITS_CBASER, OUTER, MASK)
+
+#define GITS_CBASER_InnerShareable					\
+	GIC_BASER_SHAREABILITY(GITS_CBASER, InnerShareable)
+#define GITS_CBASER_nC GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, nC)
+#define GITS_CBASER_WaWb GIC_BASER_CACHEABILITY(GITS_CBASER, INNER, WaWb)
 
 #define GITS_BASER_NR_REGS		8
 
-#define GITS_BASER_VALID		(1UL << 63)
-#define GITS_BASER_nCnB			(0UL << 59)
-#define GITS_BASER_nC			(1UL << 59)
-#define GITS_BASER_RaWt			(2UL << 59)
-#define GITS_BASER_RaWb			(3UL << 59)
-#define GITS_BASER_WaWt			(4UL << 59)
-#define GITS_BASER_WaWb			(5UL << 59)
-#define GITS_BASER_RaWaWt		(6UL << 59)
-#define GITS_BASER_RaWaWb		(7UL << 59)
-#define GITS_BASER_CACHEABILITY_MASK	(7UL << 59)
-#define GITS_BASER_TYPE_SHIFT		(56)
+#define GITS_BASER_VALID			(1UL << 63)
+#define GITS_BASER_INDIRECT			(1ULL << 62)
+#define GITS_BASER_INNER_CACHEABILITY_SHIFT	(59)
+#define GITS_BASER_OUTER_CACHEABILITY_SHIFT	(53)
+#define GITS_BASER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GITS_BASER, INNER, MASK)
+#define GITS_BASER_OUTER_CACHEABILITY_MASK				\
+	GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, MASK)
+#define GITS_BASER_SHAREABILITY_MASK					\
+	GIC_BASER_SHAREABILITY(GITS_BASER, SHAREABILITY_MASK)
+
+#define GITS_BASER_nC GIC_BASER_CACHEABILITY(GITS_BASER, INNER, nC)
+#define GITS_BASER_WaWb GIC_BASER_CACHEABILITY(GITS_BASER, INNER, WaWb)
+#define GITS_BASER_TYPE_SHIFT			(56)
 #define GITS_BASER_TYPE(r)		(((r) >> GITS_BASER_TYPE_SHIFT) & 7)
-#define GITS_BASER_ENTRY_SIZE_SHIFT	(48)
+#define GITS_BASER_ENTRY_SIZE_SHIFT		(48)
 #define GITS_BASER_ENTRY_SIZE(r)	((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0xff) + 1)
-#define GITS_BASER_NonShareable		(0UL << 10)
-#define GITS_BASER_InnerShareable	(1UL << 10)
-#define GITS_BASER_OuterShareable	(2UL << 10)
 #define GITS_BASER_SHAREABILITY_SHIFT	(10)
-#define GITS_BASER_SHAREABILITY_MASK	(3UL << GITS_BASER_SHAREABILITY_SHIFT)
+#define GITS_BASER_InnerShareable					\
+	GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable)
 #define GITS_BASER_PAGE_SIZE_SHIFT	(8)
 #define GITS_BASER_PAGE_SIZE_4K		(0UL << GITS_BASER_PAGE_SIZE_SHIFT)
 #define GITS_BASER_PAGE_SIZE_16K	(1UL << GITS_BASER_PAGE_SIZE_SHIFT)
@@ -243,7 +280,10 @@
  */
 #define GITS_CMD_MAPD			0x08
 #define GITS_CMD_MAPC			0x09
-#define GITS_CMD_MAPVI			0x0a
+#define GITS_CMD_MAPTI			0x0a
+/* older GIC documentation used MAPVI for this command */
+#define GITS_CMD_MAPVI			GITS_CMD_MAPTI
+#define GITS_CMD_MAPI			0x0b
 #define GITS_CMD_MOVI			0x01
 #define GITS_CMD_DISCARD		0x0f
 #define GITS_CMD_INV			0x0c
@@ -254,6 +294,20 @@
 #define GITS_CMD_SYNC			0x05
 
 /*
+ * ITS error numbers
+ */
+#define E_ITS_MOVI_UNMAPPED_INTERRUPT           0x010107
+#define E_ITS_MOVI_UNMAPPED_COLLECTION          0x010109
+#define E_ITS_CLEAR_UNMAPPED_INTERRUPT          0x010507
+#define E_ITS_MAPC_PROCNUM_OOR                  0x010902
+#define E_ITS_MAPTI_UNMAPPED_DEVICE             0x010a04
+#define E_ITS_MAPTI_PHYSICALID_OOR              0x010a06
+#define E_ITS_INV_UNMAPPED_INTERRUPT            0x010c07
+#define E_ITS_INVALL_UNMAPPED_COLLECTION        0x010d09
+#define E_ITS_MOVALL_PROCNUM_OOR                0x010e01
+#define E_ITS_DISCARD_UNMAPPED_INTERRUPT        0x010f07
+
+/*
  * CPU interface registers
  */
 #define ICC_CTLR_EL1_EOImode_drop_dir	(0U << 1)
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 08/17] KVM: arm64: handle ITS related GICv3 redistributor registers
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (6 preceding siblings ...)
  2016-07-13  1:58 ` [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-13  1:59 ` [PATCH v9 09/17] KVM: arm64: introduce ITS emulation file with MMIO framework Andre Przywara
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

In the GICv3 redistributor there are the PENDBASER and PROPBASER
registers which we did not emulate so far, as they only make sense
when having an ITS. In preparation for that emulate those MMIO
accesses by storing the 64-bit data written into it into a variable
which we later read in the ITS emulation.
We also sanitise the registers, making sure RES0 regions are respected
and checking for valid memory attributes.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/arm_vgic.h           |  13 ++++
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 153 ++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-mmio.h    |   8 ++
 virt/kvm/arm/vgic/vgic-v3.c      |  11 ++-
 4 files changed, 181 insertions(+), 4 deletions(-)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 450b4da..df2dec5 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -146,6 +146,14 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
+
+	/*
+	 * Contains the attributes and gpa of the LPI configuration table.
+	 * Since we report GICR_TYPER.CommonLPIAff as 0b00, we can share
+	 * one address across all redistributors.
+	 * GICv3 spec: 6.1.2 "LPI Configuration tables"
+	 */
+	u64			propbaser;
 };
 
 struct vgic_v2_cpu_if {
@@ -200,6 +208,11 @@ struct vgic_cpu {
 	 */
 	struct vgic_io_device	rd_iodev;
 	struct vgic_io_device	sgi_iodev;
+
+	/* Contains the attributes and gpa of the LPI pending tables. */
+	u64 pendbaser;
+
+	bool lpis_enabled;
 };
 
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index bfcafbd..ee299a7 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -29,6 +29,19 @@ static unsigned long extract_bytes(unsigned long data, unsigned int offset,
 	return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
 }
 
+/* allows updates of any half of a 64-bit register (or the whole thing) */
+static u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
+			    unsigned long val)
+{
+	int lower = (offset & 4) * 8;
+	int upper = lower + 8 * len - 1;
+
+	reg &= ~GENMASK_ULL(upper, lower);
+	val &= GENMASK_ULL(len * 8 - 1, 0);
+
+	return reg | ((u64)val << lower);
+}
+
 static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
 					    gpa_t addr, unsigned int len)
 {
@@ -152,6 +165,142 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/* We want to avoid outer shareable. */
+u64 vgic_sanitise_shareability(u64 field)
+{
+	switch (field) {
+	case GIC_BASER_OuterShareable:
+		return GIC_BASER_InnerShareable;
+	default:
+		return field;
+	}
+}
+
+/* Avoid any inner non-cacheable mapping. */
+u64 vgic_sanitise_inner_cacheability(u64 field)
+{
+	switch (field) {
+	case GIC_BASER_CACHE_nCnB:
+	case GIC_BASER_CACHE_nC:
+		return GIC_BASER_CACHE_RaWb;
+	default:
+		return field;
+	}
+}
+
+/* Non-cacheable or same-as-inner are OK. */
+u64 vgic_sanitise_outer_cacheability(u64 field)
+{
+	switch (field) {
+	case GIC_BASER_CACHE_SameAsInner:
+	case GIC_BASER_CACHE_nC:
+		return field;
+	default:
+		return GIC_BASER_CACHE_nC;
+	}
+}
+
+u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
+			u64 (*sanitise_fn)(u64))
+{
+	u64 field = (reg & field_mask) >> field_shift;
+
+	field = sanitise_fn(field) << field_shift;
+	return (reg & ~field_mask) | field;
+}
+
+#define PROPBASER_RES0_MASK						\
+	(GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(6, 5))
+#define PENDBASER_RES0_MASK						\
+	(BIT_ULL(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) |	\
+	 GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0))
+
+static u64 vgic_sanitise_pendbaser(u64 reg)
+{
+	reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK,
+				  GICR_PENDBASER_SHAREABILITY_SHIFT,
+				  vgic_sanitise_shareability);
+	reg = vgic_sanitise_field(reg, GICR_PENDBASER_CACHEABILITY_MASK,
+				  GICR_PENDBASER_INNER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_inner_cacheability);
+	reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK,
+				  GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_outer_cacheability);
+
+	reg &= ~PENDBASER_RES0_MASK;
+	reg &= ~GENMASK_ULL(51, 48);
+
+	return reg;
+}
+
+static u64 vgic_sanitise_propbaser(u64 reg)
+{
+	reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK,
+				  GICR_PROPBASER_SHAREABILITY_SHIFT,
+				  vgic_sanitise_shareability);
+	reg = vgic_sanitise_field(reg, GICR_PROPBASER_CACHEABILITY_MASK,
+				  GICR_PROPBASER_INNER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_inner_cacheability);
+	reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK,
+				  GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_outer_cacheability);
+
+	reg &= ~PROPBASER_RES0_MASK;
+	reg &= ~GENMASK_ULL(51, 48);
+	return reg;
+}
+
+static unsigned long vgic_mmio_read_propbase(struct kvm_vcpu *vcpu,
+					     gpa_t addr, unsigned int len)
+{
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+	return extract_bytes(dist->propbaser, addr & 7, len);
+}
+
+static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu,
+				     gpa_t addr, unsigned int len,
+				     unsigned long val)
+{
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	u64 propbaser = dist->propbaser;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	propbaser = update_64bit_reg(propbaser, addr & 4, len, val);
+	propbaser = vgic_sanitise_propbaser(propbaser);
+
+	dist->propbaser = propbaser;
+}
+
+static unsigned long vgic_mmio_read_pendbase(struct kvm_vcpu *vcpu,
+					     gpa_t addr, unsigned int len)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+	return extract_bytes(vgic_cpu->pendbaser, addr & 7, len);
+}
+
+static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu,
+				     gpa_t addr, unsigned int len,
+				     unsigned long val)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	u64 pendbaser = vgic_cpu->pendbaser;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	pendbaser = update_64bit_reg(pendbaser, addr & 4, len, val);
+	pendbaser = vgic_sanitise_pendbaser(pendbaser);
+
+	vgic_cpu->pendbaser = pendbaser;
+}
+
 /*
  * The GICv3 per-IRQ registers are split to control PPIs and SGIs in the
  * redistributors, while SPIs are covered by registers in the distributor
@@ -232,10 +381,10 @@ static const struct vgic_register_region vgic_v3_rdbase_registers[] = {
 		vgic_mmio_read_v3r_typer, vgic_mmio_write_wi, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_DESC_WITH_LENGTH(GICR_PROPBASER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8,
+		vgic_mmio_read_propbase, vgic_mmio_write_propbase, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 8,
+		vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_DESC_WITH_LENGTH(GICR_IDREGS,
 		vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48,
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 8509014..71aa39d 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -147,4 +147,12 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
 
 unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
 
+#ifdef CONFIG_KVM_ARM_VGIC_V3
+u64 vgic_sanitise_outer_cacheability(u64 reg);
+u64 vgic_sanitise_inner_cacheability(u64 reg);
+u64 vgic_sanitise_shareability(u64 reg);
+u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
+			u64 (*sanitise_fn)(u64));
+#endif
+
 #endif
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index f0ac064..6f8f31f 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -191,6 +191,11 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
 	vmcrp->pmr  = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
 }
 
+#define INITIAL_PENDBASER_VALUE						  \
+	(GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb)		| \
+	GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, SameAsInner)	| \
+	GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable))
+
 void vgic_v3_enable(struct kvm_vcpu *vcpu)
 {
 	struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
@@ -208,10 +213,12 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu)
 	 * way, so we force SRE to 1 to demonstrate this to the guest.
 	 * This goes with the spec allowing the value to be RAO/WI.
 	 */
-	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
+	if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
 		vgic_v3->vgic_sre = ICC_SRE_EL1_SRE;
-	else
+		vcpu->arch.vgic_cpu.pendbaser = INITIAL_PENDBASER_VALUE;
+	} else {
 		vgic_v3->vgic_sre = 0;
+	}
 
 	/* Get the show on the road... */
 	vgic_v3->vgic_hcr = ICH_HCR_EN;
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 09/17] KVM: arm64: introduce ITS emulation file with MMIO framework
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (7 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 08/17] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-13  1:59 ` [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device Andre Przywara
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

The ARM GICv3 ITS emulation code goes into a separate file, but needs
to be connected to the GICv3 emulation, of which it is an option.
The ITS MMIO handlers require the respective ITS pointer to be passed in,
so we amend the existing VGIC MMIO framework to let it cope with that.
Also we introduce the basic ITS data structure and initialize it, but
don't return any success yet, as we are not yet ready for the show.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 arch/arm64/kvm/Makefile          |   1 +
 include/kvm/arm_vgic.h           |  22 ++++++++-
 virt/kvm/arm/vgic/vgic-its.c     | 100 +++++++++++++++++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-mmio-v3.c |  40 +++++++++++++++-
 virt/kvm/arm/vgic/vgic-mmio.c    |  37 ++++++++++++---
 virt/kvm/arm/vgic/vgic-mmio.h    |  17 +++++--
 virt/kvm/arm/vgic/vgic.h         |   7 +++
 7 files changed, 211 insertions(+), 13 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index f00b2cd..a5b9664 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -29,5 +29,6 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v2.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-mmio-v3.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-kvm-device.o
+kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic/vgic-its.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o
 kvm-$(CONFIG_KVM_ARM_PMU) += $(KVM)/arm/pmu.o
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index df2dec5..685f339 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -108,15 +108,35 @@ struct vgic_irq {
 };
 
 struct vgic_register_region;
+struct vgic_its;
+
+enum iodev_type {
+	IODEV_CPUIF,
+	IODEV_DIST,
+	IODEV_REDIST,
+	IODEV_ITS
+};
 
 struct vgic_io_device {
 	gpa_t base_addr;
-	struct kvm_vcpu *redist_vcpu;
+	union {
+		struct kvm_vcpu *redist_vcpu;
+		struct vgic_its *its;
+	};
 	const struct vgic_register_region *regions;
+	enum iodev_type iodev_type;
 	int nr_regions;
 	struct kvm_io_device dev;
 };
 
+struct vgic_its {
+	/* The base address of the ITS control register frame */
+	gpa_t			vgic_its_base;
+
+	bool			enabled;
+	struct vgic_io_device	iodev;
+};
+
 struct vgic_dist {
 	bool			in_kernel;
 	bool			ready;
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
new file mode 100644
index 0000000..06ad94d
--- /dev/null
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -0,0 +1,100 @@
+/*
+ * GICv3 ITS emulation
+ *
+ * Copyright (C) 2015,2016 ARM Ltd.
+ * Author: Andre Przywara <andre.przywara@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/cpu.h>
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+
+#include <linux/irqchip/arm-gic-v3.h>
+
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
+
+#include "vgic.h"
+#include "vgic-mmio.h"
+
+#define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
+{								\
+	.reg_offset = off,					\
+	.len = length,						\
+	.access_flags = acc,					\
+	.its_read = rd,						\
+	.its_write = wr,					\
+}
+
+static unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its,
+				       gpa_t addr, unsigned int len)
+{
+	return 0;
+}
+
+static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
+			      gpa_t addr, unsigned int len, unsigned long val)
+{
+	/* Ignore */
+}
+
+static struct vgic_register_region its_registers[] = {
+	REGISTER_ITS_DESC(GITS_CTLR,
+		its_mmio_read_raz, its_mmio_write_wi, 4,
+		VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_IIDR,
+		its_mmio_read_raz, its_mmio_write_wi, 4,
+		VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_TYPER,
+		its_mmio_read_raz, its_mmio_write_wi, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_CBASER,
+		its_mmio_read_raz, its_mmio_write_wi, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_CWRITER,
+		its_mmio_read_raz, its_mmio_write_wi, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_CREADR,
+		its_mmio_read_raz, its_mmio_write_wi, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_BASER,
+		its_mmio_read_raz, its_mmio_write_wi, 0x40,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(GITS_IDREGS_BASE,
+		its_mmio_read_raz, its_mmio_write_wi, 0x30,
+		VGIC_ACCESS_32bit),
+};
+
+int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
+{
+	struct vgic_io_device *iodev = &its->iodev;
+	int ret;
+
+	iodev->regions = its_registers;
+	iodev->nr_regions = ARRAY_SIZE(its_registers);
+	kvm_iodevice_init(&iodev->dev, &kvm_io_gic_ops);
+
+	iodev->base_addr = its->vgic_its_base;
+	iodev->iodev_type = IODEV_ITS;
+	iodev->its = its;
+	mutex_lock(&kvm->slots_lock);
+	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, iodev->base_addr,
+				      SZ_64K, &iodev->dev);
+	mutex_unlock(&kvm->slots_lock);
+
+	return ret;
+}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index ee299a7..4169e22 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -42,6 +42,16 @@ static u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
 	return reg | ((u64)val << lower);
 }
 
+bool vgic_has_its(struct kvm *kvm)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+
+	if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
+		return false;
+
+	return false;
+}
+
 static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
 					    gpa_t addr, unsigned int len)
 {
@@ -132,6 +142,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 	vgic_put_irq(vcpu->kvm, irq);
 }
 
+static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
+					     gpa_t addr, unsigned int len)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+
+	return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0;
+}
+
+
+static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
+				     gpa_t addr, unsigned int len,
+				     unsigned long val)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	bool was_enabled = vgic_cpu->lpis_enabled;
+
+	if (!vgic_has_its(vcpu->kvm))
+		return;
+
+	vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS;
+
+	if (!was_enabled && vgic_cpu->lpis_enabled) {
+		/* Eventually do something */
+	}
+}
+
 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
 					      gpa_t addr, unsigned int len)
 {
@@ -372,7 +408,7 @@ static const struct vgic_register_region vgic_v3_dist_registers[] = {
 
 static const struct vgic_register_region vgic_v3_rdbase_registers[] = {
 	REGISTER_DESC_WITH_LENGTH(GICR_CTLR,
-		vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
+		vgic_mmio_read_v3r_ctlr, vgic_mmio_write_v3r_ctlr, 4,
 		VGIC_ACCESS_32bit),
 	REGISTER_DESC_WITH_LENGTH(GICR_IIDR,
 		vgic_mmio_read_v3r_iidr, vgic_mmio_write_wi, 4,
@@ -450,6 +486,7 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
 
 		kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops);
 		rd_dev->base_addr = rd_base;
+		rd_dev->iodev_type = IODEV_REDIST;
 		rd_dev->regions = vgic_v3_rdbase_registers;
 		rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rdbase_registers);
 		rd_dev->redist_vcpu = vcpu;
@@ -464,6 +501,7 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
 
 		kvm_iodevice_init(&sgi_dev->dev, &kvm_io_gic_ops);
 		sgi_dev->base_addr = sgi_base;
+		sgi_dev->iodev_type = IODEV_REDIST;
 		sgi_dev->regions = vgic_v3_sgibase_registers;
 		sgi_dev->nr_regions = ARRAY_SIZE(vgic_v3_sgibase_registers);
 		sgi_dev->redist_vcpu = vcpu;
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 5e79e01..26be827 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -473,8 +473,7 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
 {
 	struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
 	const struct vgic_register_region *region;
-	struct kvm_vcpu *r_vcpu;
-	unsigned long data;
+	unsigned long data = 0;
 
 	region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
 				       addr - iodev->base_addr);
@@ -483,8 +482,20 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
 		return 0;
 	}
 
-	r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
-	data = region->read(r_vcpu, addr, len);
+	switch (iodev->iodev_type) {
+	case IODEV_CPUIF:
+		return 1;
+	case IODEV_DIST:
+		data = region->read(vcpu, addr, len);
+		break;
+	case IODEV_REDIST:
+		data = region->read(iodev->redist_vcpu, addr, len);
+		break;
+	case IODEV_ITS:
+		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
+		break;
+	}
+
 	vgic_data_host_to_mmio_bus(val, len, data);
 	return 0;
 }
@@ -494,7 +505,6 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
 {
 	struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
 	const struct vgic_register_region *region;
-	struct kvm_vcpu *r_vcpu;
 	unsigned long data = vgic_data_mmio_bus_to_host(val, len);
 
 	region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
@@ -505,8 +515,20 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
 	if (!check_region(region, addr, len))
 		return 0;
 
-	r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
-	region->write(r_vcpu, addr, len, data);
+	switch (iodev->iodev_type) {
+	case IODEV_CPUIF:
+		break;
+	case IODEV_DIST:
+		region->write(vcpu, addr, len, data);
+		break;
+	case IODEV_REDIST:
+		region->write(iodev->redist_vcpu, addr, len, data);
+		break;
+	case IODEV_ITS:
+		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
+		break;
+	}
+
 	return 0;
 }
 
@@ -536,6 +558,7 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
 	}
 
 	io_device->base_addr = dist_base_address;
+	io_device->iodev_type = IODEV_DIST;
 	io_device->redist_vcpu = NULL;
 
 	mutex_lock(&kvm->slots_lock);
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 71aa39d..366d663 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -21,10 +21,19 @@ struct vgic_register_region {
 	unsigned int len;
 	unsigned int bits_per_irq;
 	unsigned int access_flags;
-	unsigned long (*read)(struct kvm_vcpu *vcpu, gpa_t addr,
-			      unsigned int len);
-	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
-		      unsigned long val);
+	union {
+		unsigned long (*read)(struct kvm_vcpu *vcpu, gpa_t addr,
+				      unsigned int len);
+		unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
+					  gpa_t addr, unsigned int len);
+	};
+	union {
+		void (*write)(struct kvm_vcpu *vcpu, gpa_t addr,
+			      unsigned int len, unsigned long val);
+		void (*its_write)(struct kvm *kvm, struct vgic_its *its,
+				  gpa_t addr, unsigned int len,
+				  unsigned long val);
+	};
 };
 
 extern struct kvm_io_device_ops kvm_io_gic_ops;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 5b79c34..31807c1 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -72,6 +72,7 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu);
 int vgic_v3_probe(const struct gic_kvm_info *info);
 int vgic_v3_map_resources(struct kvm *kvm);
 int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
+bool vgic_has_its(struct kvm *kvm);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -123,6 +124,12 @@ static inline int vgic_register_redist_iodevs(struct kvm *kvm,
 {
 	return -ENODEV;
 }
+
+static inline bool vgic_has_its(struct kvm *kvm)
+{
+	return false;
+}
+
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (8 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 09/17] KVM: arm64: introduce ITS emulation file with MMIO framework Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-14  8:36   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers Andre Przywara
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 Documentation/virtual/kvm/devices/arm-vgic.txt |  25 +++--
 arch/arm/kvm/arm.c                             |   1 +
 arch/arm64/include/uapi/asm/kvm.h              |   2 +
 include/kvm/arm_vgic.h                         |   3 +
 include/uapi/linux/kvm.h                       |   2 +
 virt/kvm/arm/vgic/vgic-its.c                   | 144 ++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-kvm-device.c            |   7 +-
 virt/kvm/arm/vgic/vgic-mmio-v3.c               |   2 +-
 virt/kvm/arm/vgic/vgic.h                       |   8 ++
 9 files changed, 183 insertions(+), 11 deletions(-)

diff --git a/Documentation/virtual/kvm/devices/arm-vgic.txt b/Documentation/virtual/kvm/devices/arm-vgic.txt
index 59541d4..89182f8 100644
--- a/Documentation/virtual/kvm/devices/arm-vgic.txt
+++ b/Documentation/virtual/kvm/devices/arm-vgic.txt
@@ -4,16 +4,22 @@ ARM Virtual Generic Interrupt Controller (VGIC)
 Device types supported:
   KVM_DEV_TYPE_ARM_VGIC_V2     ARM Generic Interrupt Controller v2.0
   KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
+  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
 
-Only one VGIC instance may be instantiated through either this API or the
-legacy KVM_CREATE_IRQCHIP api.  The created VGIC will act as the VM interrupt
-controller, requiring emulated user-space devices to inject interrupts to the
-VGIC instead of directly to CPUs.
+Only one VGIC instance of the V2/V3 types above may be instantiated through
+either this API or the legacy KVM_CREATE_IRQCHIP api.  The created VGIC will
+act as the VM interrupt controller, requiring emulated user-space devices to
+inject interrupts to the VGIC instead of directly to CPUs.
 
 Creating a guest GICv3 device requires a host GICv3 as well.
 GICv3 implementations with hardware compatibility support allow a guest GICv2
 as well.
 
+Creating a virtual ITS controller requires a host GICv3 (but does not depend
+on having physical ITS controllers).
+There can be multiple ITS controllers per guest, each of them has to have
+a separate, non-overlapping MMIO region.
+
 Groups:
   KVM_DEV_ARM_VGIC_GRP_ADDR
   Attributes:
@@ -39,6 +45,13 @@ Groups:
       Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
       This address needs to be 64K aligned.
 
+    KVM_VGIC_V3_ADDR_TYPE_ITS (rw, 64-bit)
+      Base address in the guest physical address space of the GICv3 ITS
+      control register frame. The ITS allows MSI(-X) interrupts to be
+      injected into guests. This extension is optional. If the kernel
+      does not support the ITS, the call returns -ENODEV.
+      Only valid for KVM_DEV_TYPE_ARM_VGIC_ITS.
+      This address needs to be 64K aligned and the region covers 128K.
 
   KVM_DEV_ARM_VGIC_GRP_DIST_REGS
   Attributes:
@@ -109,8 +122,8 @@ Groups:
   KVM_DEV_ARM_VGIC_GRP_CTRL
   Attributes:
     KVM_DEV_ARM_VGIC_CTRL_INIT
-      request the initialization of the VGIC, no additional parameter in
-      kvm_device_attr.addr.
+      request the initialization of the VGIC or ITS, no additional parameter
+      in kvm_device_attr.addr.
   Errors:
     -ENXIO: VGIC not properly configured as required prior to calling
      this attribute
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 557e390..0d5c255 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -20,6 +20,7 @@
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/kvm_host.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index f209ea1..3051f86 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -87,9 +87,11 @@ struct kvm_regs {
 /* Supported VGICv3 address types  */
 #define KVM_VGIC_V3_ADDR_TYPE_DIST	2
 #define KVM_VGIC_V3_ADDR_TYPE_REDIST	3
+#define KVM_VGIC_ITS_ADDR_TYPE		4
 
 #define KVM_VGIC_V3_DIST_SIZE		SZ_64K
 #define KVM_VGIC_V3_REDIST_SIZE		(2 * SZ_64K)
+#define KVM_VGIC_V3_ITS_SIZE		(2 * SZ_64K)
 
 #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
 #define KVM_ARM_VCPU_EL1_32BIT		1 /* CPU running a 32bit VM */
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 685f339..8609fac 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -134,6 +134,7 @@ struct vgic_its {
 	gpa_t			vgic_its_base;
 
 	bool			enabled;
+	bool			initialized;
 	struct vgic_io_device	iodev;
 };
 
@@ -167,6 +168,8 @@ struct vgic_dist {
 
 	struct vgic_io_device	dist_iodev;
 
+	bool			has_its;
+
 	/*
 	 * Contains the attributes and gpa of the LPI configuration table.
 	 * Since we report GICR_TYPER.CommonLPIAff as 0b00, we can share
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 7de96f5..d8c4c32 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1077,6 +1077,8 @@ enum kvm_device_type {
 #define KVM_DEV_TYPE_FLIC		KVM_DEV_TYPE_FLIC
 	KVM_DEV_TYPE_ARM_VGIC_V3,
 #define KVM_DEV_TYPE_ARM_VGIC_V3	KVM_DEV_TYPE_ARM_VGIC_V3
+	KVM_DEV_TYPE_ARM_VGIC_ITS,
+#define KVM_DEV_TYPE_ARM_VGIC_ITS	KVM_DEV_TYPE_ARM_VGIC_ITS
 	KVM_DEV_TYPE_MAX,
 };
 
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 06ad94d..393ad3a 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -21,6 +21,7 @@
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
+#include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
 
@@ -79,7 +80,7 @@ static struct vgic_register_region its_registers[] = {
 		VGIC_ACCESS_32bit),
 };
 
-int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
+static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
 {
 	struct vgic_io_device *iodev = &its->iodev;
 	int ret;
@@ -93,8 +94,147 @@ int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
 	iodev->its = its;
 	mutex_lock(&kvm->slots_lock);
 	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, iodev->base_addr,
-				      SZ_64K, &iodev->dev);
+				      KVM_VGIC_V3_ITS_SIZE, &iodev->dev);
 	mutex_unlock(&kvm->slots_lock);
 
 	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:
+			if (its->initialized)
+				return 0;
+
+			if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base))
+				return -ENXIO;
+
+			ret = vgic_its_register(dev->kvm, its);
+			if (ret)
+				return ret;
+
+			its->initialized = true;
+			return 0;
+		}
+		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;
+}
+
+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);
+}
diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
index 2f24f13..1813f93 100644
--- a/virt/kvm/arm/vgic/vgic-kvm-device.c
+++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
@@ -21,8 +21,8 @@
 
 /* common helpers */
 
-static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
-			     phys_addr_t addr, phys_addr_t alignment)
+int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
+		      phys_addr_t addr, phys_addr_t alignment)
 {
 	if (addr & ~KVM_PHYS_MASK)
 		return -E2BIG;
@@ -223,6 +223,9 @@ int kvm_register_vgic_device(unsigned long type)
 	case KVM_DEV_TYPE_ARM_VGIC_V3:
 		ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
 					      KVM_DEV_TYPE_ARM_VGIC_V3);
+		if (ret)
+			break;
+		ret = kvm_vgic_register_its_device();
 		break;
 #endif
 	}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 4169e22..6cb52dd 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)
 	if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
 		return false;
 
-	return false;
+	return dist->has_its;
 }
 
 static 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 31807c1..9dc7207 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);
 bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
 void vgic_kick_vcpus(struct kvm *kvm);
 
+int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
+		      phys_addr_t addr, phys_addr_t alignment);
+
 void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
 void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
 void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
@@ -73,6 +76,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info);
 int vgic_v3_map_resources(struct kvm *kvm);
 int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
 bool vgic_has_its(struct kvm *kvm);
+int kvm_vgic_register_its_device(void);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -130,6 +134,10 @@ static inline bool vgic_has_its(struct kvm *kvm)
 	return false;
 }
 
+static inline int kvm_vgic_register_its_device(void)
+{
+	return -ENODEV;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (9 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-14  9:13   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 12/17] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

Add emulation for some basic MMIO registers used in the ITS emulation.
This includes:
- GITS_{CTLR,TYPER,IIDR}
- ID registers
- GITS_{CBASER,CREADR,CWRITER}
  (which implement the ITS command buffer handling)
- GITS_BASER<n>

Most of the handlers are pretty straight forward, only the CWRITER
handler is a bit more involved by taking the new its_cmd mutex and
then iterating over the command buffer.
The registers holding base addresses and attributes are sanitised before
storing them.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/arm_vgic.h           |  16 ++
 virt/kvm/arm/vgic/vgic-its.c     | 394 ++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-mmio-v3.c |   8 +-
 virt/kvm/arm/vgic/vgic-mmio.h    |   6 +
 virt/kvm/arm/vgic/vgic.c         |  12 +-
 5 files changed, 419 insertions(+), 17 deletions(-)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 8609fac..6186749 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <kvm/iodev.h>
+#include <linux/list.h>
 
 #define VGIC_V3_MAX_CPUS	255
 #define VGIC_V2_MAX_CPUS	8
@@ -136,6 +137,21 @@ struct vgic_its {
 	bool			enabled;
 	bool			initialized;
 	struct vgic_io_device	iodev;
+
+	/* These registers correspond to GITS_BASER{0,1} */
+	u64			baser_device_table;
+	u64			baser_coll_table;
+
+	/* Protects the command queue */
+	struct mutex		cmd_lock;
+	u64			cbaser;
+	u32			creadr;
+	u32			cwriter;
+
+	/* Protects the device and collection lists */
+	struct mutex		its_lock;
+	struct list_head	device_list;
+	struct list_head	collection_list;
 };
 
 struct vgic_dist {
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 393ad3a..dc9a98f 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -21,6 +21,7 @@
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
+#include <linux/list.h>
 #include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
@@ -32,6 +33,326 @@
 #include "vgic.h"
 #include "vgic-mmio.h"
 
+struct its_device {
+	struct list_head dev_list;
+
+	/* the head for the list of ITTEs */
+	struct list_head itt_head;
+	u32 device_id;
+};
+
+#define COLLECTION_NOT_MAPPED ((u32)~0)
+
+struct its_collection {
+	struct list_head coll_list;
+
+	u32 collection_id;
+	u32 target_addr;
+};
+
+#define its_is_collection_mapped(coll) ((coll) && \
+				((coll)->target_addr != COLLECTION_NOT_MAPPED))
+
+struct its_itte {
+	struct list_head itte_list;
+
+	struct its_collection *collection;
+	u32 lpi;
+	u32 event_id;
+};
+
+#define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
+
+static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
+					     struct vgic_its *its,
+					     gpa_t addr, unsigned int len)
+{
+	u32 reg = 0;
+
+	mutex_lock(&its->cmd_lock);
+	if (its->creadr == its->cwriter)
+		reg |= GITS_CTLR_QUIESCENT;
+	if (its->enabled)
+		reg |= GITS_CTLR_ENABLE;
+	mutex_unlock(&its->cmd_lock);
+
+	return reg;
+}
+
+static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
+				     gpa_t addr, unsigned int len,
+				     unsigned long val)
+{
+	its->enabled = !!(val & GITS_CTLR_ENABLE);
+}
+
+static unsigned long vgic_mmio_read_its_typer(struct kvm *kvm,
+					      struct vgic_its *its,
+					      gpa_t addr, unsigned int len)
+{
+	u64 reg = GITS_TYPER_PLPIS;
+
+	/*
+	 * We use linear CPU numbers for redistributor addressing,
+	 * so GITS_TYPER.PTA is 0.
+	 * Also we force all PROPBASER registers to be the same, so
+	 * CommonLPIAff is 0 as well.
+	 * To avoid memory waste in the guest, we keep the number of IDBits and
+	 * DevBits low - as least for the time being.
+	 */
+	reg |= 0x0f << GITS_TYPER_DEVBITS_SHIFT;
+	reg |= 0x0f << GITS_TYPER_IDBITS_SHIFT;
+
+	return extract_bytes(reg, addr & 7, len);
+}
+
+static unsigned long vgic_mmio_read_its_iidr(struct kvm *kvm,
+					     struct vgic_its *its,
+					     gpa_t addr, unsigned int len)
+{
+	return (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
+}
+
+static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
+					       struct vgic_its *its,
+					       gpa_t addr, unsigned int len)
+{
+	switch (addr & 0xffff) {
+	case GITS_PIDR0:
+		return 0x92;	/* part number, bits[7:0] */
+	case GITS_PIDR1:
+		return 0xb4;	/* part number, bits[11:8] */
+	case GITS_PIDR2:
+		return GIC_PIDR2_ARCH_GICv3 | 0x0b;
+	case GITS_PIDR4:
+		return 0x40;	/* This is a 64K software visible page */
+	/* The following are the ID registers for (any) GIC. */
+	case GITS_CIDR0:
+		return 0x0d;
+	case GITS_CIDR1:
+		return 0xf0;
+	case GITS_CIDR2:
+		return 0x05;
+	case GITS_CIDR3:
+		return 0xb1;
+	}
+
+	return 0;
+}
+
+/* Requires the its_lock to be held. */
+static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
+{
+	list_del(&itte->itte_list);
+	kfree(itte);
+}
+
+static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
+				   u64 *its_cmd)
+{
+	return -ENODEV;
+}
+
+static u64 vgic_sanitise_its_baser(u64 reg)
+{
+	reg = vgic_sanitise_field(reg, GITS_BASER_SHAREABILITY_MASK,
+				  GITS_BASER_SHAREABILITY_SHIFT,
+				  vgic_sanitise_shareability);
+	reg = vgic_sanitise_field(reg, GITS_BASER_CACHEABILITY_MASK,
+				  GITS_BASER_INNER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_inner_cacheability);
+	reg = vgic_sanitise_field(reg, GITS_BASER_OUTER_CACHEABILITY_MASK,
+				  GITS_BASER_OUTER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_outer_cacheability);
+
+	/* When using 64K pages, we clear bits to limit the PA to 48 bits. */
+	if ((reg & GITS_BASER_PAGE_SIZE_MASK) == GITS_BASER_PAGE_SIZE_64K)
+		reg &= ~GENMASK_ULL(15, 12);
+
+	return reg;
+}
+
+static u64 vgic_sanitise_its_cbaser(u64 reg)
+{
+	reg = vgic_sanitise_field(reg, GITS_CBASER_SHAREABILITY_MASK,
+				  GITS_CBASER_SHAREABILITY_SHIFT,
+				  vgic_sanitise_shareability);
+	reg = vgic_sanitise_field(reg, GITS_CBASER_CACHEABILITY_MASK,
+				  GITS_CBASER_INNER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_inner_cacheability);
+	reg = vgic_sanitise_field(reg, GITS_CBASER_OUTER_CACHEABILITY_MASK,
+				  GITS_CBASER_OUTER_CACHEABILITY_SHIFT,
+				  vgic_sanitise_outer_cacheability);
+
+	/*
+	 * Sanitise the physical address to be 64k aligned (for 4K and 16K
+	 * pages), also limit the physical addresses to 48 bits.
+	 */
+	reg &= ~(GENMASK_ULL(51, 48) | GENMASK_ULL(15, 12));
+
+	return reg;
+}
+
+static unsigned long vgic_mmio_read_its_cbaser(struct kvm *kvm,
+					       struct vgic_its *its,
+					       gpa_t addr, unsigned int len)
+{
+	return extract_bytes(its->cbaser, addr & 7, len);
+}
+
+static void vgic_mmio_write_its_cbaser(struct kvm *kvm, struct vgic_its *its,
+				       gpa_t addr, unsigned int len,
+				       unsigned long val)
+{
+	/* When GITS_CTLR.Enable is 1, this register is RO. */
+	if (its->enabled)
+		return;
+
+	mutex_lock(&its->cmd_lock);
+	its->cbaser = update_64bit_reg(its->cbaser, addr & 7, len, val);
+	its->cbaser = vgic_sanitise_its_cbaser(its->cbaser);
+	its->creadr = 0;
+	/*
+	 * CWRITER is architecturally UNKNOWN on reset, but we need to reset
+	 * it to CREADR to make sure we start with an empty command buffer.
+	 */
+	its->cwriter = its->creadr;
+	mutex_unlock(&its->cmd_lock);
+}
+
+#define ITS_CMD_BUFFER_SIZE(baser)	((((baser) & 0xff) + 1) << 12)
+#define ITS_CMD_SIZE			32
+
+/*
+ * By writing to CWRITER the guest announces new commands to be processed.
+ * To avoid any races in the first place, we take the its_cmd lock, which
+ * protects our ring buffer variables, so that there is only one user
+ * per ITS handling commands@a given time.
+ */
+static void vgic_mmio_write_its_cwriter(struct kvm *kvm, struct vgic_its *its,
+					gpa_t addr, unsigned int len,
+					unsigned long val)
+{
+	gpa_t cbaser;
+	u64 cmd_buf[4];
+	u32 reg;
+
+	if (!its)
+		return;
+
+	cbaser = CBASER_ADDRESS(its->cbaser);
+
+	reg = update_64bit_reg(its->cwriter & 0xfffe0, addr & 7, len, val);
+	reg &= 0xfffe0;
+	if (reg > ITS_CMD_BUFFER_SIZE(its->cbaser))
+		return;
+
+	mutex_lock(&its->cmd_lock);
+
+	its->cwriter = reg;
+
+	while (its->cwriter != its->creadr) {
+		int ret = kvm_read_guest(kvm, cbaser + its->creadr,
+					 cmd_buf, ITS_CMD_SIZE);
+		/*
+		 * If kvm_read_guest() fails, this could be due to the guest
+		 * programming a bogus value in CBASER or something else going
+		 * wrong from which we cannot easily recover.
+		 * We just ignore that command then.
+		 */
+		if (!ret)
+			vgic_its_handle_command(kvm, its, cmd_buf);
+
+		its->creadr += ITS_CMD_SIZE;
+		if (its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser))
+			its->creadr = 0;
+	}
+
+	mutex_unlock(&its->cmd_lock);
+}
+
+static unsigned long vgic_mmio_read_its_cwriter(struct kvm *kvm,
+						struct vgic_its *its,
+						gpa_t addr, unsigned int len)
+{
+	return extract_bytes(its->cwriter & 0xfffe0, addr & 0x7, len);
+}
+
+static unsigned long vgic_mmio_read_its_creadr(struct kvm *kvm,
+					       struct vgic_its *its,
+					       gpa_t addr, unsigned int len)
+{
+	return extract_bytes(its->creadr & 0xfffe0, addr & 0x7, len);
+}
+
+#define BASER_INDEX(addr) (((addr) / sizeof(u64)) & 0x7)
+static unsigned long vgic_mmio_read_its_baser(struct kvm *kvm,
+					      struct vgic_its *its,
+					      gpa_t addr, unsigned int len)
+{
+	u64 reg;
+
+	switch (BASER_INDEX(addr)) {
+	case 0:
+		reg = its->baser_device_table;
+		break;
+	case 1:
+		reg = its->baser_coll_table;
+		break;
+	default:
+		reg = 0;
+		break;
+	}
+
+	return extract_bytes(reg, addr & 7, len);
+}
+
+#define GITS_BASER_RO_MASK	(GENMASK_ULL(52, 48) | GENMASK_ULL(58, 56))
+static void vgic_mmio_write_its_baser(struct kvm *kvm,
+				      struct vgic_its *its,
+				      gpa_t addr, unsigned int len,
+				      unsigned long val)
+{
+	u64 entry_size, device_type;
+	u64 reg, *regptr, clearbits = 0;
+
+	/* When GITS_CTLR.Enable is 1, we ignore write accesses. */
+	if (its->enabled)
+		return;
+
+	switch (BASER_INDEX(addr)) {
+	case 0:
+		regptr = &its->baser_device_table;
+		entry_size = 8;
+		device_type = GITS_BASER_TYPE_DEVICE;
+		break;
+	case 1:
+		regptr = &its->baser_coll_table;
+		entry_size = 8;
+		device_type = GITS_BASER_TYPE_COLLECTION;
+		clearbits = GITS_BASER_INDIRECT;
+		break;
+	default:
+		return;
+	}
+
+	reg = update_64bit_reg(*regptr, addr & 7, len, val);
+	reg &= ~GITS_BASER_RO_MASK;
+	reg &= ~clearbits;
+
+	/*
+	 * When the guest wants to use 64K pages, bits 15:12 contain bits 51:48
+	 * of the PA, which we don't support. Clear them to emulate RAZ/WI.
+	 */
+	if (reg & GITS_BASER_PAGE_SIZE_64K)
+		reg &= ~GENMASK_ULL(15, 12);
+	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+	reg |= device_type << GITS_BASER_TYPE_SHIFT;
+	reg = vgic_sanitise_its_baser(reg);
+
+	*regptr = reg;
+}
+
 #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
 {								\
 	.reg_offset = off,					\
@@ -41,8 +362,8 @@
 	.its_write = wr,					\
 }
 
-static unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its,
-				       gpa_t addr, unsigned int len)
+unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its,
+				gpa_t addr, unsigned int len)
 {
 	return 0;
 }
@@ -55,28 +376,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
 
 static struct vgic_register_region its_registers[] = {
 	REGISTER_ITS_DESC(GITS_CTLR,
-		its_mmio_read_raz, its_mmio_write_wi, 4,
+		vgic_mmio_read_its_ctlr, vgic_mmio_write_its_ctlr, 4,
 		VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_IIDR,
-		its_mmio_read_raz, its_mmio_write_wi, 4,
+		vgic_mmio_read_its_iidr, its_mmio_write_wi, 4,
 		VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_TYPER,
-		its_mmio_read_raz, its_mmio_write_wi, 8,
+		vgic_mmio_read_its_typer, its_mmio_write_wi, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_CBASER,
-		its_mmio_read_raz, its_mmio_write_wi, 8,
+		vgic_mmio_read_its_cbaser, vgic_mmio_write_its_cbaser, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_CWRITER,
-		its_mmio_read_raz, its_mmio_write_wi, 8,
+		vgic_mmio_read_its_cwriter, vgic_mmio_write_its_cwriter, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_CREADR,
-		its_mmio_read_raz, its_mmio_write_wi, 8,
+		vgic_mmio_read_its_creadr, its_mmio_write_wi, 8,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_BASER,
-		its_mmio_read_raz, its_mmio_write_wi, 0x40,
+		vgic_mmio_read_its_baser, vgic_mmio_write_its_baser, 0x40,
 		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 	REGISTER_ITS_DESC(GITS_IDREGS_BASE,
-		its_mmio_read_raz, its_mmio_write_wi, 0x30,
+		vgic_mmio_read_its_idregs, its_mmio_write_wi, 0x30,
 		VGIC_ACCESS_32bit),
 };
 
@@ -100,6 +421,18 @@ static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
 	return ret;
 }
 
+#define INITIAL_BASER_VALUE						  \
+	(GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb)		| \
+	 GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, SameAsInner)		| \
+	 GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable)		| \
+	 ((8ULL - 1) << GITS_BASER_ENTRY_SIZE_SHIFT)			| \
+	 GITS_BASER_PAGE_SIZE_64K)
+
+#define INITIAL_PROPBASER_VALUE						  \
+	(GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb)		| \
+	 GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, SameAsInner)	| \
+	 GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable))
+
 static int vgic_its_create(struct kvm_device *dev, u32 type)
 {
 	struct vgic_its *its;
@@ -111,12 +444,24 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
 	if (!its)
 		return -ENOMEM;
 
+	mutex_init(&its->its_lock);
+	mutex_init(&its->cmd_lock);
+
 	its->vgic_its_base = VGIC_ADDR_UNDEF;
 
+	INIT_LIST_HEAD(&its->device_list);
+	INIT_LIST_HEAD(&its->collection_list);
+
 	dev->kvm->arch.vgic.has_its = true;
 	its->initialized = false;
 	its->enabled = false;
 
+	its->baser_device_table = INITIAL_BASER_VALUE			|
+		((u64)GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT);
+	its->baser_coll_table = INITIAL_BASER_VALUE |
+		((u64)GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT);
+	dev->kvm->arch.vgic.propbaser = INITIAL_PROPBASER_VALUE;
+
 	dev->private = its;
 
 	return 0;
@@ -124,7 +469,36 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
 
 static void vgic_its_destroy(struct kvm_device *kvm_dev)
 {
+	struct kvm *kvm = kvm_dev->kvm;
 	struct vgic_its *its = kvm_dev->private;
+	struct its_device *dev;
+	struct its_itte *itte;
+	struct list_head *dev_cur, *dev_temp;
+	struct list_head *cur, *temp;
+
+	/*
+	 * We may end up here without the lists ever having been initialized.
+	 * Check this and bail out early to avoid dereferencing a NULL pointer.
+	 */
+	if (!its->device_list.next)
+		return;
+
+	mutex_lock(&its->its_lock);
+	list_for_each_safe(dev_cur, dev_temp, &its->device_list) {
+		dev = container_of(dev_cur, struct its_device, dev_list);
+		list_for_each_safe(cur, temp, &dev->itt_head) {
+			itte = (container_of(cur, struct its_itte, itte_list));
+			its_free_itte(kvm, itte);
+		}
+		list_del(dev_cur);
+		kfree(dev);
+	}
+
+	list_for_each_safe(cur, temp, &its->collection_list) {
+		list_del(cur);
+		kfree(container_of(cur, struct its_collection, coll_list));
+	}
+	mutex_unlock(&its->its_lock);
 
 	kfree(its);
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 6cb52dd..d98e009 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -23,15 +23,15 @@
 #include "vgic-mmio.h"
 
 /* extract @num bytes at @offset bytes offset in data */
-static unsigned long extract_bytes(unsigned long data, unsigned int offset,
-				   unsigned int num)
+unsigned long extract_bytes(unsigned long data, unsigned int offset,
+			    unsigned int num)
 {
 	return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
 }
 
 /* allows updates of any half of a 64-bit register (or the whole thing) */
-static u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
-			    unsigned long val)
+u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
+		     unsigned long val)
 {
 	int lower = (offset & 4) * 8;
 	int upper = lower + 8 * len - 1;
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 366d663..0b3ecf9 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -96,6 +96,12 @@ unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len);
 void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
 				unsigned long data);
 
+unsigned long extract_bytes(unsigned long data, unsigned int offset,
+			    unsigned int num);
+
+u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
+		     unsigned long val);
+
 unsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu,
 				 gpa_t addr, unsigned int len);
 
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 1fb2693..d7ea5df 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -33,10 +33,16 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
 
 /*
  * Locking order is always:
- *   vgic_cpu->ap_list_lock
- *     vgic_irq->irq_lock
+ * its->cmd_lock (mutex)
+ *   its->its_lock (mutex)
+ *     vgic_cpu->ap_list_lock
+ *       vgic_irq->irq_lock
  *
- * (that is, always take the ap_list_lock before the struct vgic_irq lock).
+ * If you need to take multiple locks, always take the upper lock first,
+ * then the lower ones, e.g. first take the its_lock, then the irq_lock.
+ * If you are already holding a lock and need to take a higher one, you
+ * have to drop the lower ranking lock first and re-aquire it after having
+ * taken the upper one.
  *
  * When taking more than one ap_list_lock at the same time, always take the
  * lowest numbered VCPU's ap_list_lock first, so:
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 12/17] KVM: arm64: connect LPIs to the VGIC emulation
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (10 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-13  1:59 ` [PATCH v9 13/17] KVM: arm64: read initial LPI pending table Andre Przywara
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

LPIs are dynamically created (mapped) at guest runtime and their
actual number can be quite high, but is mostly assigned using a very
sparse allocation scheme. So arrays are not an ideal data structure
to hold the information.
We use a spin-lock protected linked list to hold all mapped LPIs,
represented by their struct vgic_irq. This lock is grouped between the
ap_list_lock and the vgic_irq lock in our locking order.
Also we store a pointer to that struct vgic_irq in our struct its_itte,
so we can easily access it.
Eventually we call our new vgic_get_lpi() from vgic_get_irq(), so
the VGIC code gets transparently access to LPIs.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/arm_vgic.h        |  6 ++++
 virt/kvm/arm/vgic/vgic-init.c |  3 ++
 virt/kvm/arm/vgic/vgic-its.c  |  5 ++++
 virt/kvm/arm/vgic/vgic-v3.c   |  2 ++
 virt/kvm/arm/vgic/vgic.c      | 67 ++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 76 insertions(+), 7 deletions(-)

diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 6186749..a6ca326 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -77,6 +77,7 @@ enum vgic_irq_config {
 
 struct vgic_irq {
 	spinlock_t irq_lock;		/* Protects the content of the struct */
+	struct list_head lpi_list;	/* Used to link all LPIs together */
 	struct list_head ap_list;
 
 	struct kvm_vcpu *vcpu;		/* SGIs and PPIs: The VCPU
@@ -193,6 +194,11 @@ struct vgic_dist {
 	 * GICv3 spec: 6.1.2 "LPI Configuration tables"
 	 */
 	u64			propbaser;
+
+	/* Protects the lpi_list and the count value below. */
+	spinlock_t		lpi_list_lock;
+	struct list_head	lpi_list_head;
+	int			lpi_list_count;
 };
 
 struct vgic_v2_cpu_if {
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index ac3c1a5..535e713 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -157,6 +157,9 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
 	struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
 	int i;
 
+	INIT_LIST_HEAD(&dist->lpi_list_head);
+	spin_lock_init(&dist->lpi_list_lock);
+
 	dist->spis = kcalloc(nr_spis, sizeof(struct vgic_irq), GFP_KERNEL);
 	if (!dist->spis)
 		return  -ENOMEM;
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index dc9a98f..4fc830c 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -56,6 +56,7 @@ struct its_collection {
 struct its_itte {
 	struct list_head itte_list;
 
+	struct vgic_irq *irq;
 	struct its_collection *collection;
 	u32 lpi;
 	u32 event_id;
@@ -144,6 +145,10 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
 	list_del(&itte->itte_list);
+
+	/* This put matches the get in vgic_add_lpi. */
+	vgic_put_irq(kvm, itte->irq);
+
 	kfree(itte);
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 6f8f31f..0506543 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -81,6 +81,8 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 		else
 			intid = val & GICH_LR_VIRTUALID;
 		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
+		if (!irq)	/* An LPI could have been unmapped. */
+			continue;
 
 		spin_lock(&irq->irq_lock);
 
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index d7ea5df..6318ea0 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -36,7 +36,8 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
  * its->cmd_lock (mutex)
  *   its->its_lock (mutex)
  *     vgic_cpu->ap_list_lock
- *       vgic_irq->irq_lock
+ *       kvm->lpi_list_lock
+ *         vgic_irq->irq_lock
  *
  * If you need to take multiple locks, always take the upper lock first,
  * then the lower ones, e.g. first take the its_lock, then the irq_lock.
@@ -51,6 +52,41 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
  *     spin_lock(vcpuY->arch.vgic_cpu.ap_list_lock);
  */
 
+/*
+ * Iterate over the VM's list of mapped LPIs to find the one with a
+ * matching interrupt ID and return a reference to the IRQ structure.
+ */
+static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq = NULL;
+
+	spin_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
+		if (irq->intid != intid)
+			continue;
+
+		/*
+		 * This increases the refcount, the caller is expected to
+		 * call vgic_put_irq() later once it's finished with the IRQ.
+		 */
+		kref_get(&irq->refcount);
+		goto out_unlock;
+	}
+	irq = NULL;
+
+out_unlock:
+	spin_unlock(&dist->lpi_list_lock);
+
+	return irq;
+}
+
+/*
+ * This looks up the virtual interrupt ID to get the corresponding
+ * struct vgic_irq. It also increases the refcount, so any caller is expected
+ * to call vgic_put_irq() once it's finished with this IRQ.
+ */
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid)
 {
@@ -69,23 +105,40 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 		return irq;
 	}
 
-	/* LPIs are not yet covered */
-	if (intid >= VGIC_MIN_LPI)
-		return NULL;
+	if (intid >= VGIC_MIN_LPI)		/* LPIs */
+		return vgic_get_lpi(kvm, intid);
 
 	WARN(1, "Looking up struct vgic_irq for reserved INTID");
 	return NULL;
 }
 
-/* The refcount should never drop to 0 at the moment. */
+/*
+ * We can't do anything in here, because we lack the kvm pointer to
+ * lock and remove the item from the lpi_list. So we keep this function
+ * empty and use the return value of kref_put() to trigger the freeing.
+ */
 static void vgic_irq_release(struct kref *ref)
 {
-	WARN_ON(1);
 }
 
 void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
 {
-	kref_put(&irq->refcount, vgic_irq_release);
+	struct vgic_dist *dist;
+
+	if (!kref_put(&irq->refcount, vgic_irq_release))
+		return;
+
+	if (irq->intid < VGIC_MIN_LPI)
+		return;
+
+	dist = &kvm->arch.vgic;
+
+	spin_lock(&dist->lpi_list_lock);
+	list_del(&irq->lpi_list);
+	dist->lpi_list_count--;
+	spin_unlock(&dist->lpi_list_lock);
+
+	kfree(irq);
 }
 
 /**
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 13/17] KVM: arm64: read initial LPI pending table
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (11 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 12/17] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-14  9:34   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table Andre Przywara
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

The LPI pending status for a GICv3 redistributor is held in a table
in (guest) memory. To achieve reasonable performance, we cache the
pending bit in our struct vgic_irq. The initial pending state must be
read from guest memory upon enabling LPIs for this redistributor.
As we can't access the guest memory while we hold the lpi_list spinlock,
we create a snapshot of the LPI list and iterate over that.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-its.c | 91 ++++++++++++++++++++++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic.h     |  6 +++
 2 files changed, 97 insertions(+)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 4fc830c..f400ef1 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -63,6 +63,90 @@ struct its_itte {
 };
 
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
+#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
+
+/*
+ * Create a snapshot of the current LPI list, so that we can enumerate all
+ * LPIs without holding any lock.
+ * Returns the array length and puts the kmalloc'ed array into intid_ptr.
+ */
+static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq;
+	u32 *intids;
+	int irq_count = dist->lpi_list_count, i = 0;
+
+	/*
+	 * We use the current value of the list length, which may change
+	 * after the kmalloc. We don't care, because the guest shouldn't
+	 * change anything while the command handling is still running,
+	 * and in the worst case we would miss a new IRQ, which one wouldn't
+	 * expect to be covered by this command anyway.
+	 */
+	intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL);
+	if (!intids)
+		return -ENOMEM;
+
+	spin_lock(&dist->lpi_list_lock);
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
+		/* We don't need to "get" the IRQ, as we hold the list lock. */
+		intids[i] = irq->intid;
+		if (i++ == irq_count)
+			break;
+	}
+	spin_unlock(&dist->lpi_list_lock);
+
+	*intid_ptr = intids;
+	return irq_count;
+}
+
+/*
+ * Scan the whole LPI pending table and sync the pending bit in there
+ * with our own data structures. This relies on the LPI being
+ * mapped before.
+ */
+static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
+{
+	gpa_t pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
+	struct vgic_irq *irq;
+	u8 pendmask;
+	int ret = 0;
+	u32 *intids;
+	int nr_irqs, i;
+
+	nr_irqs = vgic_copy_lpi_list(vcpu->kvm, &intids);
+	if (nr_irqs < 0)
+		return nr_irqs;
+
+	for (i = 0; i < nr_irqs; i++) {
+		int byte_offset, bit_nr;
+		int last_byte_offset = -1;
+
+		byte_offset = intids[i] / BITS_PER_BYTE;
+		bit_nr = intids[i] % BITS_PER_BYTE;
+
+		if (byte_offset != last_byte_offset) {
+			ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
+					     &pendmask, 1);
+			if (ret) {
+				kfree(intids);
+				return ret;
+			}
+			last_byte_offset = byte_offset;
+		}
+
+		irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
+		spin_lock(&irq->irq_lock);
+		irq->pending = pendmask & (1U << bit_nr);
+		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_put_irq(vcpu->kvm, irq);
+	}
+
+	kfree(intids);
+
+	return ret;
+}
 
 static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
 					     struct vgic_its *its,
@@ -406,6 +490,13 @@ static struct vgic_register_region its_registers[] = {
 		VGIC_ACCESS_32bit),
 };
 
+/* This is called on setting the LPI enable bit in the redistributor. */
+void vgic_enable_lpis(struct kvm_vcpu *vcpu)
+{
+	if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ))
+		its_sync_lpi_pending_table(vcpu);
+}
+
 static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
 {
 	struct vgic_io_device *iodev = &its->iodev;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 9dc7207..32d8d82 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -25,6 +25,7 @@
 #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)
 
 #define INTERRUPT_ID_BITS_SPIS	10
+#define INTERRUPT_ID_BITS_ITS	16
 #define VGIC_PRI_BITS		5
 
 #define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
@@ -77,6 +78,7 @@ int vgic_v3_map_resources(struct kvm *kvm);
 int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
 bool vgic_has_its(struct kvm *kvm);
 int kvm_vgic_register_its_device(void);
+void vgic_enable_lpis(struct kvm_vcpu *vcpu);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -138,6 +140,10 @@ static inline int kvm_vgic_register_its_device(void)
 {
 	return -ENODEV;
 }
+
+static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
+{
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (12 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 13/17] KVM: arm64: read initial LPI pending table Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-14  9:46   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers Andre Przywara
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

The (system-wide) LPI configuration table is held in a table in
(guest) memory. To achieve reasonable performance, we cache this data
in our struct vgic_irq. If the guest updates the configuration data
(which consists of the enable bit and the priority value), it issues
an INV or INVALL command to allow us to update our information.
Provide functions that update that information for one LPI or all LPIs
mapped to a specific collection.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-its.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index f400ef1..60108f8 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -64,6 +64,45 @@ struct its_itte {
 
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
+#define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
+
+#define GIC_LPI_OFFSET 8192
+
+#define LPI_PROP_ENABLE_BIT(p)	((p) & LPI_PROP_ENABLED)
+#define LPI_PROP_PRIORITY(p)	((p) & 0xfc)
+
+/*
+ * Reads the configuration data for a given LPI from guest memory and
+ * updates the fields in struct vgic_irq.
+ * If filter_vcpu is not NULL, applies only if the IRQ is targeting this
+ * VCPU. Unconditionally applies if filter_vcpu is NULL.
+ */
+static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
+			     struct kvm_vcpu *filter_vcpu)
+{
+	u64 propbase = PROPBASER_ADDRESS(kvm->arch.vgic.propbaser);
+	u8 prop;
+	int ret;
+
+	ret = kvm_read_guest(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
+			     &prop, 1);
+
+	if (ret)
+		return ret;
+
+	spin_lock(&irq->irq_lock);
+
+	if (!filter_vcpu || filter_vcpu == irq->target_vcpu) {
+		irq->priority = LPI_PROP_PRIORITY(prop);
+		irq->enabled = LPI_PROP_ENABLE_BIT(prop);
+
+		vgic_queue_irq_unlock(kvm, irq);
+	} else {
+		spin_unlock(&irq->irq_lock);
+	}
+
+	return 0;
+}
 
 /*
  * Create a snapshot of the current LPI list, so that we can enumerate all
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (13 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-14 10:38   ` Marc Zyngier
  2016-07-13  1:59 ` [PATCH v9 16/17] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

The connection between a device, an event ID, the LPI number and the
associated CPU is stored in in-memory tables in a GICv3, but their
format is not specified by the spec. Instead software uses a command
queue in a ring buffer to let an ITS implementation use its own
format.
Implement handlers for the various ITS commands and let them store
the requested relation into our own data structures. Those data
structures are protected by the its_lock mutex.
Our internal ring buffer read and write pointers are protected by the
its_cmd mutex, so that only one VCPU per ITS can handle commands at
any given time.
Error handling is very basic at the moment, as we don't have a good
way of communicating errors to the guest (usually an SError).
The INT command handler is missing from this patch, as we gain the
capability of actually injecting MSIs into the guest only later on.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-its.c | 599 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 598 insertions(+), 1 deletion(-)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 60108f8..28abfcd 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -33,6 +33,67 @@
 #include "vgic.h"
 #include "vgic-mmio.h"
 
+/*
+ * Creates a new (reference to a) struct vgic_irq for a given LPI.
+ * If this LPI is already mapped on another ITS, we increase its refcount
+ * and return a pointer to the existing structure.
+ * If this is a "new" LPI, we allocate and initialize a new struct vgic_irq.
+ * This function returns a pointer to the _unlocked_ structure.
+ */
+static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq;
+
+	/* In this case there is no put, since we keep the reference. */
+	if (irq)
+		return irq;
+
+	irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL);
+	if (!irq)
+		return NULL;
+
+	INIT_LIST_HEAD(&irq->lpi_list);
+	INIT_LIST_HEAD(&irq->ap_list);
+	spin_lock_init(&irq->irq_lock);
+
+	irq->config = VGIC_CONFIG_EDGE;
+	kref_init(&irq->refcount);
+	irq->intid = intid;
+
+	spin_lock(&dist->lpi_list_lock);
+
+	/*
+	 * There could be a race with another vgic_add_lpi(), so we need to
+	 * check that we don't add a second list entry with the same LPI.
+	 */
+	list_for_each_entry(oldirq, &dist->lpi_list_head, lpi_list) {
+		if (oldirq->intid != intid)
+			continue;
+
+		/* Someone was faster with adding this LPI, lets use that. */
+		kfree(irq);
+		irq = oldirq;
+
+		/*
+		 * This increases the refcount, the caller is expected to
+		 * call vgic_put_irq() on the returned pointer once it's
+		 * finished with the IRQ.
+		 */
+		kref_get(&irq->refcount);
+
+		goto out_unlock;
+	}
+
+	list_add_tail(&irq->lpi_list, &dist->lpi_list_head);
+	dist->lpi_list_count++;
+
+out_unlock:
+	spin_unlock(&dist->lpi_list_lock);
+
+	return irq;
+}
+
 struct its_device {
 	struct list_head dev_list;
 
@@ -62,12 +123,70 @@ struct its_itte {
 	u32 event_id;
 };
 
+/*
+ * Find and returns a device in the device table for an ITS.
+ * Must be called with the its_lock mutex held.
+ */
+static struct its_device *find_its_device(struct vgic_its *its, u32 device_id)
+{
+	struct its_device *device;
+
+	list_for_each_entry(device, &its->device_list, dev_list)
+		if (device_id == device->device_id)
+			return device;
+
+	return NULL;
+}
+
+/*
+ * Find and returns an interrupt translation table entry (ITTE) for a given
+ * Device ID/Event ID pair on an ITS.
+ * Must be called with the its_lock mutex held.
+ */
+static struct its_itte *find_itte(struct vgic_its *its, u32 device_id,
+				  u32 event_id)
+{
+	struct its_device *device;
+	struct its_itte *itte;
+
+	device = find_its_device(its, device_id);
+	if (device == NULL)
+		return NULL;
+
+	list_for_each_entry(itte, &device->itt_head, itte_list)
+		if (itte->event_id == event_id)
+			return itte;
+
+	return NULL;
+}
+
+/* To be used as an iterator this macro misses the enclosing parentheses */
+#define for_each_lpi_its(dev, itte, its) \
+	list_for_each_entry(dev, &(its)->device_list, dev_list) \
+		list_for_each_entry(itte, &(dev)->itt_head, itte_list)
+
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
 #define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 
 #define GIC_LPI_OFFSET 8192
 
+/*
+ * Finds and returns a collection in the ITS collection table.
+ * Must be called with the its_lock mutex held.
+ */
+static struct its_collection *find_collection(struct vgic_its *its, int coll_id)
+{
+	struct its_collection *collection;
+
+	list_for_each_entry(collection, &its->collection_list, coll_list) {
+		if (coll_id == collection->collection_id)
+			return collection;
+	}
+
+	return NULL;
+}
+
 #define LPI_PROP_ENABLE_BIT(p)	((p) & LPI_PROP_ENABLED)
 #define LPI_PROP_PRIORITY(p)	((p) & 0xfc)
 
@@ -141,6 +260,48 @@ static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
 }
 
 /*
+ * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
+ * is targeting) to the VGIC's view, which deals with target VCPUs.
+ * Needs to be called whenever either the collection for a LPIs has
+ * changed or the collection itself got retargeted.
+ */
+static void update_affinity_itte(struct kvm *kvm, struct its_itte *itte)
+{
+	struct kvm_vcpu *vcpu;
+
+	vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
+
+	spin_lock(&itte->irq->irq_lock);
+	itte->irq->target_vcpu = vcpu;
+	spin_unlock(&itte->irq->irq_lock);
+}
+
+/*
+ * Updates the target VCPU for every LPI targeting this collection.
+ * Must be called with the its_lock mutex held.
+ */
+static void update_affinity_collection(struct kvm *kvm, struct vgic_its *its,
+				       struct its_collection *coll)
+{
+	struct its_device *device;
+	struct its_itte *itte;
+
+	for_each_lpi_its(device, itte, its) {
+		if (!itte->collection || coll != itte->collection)
+			continue;
+
+		update_affinity_itte(kvm, itte);
+	}
+}
+
+static u32 max_lpis_propbaser(u64 propbaser)
+{
+	int nr_idbits = (propbaser & 0x1f) + 1;
+
+	return 1U << min(nr_idbits, INTERRUPT_ID_BITS_ITS);
+}
+
+/*
  * Scan the whole LPI pending table and sync the pending bit in there
  * with our own data structures. This relies on the LPI being
  * mapped before.
@@ -275,10 +436,446 @@ static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 	kfree(itte);
 }
 
+static u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size)
+{
+	return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1);
+}
+
+#define its_cmd_get_command(cmd)	its_cmd_mask_field(cmd, 0,  0,  8)
+#define its_cmd_get_deviceid(cmd)	its_cmd_mask_field(cmd, 0, 32, 32)
+#define its_cmd_get_id(cmd)		its_cmd_mask_field(cmd, 1,  0, 32)
+#define its_cmd_get_physical_id(cmd)	its_cmd_mask_field(cmd, 1, 32, 32)
+#define its_cmd_get_collection(cmd)	its_cmd_mask_field(cmd, 2,  0, 16)
+#define its_cmd_get_target_addr(cmd)	its_cmd_mask_field(cmd, 2, 16, 32)
+#define its_cmd_get_validbit(cmd)	its_cmd_mask_field(cmd, 2, 63,  1)
+
+/*
+ * The DISCARD command frees an Interrupt Translation Table Entry (ITTE).
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
+				       u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	itte = find_itte(its, device_id, event_id);
+	if (itte && itte->collection) {
+		/*
+		 * Though the spec talks about removing the pending state, we
+		 * don't bother here since we clear the ITTE anyway and the
+		 * pending state is a property of the ITTE struct.
+		 */
+		its_free_itte(kvm, itte);
+		return 0;
+	}
+
+	return E_ITS_DISCARD_UNMAPPED_INTERRUPT;
+}
+
+/*
+ * The MOVI command moves an ITTE to a different collection.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
+				    u64 *its_cmd)
+{
+	u32 device_id = its_cmd_get_deviceid(its_cmd);
+	u32 event_id = its_cmd_get_id(its_cmd);
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct kvm_vcpu *vcpu;
+	struct its_itte *itte;
+	struct its_collection *collection;
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte)
+		return E_ITS_MOVI_UNMAPPED_INTERRUPT;
+
+	if (!its_is_collection_mapped(itte->collection))
+		return E_ITS_MOVI_UNMAPPED_COLLECTION;
+
+	collection = find_collection(its, coll_id);
+	if (!its_is_collection_mapped(collection))
+		return E_ITS_MOVI_UNMAPPED_COLLECTION;
+
+	itte->collection = collection;
+	vcpu = kvm_get_vcpu(kvm, collection->target_addr);
+
+	spin_lock(&itte->irq->irq_lock);
+	itte->irq->target_vcpu = vcpu;
+	spin_unlock(&itte->irq->irq_lock);
+
+	return 0;
+}
+
+static void vgic_its_init_collection(struct vgic_its *its,
+				     struct its_collection *collection,
+				     u32 coll_id)
+{
+	collection->collection_id = coll_id;
+	collection->target_addr = COLLECTION_NOT_MAPPED;
+
+	list_add_tail(&collection->coll_list, &its->collection_list);
+}
+
+/*
+ * The MAPTI and MAPI commands map LPIs to ITTEs.
+ * Must be called with its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
+				    u64 *its_cmd, u8 subcmd)
+{
+	u32 device_id = its_cmd_get_deviceid(its_cmd);
+	u32 event_id = its_cmd_get_id(its_cmd);
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct its_itte *itte;
+	struct its_device *device;
+	struct its_collection *collection, *new_coll = NULL;
+	int lpi_nr;
+
+	device = find_its_device(its, device_id);
+	if (!device)
+		return E_ITS_MAPTI_UNMAPPED_DEVICE;
+
+	collection = find_collection(its, coll_id);
+	if (!collection) {
+		new_coll = kzalloc(sizeof(struct its_collection), GFP_KERNEL);
+		if (!new_coll)
+			return -ENOMEM;
+	}
+
+	if (subcmd == GITS_CMD_MAPTI)
+		lpi_nr = its_cmd_get_physical_id(its_cmd);
+	else
+		lpi_nr = event_id;
+	if (lpi_nr < GIC_LPI_OFFSET ||
+	    lpi_nr >= max_lpis_propbaser(kvm->arch.vgic.propbaser)) {
+		kfree(new_coll);
+		return E_ITS_MAPTI_PHYSICALID_OOR;
+	}
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		itte = kzalloc(sizeof(struct its_itte), GFP_KERNEL);
+		if (!itte) {
+			kfree(new_coll);
+			return -ENOMEM;
+		}
+
+		itte->event_id	= event_id;
+		list_add_tail(&itte->itte_list, &device->itt_head);
+	}
+
+	if (!collection) {
+		collection = new_coll;
+		vgic_its_init_collection(its, collection, coll_id);
+	}
+
+	itte->collection = collection;
+	itte->lpi = lpi_nr;
+	itte->irq = vgic_add_lpi(kvm, lpi_nr);
+	update_affinity_itte(kvm, itte);
+
+	/*
+	 * We "cache" the configuration table entries in out struct vgic_irq's.
+	 * However we only have those structs for mapped IRQs, so we read in
+	 * the respective config data from memory here upon mapping the LPI.
+	 */
+	update_lpi_config(kvm, itte->irq, NULL);
+
+	return 0;
+}
+
+/* Requires the its_lock to be held. */
+static void vgic_its_unmap_device(struct kvm *kvm, struct its_device *device)
+{
+	struct its_itte *itte, *temp;
+
+	/*
+	 * The spec says that unmapping a device with still valid
+	 * ITTEs associated is UNPREDICTABLE. We remove all ITTEs,
+	 * since we cannot leave the memory unreferenced.
+	 */
+	list_for_each_entry_safe(itte, temp, &device->itt_head, itte_list)
+		its_free_itte(kvm, itte);
+
+	list_del(&device->dev_list);
+	kfree(device);
+}
+
+/*
+ * MAPD maps or unmaps a device ID to Interrupt Translation Tables (ITTs).
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its,
+				    u64 *its_cmd)
+{
+	bool valid = its_cmd_get_validbit(its_cmd);
+	u32 device_id = its_cmd_get_deviceid(its_cmd);
+	struct its_device *device;
+
+	device = find_its_device(its, device_id);
+	if (device)
+		vgic_its_unmap_device(kvm, device);
+
+	/*
+	 * The spec does not say whether unmapping a not-mapped device
+	 * is an error, so we are done in any case.
+	 */
+	if (!valid)
+		return 0;
+
+	device = kzalloc(sizeof(struct its_device), GFP_KERNEL);
+	if (!device)
+		return -ENOMEM;
+
+	device->device_id = device_id;
+	INIT_LIST_HEAD(&device->itt_head);
+
+	list_add_tail(&device->dev_list, &its->device_list);
+
+	return 0;
+}
+
+/*
+ * The MAPC command maps collection IDs to redistributors.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
+				    u64 *its_cmd)
+{
+	u16 coll_id;
+	u32 target_addr;
+	struct its_collection *collection;
+	bool valid;
+
+	valid = its_cmd_get_validbit(its_cmd);
+	coll_id = its_cmd_get_collection(its_cmd);
+	target_addr = its_cmd_get_target_addr(its_cmd);
+
+	if (target_addr >= atomic_read(&kvm->online_vcpus))
+		return E_ITS_MAPC_PROCNUM_OOR;
+
+	collection = find_collection(its, coll_id);
+
+	if (!valid) {
+		struct its_device *device;
+		struct its_itte *itte;
+		/*
+		 * Clearing the mapping for that collection ID removes the
+		 * entry from the list. If there wasn't any before, we can
+		 * go home early.
+		 */
+		if (!collection)
+			return 0;
+
+		for_each_lpi_its(device, itte, its)
+			if (itte->collection &&
+			    itte->collection->collection_id == coll_id)
+				itte->collection = NULL;
+
+		list_del(&collection->coll_list);
+		kfree(collection);
+	} else {
+		if (!collection) {
+			collection = kzalloc(sizeof(struct its_collection),
+					     GFP_KERNEL);
+			if (!collection)
+				return -ENOMEM;
+
+			vgic_its_init_collection(its, collection, coll_id);
+			collection->target_addr = target_addr;
+		} else {
+			collection->target_addr = target_addr;
+			update_affinity_collection(kvm, its, collection);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * The CLEAR command removes the pending state for a particular LPI.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its,
+				     u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte)
+		return E_ITS_CLEAR_UNMAPPED_INTERRUPT;
+
+	itte->irq->pending = false;
+
+	return 0;
+}
+
+/*
+ * The INV command syncs the configuration bits from the memory table.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
+				   u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte)
+		return E_ITS_INV_UNMAPPED_INTERRUPT;
+
+	return update_lpi_config(kvm, itte->irq, NULL);
+}
+
+/*
+ * The INVALL command requests flushing of all IRQ data in this collection.
+ * Find the VCPU mapped to that collection, then iterate over the VM's list
+ * of mapped LPIs and update the configuration for each IRQ which targets
+ * the specified vcpu. The configuration will be read from the in-memory
+ * configuration table.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
+				      u64 *its_cmd)
+{
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct its_collection *collection;
+	struct kvm_vcpu *vcpu;
+	struct vgic_irq *irq;
+	u32 *intids;
+	int irq_count, i;
+
+	collection = find_collection(its, coll_id);
+	if (!its_is_collection_mapped(collection))
+		return E_ITS_INVALL_UNMAPPED_COLLECTION;
+
+	vcpu = kvm_get_vcpu(kvm, collection->target_addr);
+
+	irq_count = vgic_copy_lpi_list(kvm, &intids);
+	if (irq_count < 0)
+		return irq_count;
+
+	for (i = 0; i < irq_count; i++) {
+		irq = vgic_get_irq(kvm, NULL, intids[i]);
+		if (!irq)
+			continue;
+		update_lpi_config(kvm, irq, vcpu);
+		vgic_put_irq(kvm, irq);
+	}
+
+	kfree(intids);
+
+	return 0;
+}
+
+/*
+ * The MOVALL command moves the pending state of all IRQs targeting one
+ * redistributor to another. We don't hold the pending state in the VCPUs,
+ * but in the IRQs instead, so there is really not much to do for us here.
+ * However the spec says that no IRQ must target the old redistributor
+ * afterwards, so we make sure that no LPI is using the associated target_vcpu.
+ * This command affects all LPIs in the system.
+ */
+static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
+				      u64 *its_cmd)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	u32 target1_addr = its_cmd_get_target_addr(its_cmd);
+	u32 target2_addr = its_cmd_mask_field(its_cmd, 3, 16, 32);
+	struct kvm_vcpu *vcpu1, *vcpu2;
+	struct vgic_irq *irq;
+
+	if (target1_addr >= atomic_read(&kvm->online_vcpus) ||
+	    target2_addr >= atomic_read(&kvm->online_vcpus))
+		return E_ITS_MOVALL_PROCNUM_OOR;
+
+	if (target1_addr == target2_addr)
+		return 0;
+
+	vcpu1 = kvm_get_vcpu(kvm, target1_addr);
+	vcpu2 = kvm_get_vcpu(kvm, target2_addr);
+
+	spin_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
+		spin_lock(&irq->irq_lock);
+
+		if (irq->target_vcpu == vcpu1)
+			irq->target_vcpu = vcpu2;
+
+		spin_unlock(&irq->irq_lock);
+	}
+
+	spin_unlock(&dist->lpi_list_lock);
+
+	return 0;
+}
+
+/*
+ * This function is called with the its_cmd lock held, but the ITS data
+ * structure lock dropped.
+ */
 static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
 				   u64 *its_cmd)
 {
-	return -ENODEV;
+	u8 cmd = its_cmd_get_command(its_cmd);
+	int ret = -ENODEV;
+
+	mutex_lock(&its->its_lock);
+	switch (cmd) {
+	case GITS_CMD_MAPD:
+		ret = vgic_its_cmd_handle_mapd(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPC:
+		ret = vgic_its_cmd_handle_mapc(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPI:
+		ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MAPTI:
+		ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MOVI:
+		ret = vgic_its_cmd_handle_movi(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_DISCARD:
+		ret = vgic_its_cmd_handle_discard(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_CLEAR:
+		ret = vgic_its_cmd_handle_clear(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MOVALL:
+		ret = vgic_its_cmd_handle_movall(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INV:
+		ret = vgic_its_cmd_handle_inv(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INVALL:
+		ret = vgic_its_cmd_handle_invall(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_SYNC:
+		/* we ignore this command: we are in sync all of the time */
+		ret = 0;
+		break;
+	}
+	mutex_unlock(&its->its_lock);
+
+	return ret;
 }
 
 static u64 vgic_sanitise_its_baser(u64 reg)
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 16/17] KVM: arm64: implement MSI injection in ITS emulation
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (14 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-13  1:59 ` [PATCH v9 17/17] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

When userland wants to inject an MSI into the guest, it uses the
KVM_SIGNAL_MSI ioctl, which carries the doorbell address along with
the payload and the device ID.
With the help of the KVM IO bus framework we learn the corresponding
ITS from the doorbell address. We then use our wrapper functions to
iterate the linked lists and find the proper Interrupt Translation Table
Entry (ITTE) and thus the corresponding struct vgic_irq to finally set
the pending bit.
We also provide the handler for the ITS "INT" command, which allows a
guest to trigger an MSI via the ITS command queue. Since this one knows
about the right ITS already, we directly call the MMIO handler function
without using the kvm_io_bus framework.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 virt/kvm/arm/vgic/vgic-its.c | 77 ++++++++++++++++++++++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic.h     |  6 ++++
 2 files changed, 83 insertions(+)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 28abfcd..4713e33 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -425,6 +425,65 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 	return 0;
 }
 
+/*
+ * Find the target VCPU and the LPI number for a given devid/eventid pair
+ * and make this IRQ pending, possibly injecting it.
+ * Must be called with the its_lock mutex held.
+ */
+static void vgic_its_trigger_msi(struct kvm *kvm, struct vgic_its *its,
+				 u32 devid, u32 eventid)
+{
+	struct its_itte *itte;
+
+	if (!its->enabled)
+		return;
+
+	itte = find_itte(its, devid, eventid);
+	/* Triggering an unmapped IRQ gets silently dropped. */
+	if (itte && its_is_collection_mapped(itte->collection)) {
+		struct kvm_vcpu *vcpu;
+
+		vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
+		if (vcpu && vcpu->arch.vgic_cpu.lpis_enabled) {
+			spin_lock(&itte->irq->irq_lock);
+			itte->irq->pending = true;
+			vgic_queue_irq_unlock(kvm, itte->irq);
+		}
+	}
+}
+
+/*
+ * Queries the KVM IO bus framework to get the ITS pointer from the given
+ * doorbell address.
+ * We then call vgic_its_trigger_msi() with the decoded data.
+ */
+int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	u64 address;
+	struct kvm_io_device *kvm_io_dev;
+	struct vgic_io_device *iodev;
+
+	if (!vgic_has_its(kvm))
+		return -ENODEV;
+
+	if (!(msi->flags & KVM_MSI_VALID_DEVID))
+		return -EINVAL;
+
+	address = (u64)msi->address_hi << 32 | msi->address_lo;
+
+	kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, address);
+	if (!kvm_io_dev)
+		return -ENODEV;
+
+	iodev = container_of(kvm_io_dev, struct vgic_io_device, dev);
+
+	mutex_lock(&iodev->its->its_lock);
+	vgic_its_trigger_msi(kvm, iodev->its, msi->devid, msi->data);
+	mutex_unlock(&iodev->its->its_lock);
+
+	return 0;
+}
+
 /* Requires the its_lock to be held. */
 static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
@@ -827,6 +886,21 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
 }
 
 /*
+ * The INT command injects the LPI associated with that DevID/EvID pair.
+ * Must be called with the its_lock mutex held.
+ */
+static int vgic_its_cmd_handle_int(struct kvm *kvm, struct vgic_its *its,
+				   u64 *its_cmd)
+{
+	u32 msi_data = its_cmd_get_id(its_cmd);
+	u64 msi_devid = its_cmd_get_deviceid(its_cmd);
+
+	vgic_its_trigger_msi(kvm, its, msi_devid, msi_data);
+
+	return 0;
+}
+
+/*
  * This function is called with the its_cmd lock held, but the ITS data
  * structure lock dropped.
  */
@@ -862,6 +936,9 @@ static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
 	case GITS_CMD_MOVALL:
 		ret = vgic_its_cmd_handle_movall(kvm, its, its_cmd);
 		break;
+	case GITS_CMD_INT:
+		ret = vgic_its_cmd_handle_int(kvm, its, its_cmd);
+		break;
 	case GITS_CMD_INV:
 		ret = vgic_its_cmd_handle_inv(kvm, its, its_cmd);
 		break;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 32d8d82..9d40d7b 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -79,6 +79,7 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
 bool vgic_has_its(struct kvm *kvm);
 int kvm_vgic_register_its_device(void);
 void vgic_enable_lpis(struct kvm_vcpu *vcpu);
+int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -144,6 +145,11 @@ static inline int kvm_vgic_register_its_device(void)
 static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
 {
 }
+
+static inline int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	return -ENODEV;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 17/17] KVM: arm64: enable ITS emulation as a virtual MSI controller
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (15 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 16/17] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
@ 2016-07-13  1:59 ` Andre Przywara
  2016-07-13  7:57 ` [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Diana Madalina Craciun
  2016-07-14 10:52 ` Marc Zyngier
  18 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  1:59 UTC (permalink / raw)
  To: linux-arm-kernel

Now that all ITS emulation functionality is in place, we advertise
MSI functionality to userland and also the ITS device to the guest - if
userland has configured that.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 Documentation/virtual/kvm/api.txt |  2 +-
 arch/arm64/kvm/Kconfig            |  1 +
 arch/arm64/kvm/reset.c            |  6 ++++++
 include/kvm/arm_vgic.h            |  5 +++++
 virt/kvm/arm/vgic/vgic-init.c     |  3 +++
 virt/kvm/arm/vgic/vgic-mmio-v3.c  | 14 ++++++++++----
 virt/kvm/arm/vgic/vgic.c          |  8 ++++++++
 7 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 6551311..07049ea 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2162,7 +2162,7 @@ after pausing the vcpu, but before it is resumed.
 4.71 KVM_SIGNAL_MSI
 
 Capability: KVM_CAP_SIGNAL_MSI
-Architectures: x86
+Architectures: x86 arm64
 Type: vm ioctl
 Parameters: struct kvm_msi (in)
 Returns: >0 on delivery, 0 if guest blocked the MSI, and -1 on error
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index aa2e34e..9d2eff0 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -36,6 +36,7 @@ config KVM
 	select HAVE_KVM_IRQFD
 	select KVM_ARM_VGIC_V3
 	select KVM_ARM_PMU if HW_PERF_EVENTS
+	select HAVE_KVM_MSI
 	---help---
 	  Support hosting virtualized guest machines.
 	  We don't support KVM with 16K page tables yet, due to the multiple
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index 3989833..d8c3140 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -86,6 +86,12 @@ int kvm_arch_dev_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_VCPU_ATTRIBUTES:
 		r = 1;
 		break;
+	case KVM_CAP_MSI_DEVID:
+		if (!kvm)
+			r = -EINVAL;
+		else
+			r = kvm->arch.vgic.msis_require_devid;
+		break;
 	default:
 		r = 0;
 	}
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index a6ca326..4e63a07 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -163,6 +163,9 @@ struct vgic_dist {
 	/* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */
 	u32			vgic_model;
 
+	/* Do injected MSIs require an additional device ID? */
+	bool			msis_require_devid;
+
 	int			nr_spis;
 
 	/* TODO: Consider moving to global state */
@@ -308,4 +311,6 @@ static inline int kvm_vgic_get_max_vcpus(void)
 	return kvm_vgic_global_state.max_gic_vcpus;
 }
 
+int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi);
+
 #endif /* __KVM_ARM_VGIC_H */
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 535e713..01a60dc 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -258,6 +258,9 @@ int vgic_init(struct kvm *kvm)
 	if (ret)
 		goto out;
 
+	if (vgic_has_its(kvm))
+		dist->msis_require_devid = true;
+
 	kvm_for_each_vcpu(i, vcpu, kvm)
 		kvm_vgic_vcpu_init(vcpu);
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index d98e009..9e7f6b2 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -66,7 +66,12 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
 	case GICD_TYPER:
 		value = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
 		value = (value >> 5) - 1;
-		value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
+		if (vgic_has_its(vcpu->kvm)) {
+			value |= (INTERRUPT_ID_BITS_ITS - 1) << 19;
+			value |= GICD_TYPER_LPIS;
+		} else {
+			value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
+		}
 		break;
 	case GICD_IIDR:
 		value = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
@@ -163,9 +168,8 @@ static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
 
 	vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS;
 
-	if (!was_enabled && vgic_cpu->lpis_enabled) {
-		/* Eventually do something */
-	}
+	if (!was_enabled && vgic_cpu->lpis_enabled)
+		vgic_enable_lpis(vcpu);
 }
 
 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
@@ -179,6 +183,8 @@ static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
 	value |= ((target_vcpu_id & 0xffff) << 8);
 	if (target_vcpu_id == atomic_read(&vcpu->kvm->online_vcpus) - 1)
 		value |= GICR_TYPER_LAST;
+	if (vgic_has_its(vcpu->kvm))
+		value |= GICR_TYPER_PLPIS;
 
 	return extract_bytes(value, addr & 7, len);
 }
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 6318ea0..c7cd1a3 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -709,3 +709,11 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
 
 	return map_is_active;
 }
+
+int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	if (vgic_has_its(kvm))
+		return vgic_its_inject_msi(kvm, msi);
+	else
+		return -ENODEV;
+}
-- 
2.9.0

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (16 preceding siblings ...)
  2016-07-13  1:59 ` [PATCH v9 17/17] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
@ 2016-07-13  7:57 ` Diana Madalina Craciun
  2016-07-13  8:15   ` Andre Przywara
  2016-07-14 10:52 ` Marc Zyngier
  18 siblings, 1 reply; 41+ messages in thread
From: Diana Madalina Craciun @ 2016-07-13  7:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Andre,

On 07/13/2016 04:59 AM, Andre Przywara wrote:
> To test this you can use the kvmtool patches available in the "its-v6"
> branch here [2].
> Start a guest with: "$ lkvm run --irqchip=gicv3-its --force-pci"
> and see the ITS being used for instance by the virtio devices.
>
> [1]: git://linux-arm.org/linux-ap.git
>      http://www.linux-arm.org/git?p=linux-ap.git;a=log;h=refs/heads/its-emul/v9
> [2]: git://linux-arm.org/kvmtool.git

It seems that its/emul/v9 branch does not exist.

Thanks,

Diana

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation
  2016-07-13  7:57 ` [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Diana Madalina Craciun
@ 2016-07-13  8:15   ` Andre Przywara
  0 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13  8:15 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Diana,

On 13/07/16 08:57, Diana Madalina Craciun wrote:
> Hi Andre,
> 
> On 07/13/2016 04:59 AM, Andre Przywara wrote:
>> To test this you can use the kvmtool patches available in the "its-v6"
>> branch here [2].
>> Start a guest with: "$ lkvm run --irqchip=gicv3-its --force-pci"
>> and see the ITS being used for instance by the virtio devices.
>>
>> [1]: git://linux-arm.org/linux-ap.git
>>      http://www.linux-arm.org/git?p=linux-ap.git;a=log;h=refs/heads/its-emul/v9
>> [2]: git://linux-arm.org/kvmtool.git
> 
> It seems that its/emul/v9 branch does not exist.

I could claim it was just a test to see whether someone cares ;-), but
in fact I just forgot it.
Just pushed it now, please try again!

Thanks,
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID
  2016-07-13  1:58 ` [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
@ 2016-07-13 10:28   ` Paolo Bonzini
  0 siblings, 0 replies; 41+ messages in thread
From: Paolo Bonzini @ 2016-07-13 10:28 UTC (permalink / raw)
  To: linux-arm-kernel



On 13/07/2016 03:58, Andre Przywara wrote:
> The ARM GICv3 ITS MSI controller requires a device ID to be able to
> assign the proper interrupt vector. On real hardware, this ID is
> sampled from the bus. To be able to emulate an ITS controller, extend
> the KVM MSI interface to let userspace provide such a device ID. For
> PCI devices, the device ID is simply the 16-bit bus-device-function
> triplet, which should be easily available to the userland tool.
> 
> Also there is a new KVM capability which advertises whether the
> current VM requires a device ID to be set along with the MSI data.
> This flag is still reported as not available everywhere, later we will
> enable it when ITS emulation is used.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Reviewed-by: Eric Auger <eric.auger@linaro.org>
> Acked-by: Christoffer Dall <christoffer.dall@linaro.org>

Acked-by: Paolo Bonzini <pbonzini@redhat.com>

> ---
>  Documentation/virtual/kvm/api.txt | 12 ++++++++++--
>  include/uapi/linux/kvm.h          |  5 ++++-
>  2 files changed, 14 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
> index 09efa9e..6551311 100644
> --- a/Documentation/virtual/kvm/api.txt
> +++ b/Documentation/virtual/kvm/api.txt
> @@ -2175,10 +2175,18 @@ struct kvm_msi {
>  	__u32 address_hi;
>  	__u32 data;
>  	__u32 flags;
> -	__u8  pad[16];
> +	__u32 devid;
> +	__u8  pad[12];
>  };
>  
> -No flags are defined so far. The corresponding field must be 0.
> +flags: KVM_MSI_VALID_DEVID: devid contains a valid value
> +devid: If KVM_MSI_VALID_DEVID is set, contains a unique device identifier
> +       for the device that wrote the MSI message.
> +       For PCI, this is usually a BFD identifier in the lower 16 bits.
> +
> +The per-VM KVM_CAP_MSI_DEVID capability advertises the need to provide
> +the device ID. If this capability is not set, userland cannot rely on
> +the kernel to allow the KVM_MSI_VALID_DEVID flag being set.
>  
>  
>  4.71 KVM_CREATE_PIT2
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 05ebf47..7de96f5 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -866,6 +866,7 @@ struct kvm_ppc_smmu_info {
>  #define KVM_CAP_ARM_PMU_V3 126
>  #define KVM_CAP_VCPU_ATTRIBUTES 127
>  #define KVM_CAP_MAX_VCPU_ID 128
> +#define KVM_CAP_MSI_DEVID 129
>  
>  #ifdef KVM_CAP_IRQ_ROUTING
>  
> @@ -1024,12 +1025,14 @@ struct kvm_one_reg {
>  	__u64 addr;
>  };
>  
> +#define KVM_MSI_VALID_DEVID	(1U << 0)
>  struct kvm_msi {
>  	__u32 address_lo;
>  	__u32 address_hi;
>  	__u32 data;
>  	__u32 flags;
> -	__u8  pad[16];
> +	__u32 devid;
> +	__u8  pad[12];
>  };
>  
>  struct kvm_arm_device_addr {
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call
  2016-07-13  1:58 ` [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call Andre Przywara
@ 2016-07-13 10:29   ` Paolo Bonzini
  0 siblings, 0 replies; 41+ messages in thread
From: Paolo Bonzini @ 2016-07-13 10:29 UTC (permalink / raw)
  To: linux-arm-kernel



On 13/07/2016 03:58, Andre Przywara wrote:
> The kvm_io_bus framework is a nice place of holding information about
> various MMIO regions for kernel emulated devices.
> Add a call to retrieve the kvm_io_device structure which is associated
> with a certain MMIO address. This avoids to duplicate kvm_io_bus'
> knowledge of MMIO regions without having to fake MMIO calls if a user
> needs the device a certain MMIO address belongs to.
> This will be used by the ITS emulation to get the associated ITS device
> when someone triggers an MSI via an ioctl from userspace.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Reviewed-by: Eric Auger <eric.auger@redhat.com>
> Acked-by: Christoffer Dall <christoffer.dall@linaro.org>

Acked-by: Paolo Bonzini <pbonzini@redhat.com>

> ---
>  include/linux/kvm_host.h |  2 ++
>  virt/kvm/kvm_main.c      | 24 ++++++++++++++++++++++++
>  2 files changed, 26 insertions(+)
> 
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index 0640ee9..614a981 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -164,6 +164,8 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
>  			    int len, struct kvm_io_device *dev);
>  int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
>  			      struct kvm_io_device *dev);
> +struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
> +					 gpa_t addr);
>  
>  #ifdef CONFIG_KVM_ASYNC_PF
>  struct kvm_async_pf {
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index ef54b4c..bd2eb92 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -3496,6 +3496,30 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
>  	return r;
>  }
>  
> +struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
> +					 gpa_t addr)
> +{
> +	struct kvm_io_bus *bus;
> +	int dev_idx, srcu_idx;
> +	struct kvm_io_device *iodev = NULL;
> +
> +	srcu_idx = srcu_read_lock(&kvm->srcu);
> +
> +	bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
> +
> +	dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1);
> +	if (dev_idx < 0)
> +		goto out_unlock;
> +
> +	iodev = bus->range[dev_idx].dev;
> +
> +out_unlock:
> +	srcu_read_unlock(&kvm->srcu, srcu_idx);
> +
> +	return iodev;
> +}
> +EXPORT_SYMBOL_GPL(kvm_io_bus_get_dev);
> +
>  static struct notifier_block kvm_cpu_notifier = {
>  	.notifier_call = kvm_cpu_hotplug,
>  };
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-07-13  1:58 ` [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
@ 2016-07-13 12:15   ` Marc Zyngier
  2016-07-13 13:50     ` Andre Przywara
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-13 12:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:58, Andre Przywara wrote:
> In the moment our struct vgic_irq's are statically allocated at guest
> creation time. So getting a pointer to an IRQ structure is trivial and
> safe. LPIs are more dynamic, they can be mapped and unmapped at any time
> during the guest's _runtime_.
> In preparation for supporting LPIs we introduce reference counting for
> those structures using the kernel's kref infrastructure.
> Since private IRQs and SPIs are statically allocated, the refcount never
> drops to 0 at the moment, but we increase it when an IRQ gets onto a VCPU
> list and decrease it when it gets removed.
> This introduces vgic_put_irq(), which wraps kref_put and hides the
> release function from the callers.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  include/kvm/arm_vgic.h           |  1 +
>  virt/kvm/arm/vgic/vgic-init.c    |  2 ++
>  virt/kvm/arm/vgic/vgic-mmio-v2.c |  8 ++++++
>  virt/kvm/arm/vgic/vgic-mmio-v3.c | 20 +++++++++------
>  virt/kvm/arm/vgic/vgic-mmio.c    | 25 ++++++++++++++++++-
>  virt/kvm/arm/vgic/vgic-v2.c      |  1 +
>  virt/kvm/arm/vgic/vgic-v3.c      |  1 +
>  virt/kvm/arm/vgic/vgic.c         | 53 ++++++++++++++++++++++++++++++++--------
>  virt/kvm/arm/vgic/vgic.h         |  1 +
>  9 files changed, 94 insertions(+), 18 deletions(-)

[...]

> --- a/virt/kvm/arm/vgic/vgic.c
> +++ b/virt/kvm/arm/vgic/vgic.c
> @@ -48,13 +48,20 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
>  struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  			      u32 intid)
>  {
> -	/* SGIs and PPIs */
> -	if (intid <= VGIC_MAX_PRIVATE)
> -		return &vcpu->arch.vgic_cpu.private_irqs[intid];
> +	struct vgic_dist *dist = &kvm->arch.vgic;
> +	struct vgic_irq *irq;
>  
> -	/* SPIs */
> -	if (intid <= VGIC_MAX_SPI)
> -		return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
> +	if (intid <= VGIC_MAX_PRIVATE) {        /* SGIs and PPIs */
> +		irq = &vcpu->arch.vgic_cpu.private_irqs[intid];
> +		kref_get(&irq->refcount);
> +		return irq;
> +	}
> +
> +	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
> +		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
> +		kref_get(&irq->refcount);
> +		return irq;
> +	}

I'm a bit concerned by the fact that we perform the refcounting on 
objects that shouldn't be concerned by it. None of the static interrupts
should have to suffer from the overhead, as they cannot be freed. 
So I came up with the following patch:

diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index c7cd1a3..6bbff9a 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -91,19 +91,12 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid)
 {
 	struct vgic_dist *dist = &kvm->arch.vgic;
-	struct vgic_irq *irq;
 
-	if (intid <= VGIC_MAX_PRIVATE) {        /* SGIs and PPIs */
-		irq = &vcpu->arch.vgic_cpu.private_irqs[intid];
-		kref_get(&irq->refcount);
-		return irq;
-	}
+	if (intid <= VGIC_MAX_PRIVATE)		/* SGIs and PPIs */
+		return &vcpu->arch.vgic_cpu.private_irqs[intid];
 
-	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
-		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
-		kref_get(&irq->refcount);
-		return irq;
-	}
+	if (intid <= VGIC_MAX_SPI)		/* SPIs */
+		return &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
 
 	if (intid >= VGIC_MIN_LPI)		/* LPIs */
 		return vgic_get_lpi(kvm, intid);
@@ -112,6 +105,14 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 	return NULL;
 }
 
+static void vgic_get_irq_kref(struct vgic_irq *irq)
+{
+	if (irq->intid < VGIC_MIN_LPI)
+		return;
+
+	kref_get(&irq->refcount);
+}
+
 /*
  * We can't do anything in here, because we lack the kvm pointer to
  * lock and remove the item from the lpi_list. So we keep this function
@@ -125,10 +126,10 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
 {
 	struct vgic_dist *dist;
 
-	if (!kref_put(&irq->refcount, vgic_irq_release))
+	if (irq->intid < VGIC_MIN_LPI)
 		return;
 
-	if (irq->intid < VGIC_MIN_LPI)
+	if (!kref_put(&irq->refcount, vgic_irq_release))
 		return;
 
 	dist = &kvm->arch.vgic;
@@ -313,7 +314,11 @@ retry:
 		goto retry;
 	}
 
-	kref_get(&irq->refcount);
+	/*
+	 * Grab a reference to the irq to reflect the fact that it is
+	 * now in the ap_list.
+	 */
+	vgic_get_irq_kref(irq);
 	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
 	irq->vcpu = vcpu;
 
@@ -473,8 +478,11 @@ retry:
 			spin_unlock(&irq->irq_lock);
 
 			/*
-			 * This put matches the get when we added the LPI to
-			 * the ap_list. We now drop the reference from the list.
+			 * This vgic_put_irq call matches the
+			 * vgic_get_irq_kref in vgic_queue_irq_unlock,
+			 * where we added the LPI to the ap_list. As
+			 * we remove the irq from the list, we drop
+			 * also drop the refcount.
 			 */
 			vgic_put_irq(vcpu->kvm, irq);
 			continue;

Thoughts?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions
  2016-07-13  1:58 ` [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions Andre Przywara
@ 2016-07-13 12:28   ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-13 12:28 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:58, Andre Przywara wrote:
> arm-gic-v3.h contains bit and register definitions for the GICv3 and ITS,
> at least for the bits the we currently care about.
> The ITS emulation needs more definitions, so add them and refactor
> the memory attribute #defines to be more universally usable.
> To avoid changing all users, we still provide some of the old definitons
> defined with the help of the new macros.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  include/linux/irqchip/arm-gic-v3.h | 174 ++++++++++++++++++++++++-------------
>  1 file changed, 114 insertions(+), 60 deletions(-)
> 
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index bfbd707..ed61647 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -112,34 +112,58 @@
>  #define GICR_WAKER_ProcessorSleep	(1U << 1)
>  #define GICR_WAKER_ChildrenAsleep	(1U << 2)
>  
> -#define GICR_PROPBASER_NonShareable	(0U << 10)
> -#define GICR_PROPBASER_InnerShareable	(1U << 10)
> -#define GICR_PROPBASER_OuterShareable	(2U << 10)
> -#define GICR_PROPBASER_SHAREABILITY_MASK (3UL << 10)
> -#define GICR_PROPBASER_nCnB		(0U << 7)
> -#define GICR_PROPBASER_nC		(1U << 7)
> -#define GICR_PROPBASER_RaWt		(2U << 7)
> -#define GICR_PROPBASER_RaWb		(3U << 7)
> -#define GICR_PROPBASER_WaWt		(4U << 7)
> -#define GICR_PROPBASER_WaWb		(5U << 7)
> -#define GICR_PROPBASER_RaWaWt		(6U << 7)
> -#define GICR_PROPBASER_RaWaWb		(7U << 7)
> -#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
> -#define GICR_PROPBASER_IDBITS_MASK	(0x1f)
> -
> -#define GICR_PENDBASER_NonShareable	(0U << 10)
> -#define GICR_PENDBASER_InnerShareable	(1U << 10)
> -#define GICR_PENDBASER_OuterShareable	(2U << 10)
> -#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10)
> -#define GICR_PENDBASER_nCnB		(0U << 7)
> -#define GICR_PENDBASER_nC		(1U << 7)
> -#define GICR_PENDBASER_RaWt		(2U << 7)
> -#define GICR_PENDBASER_RaWb		(3U << 7)
> -#define GICR_PENDBASER_WaWt		(4U << 7)
> -#define GICR_PENDBASER_WaWb		(5U << 7)
> -#define GICR_PENDBASER_RaWaWt		(6U << 7)
> -#define GICR_PENDBASER_RaWaWb		(7U << 7)
> -#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7)
> +#define GIC_BASER_CACHE_nCnB		0ULL
> +#define GIC_BASER_CACHE_SameAsInner	0ULL
> +#define GIC_BASER_CACHE_nC		1ULL
> +#define GIC_BASER_CACHE_RaWt		2ULL
> +#define GIC_BASER_CACHE_RaWb		3ULL
> +#define GIC_BASER_CACHE_WaWt		4ULL
> +#define GIC_BASER_CACHE_WaWb		5ULL
> +#define GIC_BASER_CACHE_RaWaWt		6ULL
> +#define GIC_BASER_CACHE_RaWaWb		7ULL
> +#define GIC_BASER_CACHE_MASK		7ULL
> +#define GIC_BASER_NonShareable		0ULL
> +#define GIC_BASER_InnerShareable	1ULL
> +#define GIC_BASER_OuterShareable	2ULL
> +#define GIC_BASER_SHAREABILITY_MASK	3ULL
> +
> +#define GIC_BASER_CACHEABILITY(reg, inner_outer, type)			\
> +	(GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT)
> +
> +#define GIC_BASER_SHAREABILITY(reg, type)				\
> +	(GIC_BASER_##type << reg##_SHAREABILITY_SHIFT)
> +
> +#define GICR_PROPBASER_SHAREABILITY_SHIFT		(10)
> +#define GICR_PROPBASER_INNER_CACHEABILITY_SHIFT		(7)
> +#define GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT		(56)
> +#define GICR_PROPBASER_SHAREABILITY_MASK				\
> +	GIC_BASER_SHAREABILITY(GICR_PROPBASER, SHAREABILITY_MASK)
> +#define GICR_PROPBASER_CACHEABILITY_MASK				\
> +	GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, MASK)
> +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK				\
> +	GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, MASK)

Inner Cacheability : GICR_PROPBASER_CACHEABILITY_MASK
Outer Cacheability : GICR_PROPBASER_OUTER_CACHEABILITY_MASK

Why don't we have a symmetric naming (GICR_PROPBASER_INNER_CACHEABILITY_MASK)?

I'm all for having some backward compatibility, but given
that you're adding a lot of those, you should make sure that
the new stuff is not ambiguous.

This applies throughout the file.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-07-13 12:15   ` Marc Zyngier
@ 2016-07-13 13:50     ` Andre Przywara
  0 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-13 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On 13/07/16 13:15, Marc Zyngier wrote:
> On 13/07/16 02:58, Andre Przywara wrote:
>> In the moment our struct vgic_irq's are statically allocated at guest
>> creation time. So getting a pointer to an IRQ structure is trivial and
>> safe. LPIs are more dynamic, they can be mapped and unmapped at any time
>> during the guest's _runtime_.
>> In preparation for supporting LPIs we introduce reference counting for
>> those structures using the kernel's kref infrastructure.
>> Since private IRQs and SPIs are statically allocated, the refcount never
>> drops to 0 at the moment, but we increase it when an IRQ gets onto a VCPU
>> list and decrease it when it gets removed.
>> This introduces vgic_put_irq(), which wraps kref_put and hides the
>> release function from the callers.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  include/kvm/arm_vgic.h           |  1 +
>>  virt/kvm/arm/vgic/vgic-init.c    |  2 ++
>>  virt/kvm/arm/vgic/vgic-mmio-v2.c |  8 ++++++
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c | 20 +++++++++------
>>  virt/kvm/arm/vgic/vgic-mmio.c    | 25 ++++++++++++++++++-
>>  virt/kvm/arm/vgic/vgic-v2.c      |  1 +
>>  virt/kvm/arm/vgic/vgic-v3.c      |  1 +
>>  virt/kvm/arm/vgic/vgic.c         | 53 ++++++++++++++++++++++++++++++++--------
>>  virt/kvm/arm/vgic/vgic.h         |  1 +
>>  9 files changed, 94 insertions(+), 18 deletions(-)
> 
> [...]
> 
>> --- a/virt/kvm/arm/vgic/vgic.c
>> +++ b/virt/kvm/arm/vgic/vgic.c
>> @@ -48,13 +48,20 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
>>  struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>>  			      u32 intid)
>>  {
>> -	/* SGIs and PPIs */
>> -	if (intid <= VGIC_MAX_PRIVATE)
>> -		return &vcpu->arch.vgic_cpu.private_irqs[intid];
>> +	struct vgic_dist *dist = &kvm->arch.vgic;
>> +	struct vgic_irq *irq;
>>  
>> -	/* SPIs */
>> -	if (intid <= VGIC_MAX_SPI)
>> -		return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
>> +	if (intid <= VGIC_MAX_PRIVATE) {        /* SGIs and PPIs */
>> +		irq = &vcpu->arch.vgic_cpu.private_irqs[intid];
>> +		kref_get(&irq->refcount);
>> +		return irq;
>> +	}
>> +
>> +	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
>> +		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
>> +		kref_get(&irq->refcount);
>> +		return irq;
>> +	}
> 
> I'm a bit concerned by the fact that we perform the refcounting on 
> objects that shouldn't be concerned by it. None of the static interrupts
> should have to suffer from the overhead, as they cannot be freed. 
> So I came up with the following patch:
> 
> diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
> index c7cd1a3..6bbff9a 100644
> --- a/virt/kvm/arm/vgic/vgic.c
> +++ b/virt/kvm/arm/vgic/vgic.c
> @@ -91,19 +91,12 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  			      u32 intid)
>  {
>  	struct vgic_dist *dist = &kvm->arch.vgic;
> -	struct vgic_irq *irq;
>  
> -	if (intid <= VGIC_MAX_PRIVATE) {        /* SGIs and PPIs */
> -		irq = &vcpu->arch.vgic_cpu.private_irqs[intid];
> -		kref_get(&irq->refcount);
> -		return irq;
> -	}
> +	if (intid <= VGIC_MAX_PRIVATE)		/* SGIs and PPIs */
> +		return &vcpu->arch.vgic_cpu.private_irqs[intid];
>  
> -	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
> -		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
> -		kref_get(&irq->refcount);
> -		return irq;
> -	}
> +	if (intid <= VGIC_MAX_SPI)		/* SPIs */
> +		return &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
>  
>  	if (intid >= VGIC_MIN_LPI)		/* LPIs */
>  		return vgic_get_lpi(kvm, intid);
> @@ -112,6 +105,14 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  	return NULL;
>  }
>  
> +static void vgic_get_irq_kref(struct vgic_irq *irq)
> +{
> +	if (irq->intid < VGIC_MIN_LPI)
> +		return;
> +
> +	kref_get(&irq->refcount);
> +}
> +
>  /*
>   * We can't do anything in here, because we lack the kvm pointer to
>   * lock and remove the item from the lpi_list. So we keep this function
> @@ -125,10 +126,10 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
>  {
>  	struct vgic_dist *dist;
>  
> -	if (!kref_put(&irq->refcount, vgic_irq_release))
> +	if (irq->intid < VGIC_MIN_LPI)
>  		return;
>  
> -	if (irq->intid < VGIC_MIN_LPI)
> +	if (!kref_put(&irq->refcount, vgic_irq_release))
>  		return;
>  
>  	dist = &kvm->arch.vgic;
> @@ -313,7 +314,11 @@ retry:
>  		goto retry;
>  	}
>  
> -	kref_get(&irq->refcount);
> +	/*
> +	 * Grab a reference to the irq to reflect the fact that it is
> +	 * now in the ap_list.
> +	 */
> +	vgic_get_irq_kref(irq);
>  	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
>  	irq->vcpu = vcpu;
>  
> @@ -473,8 +478,11 @@ retry:
>  			spin_unlock(&irq->irq_lock);
>  
>  			/*
> -			 * This put matches the get when we added the LPI to
> -			 * the ap_list. We now drop the reference from the list.
> +			 * This vgic_put_irq call matches the
> +			 * vgic_get_irq_kref in vgic_queue_irq_unlock,
> +			 * where we added the LPI to the ap_list. As
> +			 * we remove the irq from the list, we drop
> +			 * also drop the refcount.
>  			 */
>  			vgic_put_irq(vcpu->kvm, irq);
>  			continue;
> 
> Thoughts?

Looks good to me, I checked the code and quickly tested it as well,
without any issues.
I'd integrate this in next respin.

Thanks!
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device
  2016-07-13  1:59 ` [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device Andre Przywara
@ 2016-07-14  8:36   ` Marc Zyngier
  2016-07-14 11:11     ` Andre Przywara
  2016-07-15  9:33     ` Andre Przywara
  0 siblings, 2 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14  8:36 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:59, Andre Przywara wrote:
> 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>
> ---
>  Documentation/virtual/kvm/devices/arm-vgic.txt |  25 +++--
>  arch/arm/kvm/arm.c                             |   1 +
>  arch/arm64/include/uapi/asm/kvm.h              |   2 +
>  include/kvm/arm_vgic.h                         |   3 +
>  include/uapi/linux/kvm.h                       |   2 +
>  virt/kvm/arm/vgic/vgic-its.c                   | 144 ++++++++++++++++++++++++-
>  virt/kvm/arm/vgic/vgic-kvm-device.c            |   7 +-
>  virt/kvm/arm/vgic/vgic-mmio-v3.c               |   2 +-
>  virt/kvm/arm/vgic/vgic.h                       |   8 ++
>  9 files changed, 183 insertions(+), 11 deletions(-)
> 
> diff --git a/Documentation/virtual/kvm/devices/arm-vgic.txt b/Documentation/virtual/kvm/devices/arm-vgic.txt
> index 59541d4..89182f8 100644
> --- a/Documentation/virtual/kvm/devices/arm-vgic.txt
> +++ b/Documentation/virtual/kvm/devices/arm-vgic.txt
> @@ -4,16 +4,22 @@ ARM Virtual Generic Interrupt Controller (VGIC)
>  Device types supported:
>    KVM_DEV_TYPE_ARM_VGIC_V2     ARM Generic Interrupt Controller v2.0
>    KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
> +  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
>  
> -Only one VGIC instance may be instantiated through either this API or the
> -legacy KVM_CREATE_IRQCHIP api.  The created VGIC will act as the VM interrupt
> -controller, requiring emulated user-space devices to inject interrupts to the
> -VGIC instead of directly to CPUs.
> +Only one VGIC instance of the V2/V3 types above may be instantiated through
> +either this API or the legacy KVM_CREATE_IRQCHIP api.  The created VGIC will
> +act as the VM interrupt controller, requiring emulated user-space devices to
> +inject interrupts to the VGIC instead of directly to CPUs.
>  
>  Creating a guest GICv3 device requires a host GICv3 as well.
>  GICv3 implementations with hardware compatibility support allow a guest GICv2
>  as well.
>  
> +Creating a virtual ITS controller requires a host GICv3 (but does not depend
> +on having physical ITS controllers).
> +There can be multiple ITS controllers per guest, each of them has to have
> +a separate, non-overlapping MMIO region.
> +
>  Groups:
>    KVM_DEV_ARM_VGIC_GRP_ADDR
>    Attributes:
> @@ -39,6 +45,13 @@ Groups:
>        Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
>        This address needs to be 64K aligned.
>  
> +    KVM_VGIC_V3_ADDR_TYPE_ITS (rw, 64-bit)
> +      Base address in the guest physical address space of the GICv3 ITS
> +      control register frame. The ITS allows MSI(-X) interrupts to be
> +      injected into guests. This extension is optional. If the kernel
> +      does not support the ITS, the call returns -ENODEV.
> +      Only valid for KVM_DEV_TYPE_ARM_VGIC_ITS.
> +      This address needs to be 64K aligned and the region covers 128K.
>  
>    KVM_DEV_ARM_VGIC_GRP_DIST_REGS
>    Attributes:
> @@ -109,8 +122,8 @@ Groups:
>    KVM_DEV_ARM_VGIC_GRP_CTRL
>    Attributes:
>      KVM_DEV_ARM_VGIC_CTRL_INIT
> -      request the initialization of the VGIC, no additional parameter in
> -      kvm_device_attr.addr.
> +      request the initialization of the VGIC or ITS, no additional parameter
> +      in kvm_device_attr.addr.
>    Errors:
>      -ENXIO: VGIC not properly configured as required prior to calling
>       this attribute
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
> index 557e390..0d5c255 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -20,6 +20,7 @@
>  #include <linux/errno.h>
>  #include <linux/err.h>
>  #include <linux/kvm_host.h>
> +#include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/vmalloc.h>
>  #include <linux/fs.h>
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index f209ea1..3051f86 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -87,9 +87,11 @@ struct kvm_regs {
>  /* Supported VGICv3 address types  */
>  #define KVM_VGIC_V3_ADDR_TYPE_DIST	2
>  #define KVM_VGIC_V3_ADDR_TYPE_REDIST	3
> +#define KVM_VGIC_ITS_ADDR_TYPE		4
>  
>  #define KVM_VGIC_V3_DIST_SIZE		SZ_64K
>  #define KVM_VGIC_V3_REDIST_SIZE		(2 * SZ_64K)
> +#define KVM_VGIC_V3_ITS_SIZE		(2 * SZ_64K)
>  
>  #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
>  #define KVM_ARM_VCPU_EL1_32BIT		1 /* CPU running a 32bit VM */
> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
> index 685f339..8609fac 100644
> --- a/include/kvm/arm_vgic.h
> +++ b/include/kvm/arm_vgic.h
> @@ -134,6 +134,7 @@ struct vgic_its {
>  	gpa_t			vgic_its_base;
>  
>  	bool			enabled;
> +	bool			initialized;
>  	struct vgic_io_device	iodev;
>  };
>  
> @@ -167,6 +168,8 @@ struct vgic_dist {
>  
>  	struct vgic_io_device	dist_iodev;
>  
> +	bool			has_its;
> +
>  	/*
>  	 * Contains the attributes and gpa of the LPI configuration table.
>  	 * Since we report GICR_TYPER.CommonLPIAff as 0b00, we can share
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 7de96f5..d8c4c32 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1077,6 +1077,8 @@ enum kvm_device_type {
>  #define KVM_DEV_TYPE_FLIC		KVM_DEV_TYPE_FLIC
>  	KVM_DEV_TYPE_ARM_VGIC_V3,
>  #define KVM_DEV_TYPE_ARM_VGIC_V3	KVM_DEV_TYPE_ARM_VGIC_V3
> +	KVM_DEV_TYPE_ARM_VGIC_ITS,
> +#define KVM_DEV_TYPE_ARM_VGIC_ITS	KVM_DEV_TYPE_ARM_VGIC_ITS
>  	KVM_DEV_TYPE_MAX,
>  };
>  
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 06ad94d..393ad3a 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -21,6 +21,7 @@
>  #include <linux/kvm.h>
>  #include <linux/kvm_host.h>
>  #include <linux/interrupt.h>
> +#include <linux/uaccess.h>
>  
>  #include <linux/irqchip/arm-gic-v3.h>
>  
> @@ -79,7 +80,7 @@ static struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> -int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
> +static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)

Please move this hunk to the previous patch...

>  {
>  	struct vgic_io_device *iodev = &its->iodev;
>  	int ret;
> @@ -93,8 +94,147 @@ int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>  	iodev->its = its;
>  	mutex_lock(&kvm->slots_lock);
>  	ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, iodev->base_addr,
> -				      SZ_64K, &iodev->dev);
> +				      KVM_VGIC_V3_ITS_SIZE, &iodev->dev);

as well as this one.

>  	mutex_unlock(&kvm->slots_lock);
>  
>  	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);

Shouldn't that be the ITS size (128kB)?

> +		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:
> +			if (its->initialized)
> +				return 0;
> +
> +			if (IS_VGIC_ADDR_UNDEF(its->vgic_its_base))
> +				return -ENXIO;
> +
> +			ret = vgic_its_register(dev->kvm, its);
> +			if (ret)
> +				return ret;
> +
> +			its->initialized = true;
> +			return 0;

Can't you move all of that into vgic_its_register? That'd be a lot cleaner.

> +		}
> +		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;
> +}
> +
> +struct kvm_device_ops kvm_arm_vgic_its_ops = {

Should be static.

> +	.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);
> +}
> diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
> index 2f24f13..1813f93 100644
> --- a/virt/kvm/arm/vgic/vgic-kvm-device.c
> +++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
> @@ -21,8 +21,8 @@
>  
>  /* common helpers */
>  
> -static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
> -			     phys_addr_t addr, phys_addr_t alignment)
> +int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
> +		      phys_addr_t addr, phys_addr_t alignment)
>  {
>  	if (addr & ~KVM_PHYS_MASK)
>  		return -E2BIG;
> @@ -223,6 +223,9 @@ int kvm_register_vgic_device(unsigned long type)
>  	case KVM_DEV_TYPE_ARM_VGIC_V3:
>  		ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
>  					      KVM_DEV_TYPE_ARM_VGIC_V3);
> +		if (ret)
> +			break;
> +		ret = kvm_vgic_register_its_device();
>  		break;
>  #endif
>  	}
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 4169e22..6cb52dd 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)
>  	if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
>  		return false;
>  
> -	return false;
> +	return dist->has_its;
>  }
>  
>  static 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 31807c1..9dc7207 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);
>  bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
>  void vgic_kick_vcpus(struct kvm *kvm);
>  
> +int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
> +		      phys_addr_t addr, phys_addr_t alignment);
> +
>  void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
>  void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
>  void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
> @@ -73,6 +76,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info);
>  int vgic_v3_map_resources(struct kvm *kvm);
>  int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
>  bool vgic_has_its(struct kvm *kvm);
> +int kvm_vgic_register_its_device(void);
>  #else
>  static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
>  {
> @@ -130,6 +134,10 @@ static inline bool vgic_has_its(struct kvm *kvm)
>  	return false;
>  }
>  
> +static inline int kvm_vgic_register_its_device(void)
> +{
> +	return -ENODEV;
> +}
>  #endif
>  
>  int kvm_register_vgic_device(unsigned long type);
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers
  2016-07-13  1:59 ` [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers Andre Przywara
@ 2016-07-14  9:13   ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14  9:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:59, Andre Przywara wrote:
> Add emulation for some basic MMIO registers used in the ITS emulation.
> This includes:
> - GITS_{CTLR,TYPER,IIDR}
> - ID registers
> - GITS_{CBASER,CREADR,CWRITER}
>   (which implement the ITS command buffer handling)
> - GITS_BASER<n>
> 
> Most of the handlers are pretty straight forward, only the CWRITER
> handler is a bit more involved by taking the new its_cmd mutex and
> then iterating over the command buffer.
> The registers holding base addresses and attributes are sanitised before
> storing them.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  include/kvm/arm_vgic.h           |  16 ++
>  virt/kvm/arm/vgic/vgic-its.c     | 394 ++++++++++++++++++++++++++++++++++++++-
>  virt/kvm/arm/vgic/vgic-mmio-v3.c |   8 +-
>  virt/kvm/arm/vgic/vgic-mmio.h    |   6 +
>  virt/kvm/arm/vgic/vgic.c         |  12 +-
>  5 files changed, 419 insertions(+), 17 deletions(-)
> 
> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
> index 8609fac..6186749 100644
> --- a/include/kvm/arm_vgic.h
> +++ b/include/kvm/arm_vgic.h
> @@ -22,6 +22,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/types.h>
>  #include <kvm/iodev.h>
> +#include <linux/list.h>
>  
>  #define VGIC_V3_MAX_CPUS	255
>  #define VGIC_V2_MAX_CPUS	8
> @@ -136,6 +137,21 @@ struct vgic_its {
>  	bool			enabled;
>  	bool			initialized;
>  	struct vgic_io_device	iodev;
> +
> +	/* These registers correspond to GITS_BASER{0,1} */
> +	u64			baser_device_table;
> +	u64			baser_coll_table;
> +
> +	/* Protects the command queue */
> +	struct mutex		cmd_lock;
> +	u64			cbaser;
> +	u32			creadr;
> +	u32			cwriter;
> +
> +	/* Protects the device and collection lists */
> +	struct mutex		its_lock;
> +	struct list_head	device_list;
> +	struct list_head	collection_list;
>  };
>  
>  struct vgic_dist {
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 393ad3a..dc9a98f 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -21,6 +21,7 @@
>  #include <linux/kvm.h>
>  #include <linux/kvm_host.h>
>  #include <linux/interrupt.h>
> +#include <linux/list.h>
>  #include <linux/uaccess.h>
>  
>  #include <linux/irqchip/arm-gic-v3.h>
> @@ -32,6 +33,326 @@
>  #include "vgic.h"
>  #include "vgic-mmio.h"
>  
> +struct its_device {
> +	struct list_head dev_list;
> +
> +	/* the head for the list of ITTEs */
> +	struct list_head itt_head;
> +	u32 device_id;
> +};
> +
> +#define COLLECTION_NOT_MAPPED ((u32)~0)
> +
> +struct its_collection {
> +	struct list_head coll_list;
> +
> +	u32 collection_id;
> +	u32 target_addr;
> +};
> +
> +#define its_is_collection_mapped(coll) ((coll) && \
> +				((coll)->target_addr != COLLECTION_NOT_MAPPED))
> +
> +struct its_itte {
> +	struct list_head itte_list;
> +
> +	struct its_collection *collection;
> +	u32 lpi;
> +	u32 event_id;
> +};
> +
> +#define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))

I thought we had agreed on sticking to 48bit all over the place?

> +
> +static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
> +					     struct vgic_its *its,
> +					     gpa_t addr, unsigned int len)
> +{
> +	u32 reg = 0;
> +
> +	mutex_lock(&its->cmd_lock);
> +	if (its->creadr == its->cwriter)
> +		reg |= GITS_CTLR_QUIESCENT;
> +	if (its->enabled)
> +		reg |= GITS_CTLR_ENABLE;
> +	mutex_unlock(&its->cmd_lock);
> +
> +	return reg;
> +}
> +
> +static void vgic_mmio_write_its_ctlr(struct kvm *kvm, struct vgic_its *its,
> +				     gpa_t addr, unsigned int len,
> +				     unsigned long val)
> +{
> +	its->enabled = !!(val & GITS_CTLR_ENABLE);
> +}
> +
> +static unsigned long vgic_mmio_read_its_typer(struct kvm *kvm,
> +					      struct vgic_its *its,
> +					      gpa_t addr, unsigned int len)
> +{
> +	u64 reg = GITS_TYPER_PLPIS;
> +
> +	/*
> +	 * We use linear CPU numbers for redistributor addressing,
> +	 * so GITS_TYPER.PTA is 0.
> +	 * Also we force all PROPBASER registers to be the same, so
> +	 * CommonLPIAff is 0 as well.
> +	 * To avoid memory waste in the guest, we keep the number of IDBits and
> +	 * DevBits low - as least for the time being.
> +	 */
> +	reg |= 0x0f << GITS_TYPER_DEVBITS_SHIFT;
> +	reg |= 0x0f << GITS_TYPER_IDBITS_SHIFT;
> +
> +	return extract_bytes(reg, addr & 7, len);
> +}
> +
> +static unsigned long vgic_mmio_read_its_iidr(struct kvm *kvm,
> +					     struct vgic_its *its,
> +					     gpa_t addr, unsigned int len)
> +{
> +	return (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
> +}
> +
> +static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
> +					       struct vgic_its *its,
> +					       gpa_t addr, unsigned int len)
> +{
> +	switch (addr & 0xffff) {
> +	case GITS_PIDR0:
> +		return 0x92;	/* part number, bits[7:0] */
> +	case GITS_PIDR1:
> +		return 0xb4;	/* part number, bits[11:8] */
> +	case GITS_PIDR2:
> +		return GIC_PIDR2_ARCH_GICv3 | 0x0b;
> +	case GITS_PIDR4:
> +		return 0x40;	/* This is a 64K software visible page */
> +	/* The following are the ID registers for (any) GIC. */
> +	case GITS_CIDR0:
> +		return 0x0d;
> +	case GITS_CIDR1:
> +		return 0xf0;
> +	case GITS_CIDR2:
> +		return 0x05;
> +	case GITS_CIDR3:
> +		return 0xb1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Requires the its_lock to be held. */
> +static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
> +{
> +	list_del(&itte->itte_list);
> +	kfree(itte);
> +}
> +
> +static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
> +				   u64 *its_cmd)
> +{
> +	return -ENODEV;
> +}
> +
> +static u64 vgic_sanitise_its_baser(u64 reg)
> +{
> +	reg = vgic_sanitise_field(reg, GITS_BASER_SHAREABILITY_MASK,
> +				  GITS_BASER_SHAREABILITY_SHIFT,
> +				  vgic_sanitise_shareability);
> +	reg = vgic_sanitise_field(reg, GITS_BASER_CACHEABILITY_MASK,
> +				  GITS_BASER_INNER_CACHEABILITY_SHIFT,
> +				  vgic_sanitise_inner_cacheability);
> +	reg = vgic_sanitise_field(reg, GITS_BASER_OUTER_CACHEABILITY_MASK,
> +				  GITS_BASER_OUTER_CACHEABILITY_SHIFT,
> +				  vgic_sanitise_outer_cacheability);
> +
> +	/* When using 64K pages, we clear bits to limit the PA to 48 bits. */
> +	if ((reg & GITS_BASER_PAGE_SIZE_MASK) == GITS_BASER_PAGE_SIZE_64K)

Why that check? We were supposed to only support 64kB pages so that we
could simplify the code. The page size field should be RO, and set to 64k.

> +		reg &= ~GENMASK_ULL(15, 12);
> +
> +	return reg;
> +}
> +
> +static u64 vgic_sanitise_its_cbaser(u64 reg)
> +{
> +	reg = vgic_sanitise_field(reg, GITS_CBASER_SHAREABILITY_MASK,
> +				  GITS_CBASER_SHAREABILITY_SHIFT,
> +				  vgic_sanitise_shareability);
> +	reg = vgic_sanitise_field(reg, GITS_CBASER_CACHEABILITY_MASK,
> +				  GITS_CBASER_INNER_CACHEABILITY_SHIFT,
> +				  vgic_sanitise_inner_cacheability);
> +	reg = vgic_sanitise_field(reg, GITS_CBASER_OUTER_CACHEABILITY_MASK,
> +				  GITS_CBASER_OUTER_CACHEABILITY_SHIFT,
> +				  vgic_sanitise_outer_cacheability);
> +
> +	/*
> +	 * Sanitise the physical address to be 64k aligned (for 4K and 16K
> +	 * pages), also limit the physical addresses to 48 bits.

Please drop the remark about 4k and 16k pages, this is irrelevant here.

> +	 */
> +	reg &= ~(GENMASK_ULL(51, 48) | GENMASK_ULL(15, 12));
> +
> +	return reg;
> +}
> +
> +static unsigned long vgic_mmio_read_its_cbaser(struct kvm *kvm,
> +					       struct vgic_its *its,
> +					       gpa_t addr, unsigned int len)
> +{
> +	return extract_bytes(its->cbaser, addr & 7, len);
> +}
> +
> +static void vgic_mmio_write_its_cbaser(struct kvm *kvm, struct vgic_its *its,
> +				       gpa_t addr, unsigned int len,
> +				       unsigned long val)
> +{
> +	/* When GITS_CTLR.Enable is 1, this register is RO. */
> +	if (its->enabled)
> +		return;
> +
> +	mutex_lock(&its->cmd_lock);
> +	its->cbaser = update_64bit_reg(its->cbaser, addr & 7, len, val);
> +	its->cbaser = vgic_sanitise_its_cbaser(its->cbaser);
> +	its->creadr = 0;
> +	/*
> +	 * CWRITER is architecturally UNKNOWN on reset, but we need to reset
> +	 * it to CREADR to make sure we start with an empty command buffer.
> +	 */
> +	its->cwriter = its->creadr;
> +	mutex_unlock(&its->cmd_lock);
> +}
> +
> +#define ITS_CMD_BUFFER_SIZE(baser)	((((baser) & 0xff) + 1) << 12)
> +#define ITS_CMD_SIZE			32
> +
> +/*
> + * By writing to CWRITER the guest announces new commands to be processed.
> + * To avoid any races in the first place, we take the its_cmd lock, which
> + * protects our ring buffer variables, so that there is only one user
> + * per ITS handling commands at a given time.
> + */
> +static void vgic_mmio_write_its_cwriter(struct kvm *kvm, struct vgic_its *its,
> +					gpa_t addr, unsigned int len,
> +					unsigned long val)
> +{
> +	gpa_t cbaser;
> +	u64 cmd_buf[4];
> +	u32 reg;
> +
> +	if (!its)
> +		return;
> +
> +	cbaser = CBASER_ADDRESS(its->cbaser);
> +
> +	reg = update_64bit_reg(its->cwriter & 0xfffe0, addr & 7, len, val);
> +	reg &= 0xfffe0;

Why not use GENMASK to define the valid bits like we did everywhere
else? Also, do you really need this mask in the call to update_64bit_reg?

> +	if (reg > ITS_CMD_BUFFER_SIZE(its->cbaser))
> +		return;
> +
> +	mutex_lock(&its->cmd_lock);
> +
> +	its->cwriter = reg;
> +
> +	while (its->cwriter != its->creadr) {
> +		int ret = kvm_read_guest(kvm, cbaser + its->creadr,
> +					 cmd_buf, ITS_CMD_SIZE);
> +		/*
> +		 * If kvm_read_guest() fails, this could be due to the guest
> +		 * programming a bogus value in CBASER or something else going
> +		 * wrong from which we cannot easily recover.
> +		 * We just ignore that command then.

It would be good to mention that this is consistent with the handling of
command errors as described in section 6.3.2 of the architecture
specification.

> +		 */
> +		if (!ret)
> +			vgic_its_handle_command(kvm, its, cmd_buf);
> +
> +		its->creadr += ITS_CMD_SIZE;
> +		if (its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser))
> +			its->creadr = 0;
> +	}
> +
> +	mutex_unlock(&its->cmd_lock);
> +}
> +
> +static unsigned long vgic_mmio_read_its_cwriter(struct kvm *kvm,
> +						struct vgic_its *its,
> +						gpa_t addr, unsigned int len)
> +{
> +	return extract_bytes(its->cwriter & 0xfffe0, addr & 0x7, len);
> +}
> +
> +static unsigned long vgic_mmio_read_its_creadr(struct kvm *kvm,
> +					       struct vgic_its *its,
> +					       gpa_t addr, unsigned int len)
> +{
> +	return extract_bytes(its->creadr & 0xfffe0, addr & 0x7, len);
> +}
> +
> +#define BASER_INDEX(addr) (((addr) / sizeof(u64)) & 0x7)
> +static unsigned long vgic_mmio_read_its_baser(struct kvm *kvm,
> +					      struct vgic_its *its,
> +					      gpa_t addr, unsigned int len)
> +{
> +	u64 reg;
> +
> +	switch (BASER_INDEX(addr)) {
> +	case 0:
> +		reg = its->baser_device_table;
> +		break;
> +	case 1:
> +		reg = its->baser_coll_table;
> +		break;
> +	default:
> +		reg = 0;
> +		break;
> +	}
> +
> +	return extract_bytes(reg, addr & 7, len);
> +}
> +
> +#define GITS_BASER_RO_MASK	(GENMASK_ULL(52, 48) | GENMASK_ULL(58, 56))
> +static void vgic_mmio_write_its_baser(struct kvm *kvm,
> +				      struct vgic_its *its,
> +				      gpa_t addr, unsigned int len,
> +				      unsigned long val)
> +{
> +	u64 entry_size, device_type;
> +	u64 reg, *regptr, clearbits = 0;
> +
> +	/* When GITS_CTLR.Enable is 1, we ignore write accesses. */
> +	if (its->enabled)
> +		return;
> +
> +	switch (BASER_INDEX(addr)) {
> +	case 0:
> +		regptr = &its->baser_device_table;
> +		entry_size = 8;
> +		device_type = GITS_BASER_TYPE_DEVICE;
> +		break;
> +	case 1:
> +		regptr = &its->baser_coll_table;
> +		entry_size = 8;
> +		device_type = GITS_BASER_TYPE_COLLECTION;
> +		clearbits = GITS_BASER_INDIRECT;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	reg = update_64bit_reg(*regptr, addr & 7, len, val);
> +	reg &= ~GITS_BASER_RO_MASK;
> +	reg &= ~clearbits;
> +
> +	/*
> +	 * When the guest wants to use 64K pages, bits 15:12 contain bits 51:48
> +	 * of the PA, which we don't support. Clear them to emulate RAZ/WI.
> +	 */
> +	if (reg & GITS_BASER_PAGE_SIZE_64K)
> +		reg &= ~GENMASK_ULL(15, 12);

Again, this is not what I thought we had agreed on. What I'd like to see
is an ITS that *only* supports 64kB pages, and nothing else. It
simplifies the code, and is consistent with the rest of vgic where we
choose a configuration.

Please drop all support for 4k and 16k pages, and make the page size
field RO.

> +	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +	reg |= device_type << GITS_BASER_TYPE_SHIFT;
> +	reg = vgic_sanitise_its_baser(reg);
> +
> +	*regptr = reg;
> +}
> +
>  #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
>  {								\
>  	.reg_offset = off,					\
> @@ -41,8 +362,8 @@
>  	.its_write = wr,					\
>  }
>  
> -static unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its,
> -				       gpa_t addr, unsigned int len)
> +unsigned long its_mmio_read_raz(struct kvm *kvm, struct vgic_its *its,
> +				gpa_t addr, unsigned int len)
>  {
>  	return 0;
>  }
> @@ -55,28 +376,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
>  
>  static struct vgic_register_region its_registers[] = {
>  	REGISTER_ITS_DESC(GITS_CTLR,
> -		its_mmio_read_raz, its_mmio_write_wi, 4,
> +		vgic_mmio_read_its_ctlr, vgic_mmio_write_its_ctlr, 4,
>  		VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_IIDR,
> -		its_mmio_read_raz, its_mmio_write_wi, 4,
> +		vgic_mmio_read_its_iidr, its_mmio_write_wi, 4,
>  		VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_TYPER,
> -		its_mmio_read_raz, its_mmio_write_wi, 8,
> +		vgic_mmio_read_its_typer, its_mmio_write_wi, 8,
>  		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_CBASER,
> -		its_mmio_read_raz, its_mmio_write_wi, 8,
> +		vgic_mmio_read_its_cbaser, vgic_mmio_write_its_cbaser, 8,
>  		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_CWRITER,
> -		its_mmio_read_raz, its_mmio_write_wi, 8,
> +		vgic_mmio_read_its_cwriter, vgic_mmio_write_its_cwriter, 8,
>  		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_CREADR,
> -		its_mmio_read_raz, its_mmio_write_wi, 8,
> +		vgic_mmio_read_its_creadr, its_mmio_write_wi, 8,
>  		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_BASER,
> -		its_mmio_read_raz, its_mmio_write_wi, 0x40,
> +		vgic_mmio_read_its_baser, vgic_mmio_write_its_baser, 0x40,
>  		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
>  	REGISTER_ITS_DESC(GITS_IDREGS_BASE,
> -		its_mmio_read_raz, its_mmio_write_wi, 0x30,
> +		vgic_mmio_read_its_idregs, its_mmio_write_wi, 0x30,
>  		VGIC_ACCESS_32bit),
>  };
>  
> @@ -100,6 +421,18 @@ static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>  	return ret;
>  }
>  
> +#define INITIAL_BASER_VALUE						  \
> +	(GIC_BASER_CACHEABILITY(GITS_BASER, INNER, RaWb)		| \
> +	 GIC_BASER_CACHEABILITY(GITS_BASER, OUTER, SameAsInner)		| \
> +	 GIC_BASER_SHAREABILITY(GITS_BASER, InnerShareable)		| \
> +	 ((8ULL - 1) << GITS_BASER_ENTRY_SIZE_SHIFT)			| \
> +	 GITS_BASER_PAGE_SIZE_64K)
> +
> +#define INITIAL_PROPBASER_VALUE						  \
> +	(GIC_BASER_CACHEABILITY(GICR_PROPBASER, INNER, RaWb)		| \
> +	 GIC_BASER_CACHEABILITY(GICR_PROPBASER, OUTER, SameAsInner)	| \
> +	 GIC_BASER_SHAREABILITY(GICR_PROPBASER, InnerShareable))
> +
>  static int vgic_its_create(struct kvm_device *dev, u32 type)
>  {
>  	struct vgic_its *its;
> @@ -111,12 +444,24 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
>  	if (!its)
>  		return -ENOMEM;
>  
> +	mutex_init(&its->its_lock);
> +	mutex_init(&its->cmd_lock);
> +
>  	its->vgic_its_base = VGIC_ADDR_UNDEF;
>  
> +	INIT_LIST_HEAD(&its->device_list);
> +	INIT_LIST_HEAD(&its->collection_list);
> +
>  	dev->kvm->arch.vgic.has_its = true;
>  	its->initialized = false;
>  	its->enabled = false;
>  
> +	its->baser_device_table = INITIAL_BASER_VALUE			|
> +		((u64)GITS_BASER_TYPE_DEVICE << GITS_BASER_TYPE_SHIFT);
> +	its->baser_coll_table = INITIAL_BASER_VALUE |
> +		((u64)GITS_BASER_TYPE_COLLECTION << GITS_BASER_TYPE_SHIFT);
> +	dev->kvm->arch.vgic.propbaser = INITIAL_PROPBASER_VALUE;
> +
>  	dev->private = its;
>  
>  	return 0;
> @@ -124,7 +469,36 @@ static int vgic_its_create(struct kvm_device *dev, u32 type)
>  
>  static void vgic_its_destroy(struct kvm_device *kvm_dev)
>  {
> +	struct kvm *kvm = kvm_dev->kvm;
>  	struct vgic_its *its = kvm_dev->private;
> +	struct its_device *dev;
> +	struct its_itte *itte;
> +	struct list_head *dev_cur, *dev_temp;
> +	struct list_head *cur, *temp;
> +
> +	/*
> +	 * We may end up here without the lists ever having been initialized.
> +	 * Check this and bail out early to avoid dereferencing a NULL pointer.
> +	 */
> +	if (!its->device_list.next)
> +		return;
> +
> +	mutex_lock(&its->its_lock);
> +	list_for_each_safe(dev_cur, dev_temp, &its->device_list) {
> +		dev = container_of(dev_cur, struct its_device, dev_list);
> +		list_for_each_safe(cur, temp, &dev->itt_head) {
> +			itte = (container_of(cur, struct its_itte, itte_list));
> +			its_free_itte(kvm, itte);
> +		}
> +		list_del(dev_cur);
> +		kfree(dev);
> +	}
> +
> +	list_for_each_safe(cur, temp, &its->collection_list) {
> +		list_del(cur);
> +		kfree(container_of(cur, struct its_collection, coll_list));
> +	}
> +	mutex_unlock(&its->its_lock);
>  
>  	kfree(its);
>  }
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 6cb52dd..d98e009 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> @@ -23,15 +23,15 @@
>  #include "vgic-mmio.h"
>  
>  /* extract @num bytes at @offset bytes offset in data */
> -static unsigned long extract_bytes(unsigned long data, unsigned int offset,
> -				   unsigned int num)
> +unsigned long extract_bytes(unsigned long data, unsigned int offset,
> +			    unsigned int num)
>  {
>  	return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
>  }
>  
>  /* allows updates of any half of a 64-bit register (or the whole thing) */
> -static u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
> -			    unsigned long val)
> +u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
> +		     unsigned long val)
>  {
>  	int lower = (offset & 4) * 8;
>  	int upper = lower + 8 * len - 1;
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
> index 366d663..0b3ecf9 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.h
> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
> @@ -96,6 +96,12 @@ unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len);
>  void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
>  				unsigned long data);
>  
> +unsigned long extract_bytes(unsigned long data, unsigned int offset,
> +			    unsigned int num);
> +
> +u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
> +		     unsigned long val);
> +
>  unsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu,
>  				 gpa_t addr, unsigned int len);
>  
> diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
> index 1fb2693..d7ea5df 100644
> --- a/virt/kvm/arm/vgic/vgic.c
> +++ b/virt/kvm/arm/vgic/vgic.c
> @@ -33,10 +33,16 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
>  
>  /*
>   * Locking order is always:
> - *   vgic_cpu->ap_list_lock
> - *     vgic_irq->irq_lock
> + * its->cmd_lock (mutex)
> + *   its->its_lock (mutex)
> + *     vgic_cpu->ap_list_lock
> + *       vgic_irq->irq_lock
>   *
> - * (that is, always take the ap_list_lock before the struct vgic_irq lock).
> + * If you need to take multiple locks, always take the upper lock first,
> + * then the lower ones, e.g. first take the its_lock, then the irq_lock.
> + * If you are already holding a lock and need to take a higher one, you
> + * have to drop the lower ranking lock first and re-aquire it after having
> + * taken the upper one.
>   *
>   * When taking more than one ap_list_lock at the same time, always take the
>   * lowest numbered VCPU's ap_list_lock first, so:
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 13/17] KVM: arm64: read initial LPI pending table
  2016-07-13  1:59 ` [PATCH v9 13/17] KVM: arm64: read initial LPI pending table Andre Przywara
@ 2016-07-14  9:34   ` Marc Zyngier
  2016-07-14 10:16     ` Andre Przywara
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14  9:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:59, Andre Przywara wrote:
> The LPI pending status for a GICv3 redistributor is held in a table
> in (guest) memory. To achieve reasonable performance, we cache the
> pending bit in our struct vgic_irq. The initial pending state must be
> read from guest memory upon enabling LPIs for this redistributor.
> As we can't access the guest memory while we hold the lpi_list spinlock,
> we create a snapshot of the LPI list and iterate over that.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  virt/kvm/arm/vgic/vgic-its.c | 91 ++++++++++++++++++++++++++++++++++++++++++++
>  virt/kvm/arm/vgic/vgic.h     |  6 +++
>  2 files changed, 97 insertions(+)
> 
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 4fc830c..f400ef1 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -63,6 +63,90 @@ struct its_itte {
>  };
>  
>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
> +#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
> +
> +/*
> + * Create a snapshot of the current LPI list, so that we can enumerate all
> + * LPIs without holding any lock.
> + * Returns the array length and puts the kmalloc'ed array into intid_ptr.
> + */
> +static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
> +{
> +	struct vgic_dist *dist = &kvm->arch.vgic;
> +	struct vgic_irq *irq;
> +	u32 *intids;
> +	int irq_count = dist->lpi_list_count, i = 0;
> +
> +	/*
> +	 * We use the current value of the list length, which may change
> +	 * after the kmalloc. We don't care, because the guest shouldn't
> +	 * change anything while the command handling is still running,
> +	 * and in the worst case we would miss a new IRQ, which one wouldn't
> +	 * expect to be covered by this command anyway.
> +	 */
> +	intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL);
> +	if (!intids)
> +		return -ENOMEM;
> +
> +	spin_lock(&dist->lpi_list_lock);
> +	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
> +		/* We don't need to "get" the IRQ, as we hold the list lock. */
> +		intids[i] = irq->intid;
> +		if (i++ == irq_count)
> +			break;

So I can perfectly rewrite this as:

		if (i == irq_count)
			break;
		i++;

Do you now see the bug and how you are performing out of bound accesses?

> +	}
> +	spin_unlock(&dist->lpi_list_lock);
> +
> +	*intid_ptr = intids;
> +	return irq_count;
> +}
> +
> +/*
> + * Scan the whole LPI pending table and sync the pending bit in there
> + * with our own data structures. This relies on the LPI being
> + * mapped before.
> + */
> +static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
> +{
> +	gpa_t pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
> +	struct vgic_irq *irq;
> +	u8 pendmask;
> +	int ret = 0;
> +	u32 *intids;
> +	int nr_irqs, i;
> +
> +	nr_irqs = vgic_copy_lpi_list(vcpu->kvm, &intids);
> +	if (nr_irqs < 0)
> +		return nr_irqs;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		int byte_offset, bit_nr;
> +		int last_byte_offset = -1;

Nice try. But by keeping the last_byte_offset inside the loop, you've
made sure that it is reset to -1 on each iteration. Wall <- head.

> +
> +		byte_offset = intids[i] / BITS_PER_BYTE;
> +		bit_nr = intids[i] % BITS_PER_BYTE;
> +
> +		if (byte_offset != last_byte_offset) {
> +			ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
> +					     &pendmask, 1);
> +			if (ret) {
> +				kfree(intids);
> +				return ret;
> +			}
> +			last_byte_offset = byte_offset;
> +		}
> +
> +		irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
> +		spin_lock(&irq->irq_lock);
> +		irq->pending = pendmask & (1U << bit_nr);
> +		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_put_irq(vcpu->kvm, irq);
> +	}
> +
> +	kfree(intids);
> +
> +	return ret;
> +}
>  
>  static unsigned long vgic_mmio_read_its_ctlr(struct kvm *vcpu,
>  					     struct vgic_its *its,
> @@ -406,6 +490,13 @@ static struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> +/* This is called on setting the LPI enable bit in the redistributor. */
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +	if (!(vcpu->arch.vgic_cpu.pendbaser & GICR_PENDBASER_PTZ))
> +		its_sync_lpi_pending_table(vcpu);
> +}
> +
>  static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>  {
>  	struct vgic_io_device *iodev = &its->iodev;
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 9dc7207..32d8d82 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -25,6 +25,7 @@
>  #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)
>  
>  #define INTERRUPT_ID_BITS_SPIS	10
> +#define INTERRUPT_ID_BITS_ITS	16
>  #define VGIC_PRI_BITS		5
>  
>  #define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
> @@ -77,6 +78,7 @@ int vgic_v3_map_resources(struct kvm *kvm);
>  int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
>  bool vgic_has_its(struct kvm *kvm);
>  int kvm_vgic_register_its_device(void);
> +void vgic_enable_lpis(struct kvm_vcpu *vcpu);
>  #else
>  static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
>  {
> @@ -138,6 +140,10 @@ static inline int kvm_vgic_register_its_device(void)
>  {
>  	return -ENODEV;
>  }
> +
> +static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
> +{
> +}
>  #endif
>  
>  int kvm_register_vgic_device(unsigned long type);
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table
  2016-07-13  1:59 ` [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table Andre Przywara
@ 2016-07-14  9:46   ` Marc Zyngier
  2016-07-14 10:00     ` Andre Przywara
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14  9:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:59, Andre Przywara wrote:
> The (system-wide) LPI configuration table is held in a table in
> (guest) memory. To achieve reasonable performance, we cache this data
> in our struct vgic_irq. If the guest updates the configuration data
> (which consists of the enable bit and the priority value), it issues
> an INV or INVALL command to allow us to update our information.
> Provide functions that update that information for one LPI or all LPIs
> mapped to a specific collection.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  virt/kvm/arm/vgic/vgic-its.c | 39 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 39 insertions(+)
> 
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index f400ef1..60108f8 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -64,6 +64,45 @@ struct its_itte {
>  
>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>  #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
> +#define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))

Again, these masks are wrong as we limit the ITS to 48 bits PA.

> +
> +#define GIC_LPI_OFFSET 8192
> +
> +#define LPI_PROP_ENABLE_BIT(p)	((p) & LPI_PROP_ENABLED)
> +#define LPI_PROP_PRIORITY(p)	((p) & 0xfc)
> +
> +/*
> + * Reads the configuration data for a given LPI from guest memory and
> + * updates the fields in struct vgic_irq.
> + * If filter_vcpu is not NULL, applies only if the IRQ is targeting this
> + * VCPU. Unconditionally applies if filter_vcpu is NULL.
> + */
> +static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
> +			     struct kvm_vcpu *filter_vcpu)
> +{
> +	u64 propbase = PROPBASER_ADDRESS(kvm->arch.vgic.propbaser);
> +	u8 prop;
> +	int ret;
> +
> +	ret = kvm_read_guest(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
> +			     &prop, 1);
> +
> +	if (ret)
> +		return ret;
> +
> +	spin_lock(&irq->irq_lock);
> +
> +	if (!filter_vcpu || filter_vcpu == irq->target_vcpu) {
> +		irq->priority = LPI_PROP_PRIORITY(prop);
> +		irq->enabled = LPI_PROP_ENABLE_BIT(prop);
> +
> +		vgic_queue_irq_unlock(kvm, irq);
> +	} else {
> +		spin_unlock(&irq->irq_lock);
> +	}
> +
> +	return 0;
> +}
>  
>  /*
>   * Create a snapshot of the current LPI list, so that we can enumerate all
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table
  2016-07-14  9:46   ` Marc Zyngier
@ 2016-07-14 10:00     ` Andre Przywara
  2016-07-14 10:10       ` Marc Zyngier
  0 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-14 10:00 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 14/07/16 10:46, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:
>> The (system-wide) LPI configuration table is held in a table in
>> (guest) memory. To achieve reasonable performance, we cache this data
>> in our struct vgic_irq. If the guest updates the configuration data
>> (which consists of the enable bit and the priority value), it issues
>> an INV or INVALL command to allow us to update our information.
>> Provide functions that update that information for one LPI or all LPIs
>> mapped to a specific collection.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  virt/kvm/arm/vgic/vgic-its.c | 39 +++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 39 insertions(+)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index f400ef1..60108f8 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -64,6 +64,45 @@ struct its_itte {
>>  
>>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>>  #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
>> +#define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
> 
> Again, these masks are wrong as we limit the ITS to 48 bits PA.

Those masks match the architecture description of the register. We limit
to 48 bits of PA when the guest writes to those registers. I'd like to
keep this limitation in one place only.

Cheers,
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table
  2016-07-14 10:00     ` Andre Przywara
@ 2016-07-14 10:10       ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/07/16 11:00, Andre Przywara wrote:
> Hi,
> 
> On 14/07/16 10:46, Marc Zyngier wrote:
>> On 13/07/16 02:59, Andre Przywara wrote:
>>> The (system-wide) LPI configuration table is held in a table in
>>> (guest) memory. To achieve reasonable performance, we cache this data
>>> in our struct vgic_irq. If the guest updates the configuration data
>>> (which consists of the enable bit and the priority value), it issues
>>> an INV or INVALL command to allow us to update our information.
>>> Provide functions that update that information for one LPI or all LPIs
>>> mapped to a specific collection.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>> ---
>>>  virt/kvm/arm/vgic/vgic-its.c | 39 +++++++++++++++++++++++++++++++++++++++
>>>  1 file changed, 39 insertions(+)
>>>
>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>> index f400ef1..60108f8 100644
>>> --- a/virt/kvm/arm/vgic/vgic-its.c
>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>>> @@ -64,6 +64,45 @@ struct its_itte {
>>>  
>>>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>>>  #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
>>> +#define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>>
>> Again, these masks are wrong as we limit the ITS to 48 bits PA.
> 
> Those masks match the architecture description of the register. We limit
> to 48 bits of PA when the guest writes to those registers. I'd like to
> keep this limitation in one place only.

Which makes it hard to understand for everyone. If you want something
truly generic, assign proper names to these defines:

#define CBASER_ARCHITECTURAL_PA_BITS	52
#define CBASER_IMPLEMENTATION_PA_BITS	48

and repaint all your defines to use macros along those lines. If not,
just change everything to 48 and let's be done with it.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 13/17] KVM: arm64: read initial LPI pending table
  2016-07-14  9:34   ` Marc Zyngier
@ 2016-07-14 10:16     ` Andre Przywara
  0 siblings, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-14 10:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 14/07/16 10:34, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:
>> The LPI pending status for a GICv3 redistributor is held in a table
>> in (guest) memory. To achieve reasonable performance, we cache the
>> pending bit in our struct vgic_irq. The initial pending state must be
>> read from guest memory upon enabling LPIs for this redistributor.
>> As we can't access the guest memory while we hold the lpi_list spinlock,
>> we create a snapshot of the LPI list and iterate over that.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  virt/kvm/arm/vgic/vgic-its.c | 91 ++++++++++++++++++++++++++++++++++++++++++++
>>  virt/kvm/arm/vgic/vgic.h     |  6 +++
>>  2 files changed, 97 insertions(+)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 4fc830c..f400ef1 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -63,6 +63,90 @@ struct its_itte {
>>  };
>>  
>>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>> +#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
>> +
>> +/*
>> + * Create a snapshot of the current LPI list, so that we can enumerate all
>> + * LPIs without holding any lock.
>> + * Returns the array length and puts the kmalloc'ed array into intid_ptr.
>> + */
>> +static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
>> +{
>> +	struct vgic_dist *dist = &kvm->arch.vgic;
>> +	struct vgic_irq *irq;
>> +	u32 *intids;
>> +	int irq_count = dist->lpi_list_count, i = 0;
>> +
>> +	/*
>> +	 * We use the current value of the list length, which may change
>> +	 * after the kmalloc. We don't care, because the guest shouldn't
>> +	 * change anything while the command handling is still running,
>> +	 * and in the worst case we would miss a new IRQ, which one wouldn't
>> +	 * expect to be covered by this command anyway.
>> +	 */
>> +	intids = kmalloc_array(irq_count, sizeof(intids[0]), GFP_KERNEL);
>> +	if (!intids)
>> +		return -ENOMEM;
>> +
>> +	spin_lock(&dist->lpi_list_lock);
>> +	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
>> +		/* We don't need to "get" the IRQ, as we hold the list lock. */
>> +		intids[i] = irq->intid;
>> +		if (i++ == irq_count)
>> +			break;
> 
> So I can perfectly rewrite this as:
> 
> 		if (i == irq_count)
> 			break;
> 		i++;
> 
> Do you now see the bug and how you are performing out of bound accesses?

Ouch!

> 
>> +	}
>> +	spin_unlock(&dist->lpi_list_lock);
>> +
>> +	*intid_ptr = intids;
>> +	return irq_count;
>> +}
>> +
>> +/*
>> + * Scan the whole LPI pending table and sync the pending bit in there
>> + * with our own data structures. This relies on the LPI being
>> + * mapped before.
>> + */
>> +static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
>> +{
>> +	gpa_t pendbase = PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
>> +	struct vgic_irq *irq;
>> +	u8 pendmask;
>> +	int ret = 0;
>> +	u32 *intids;
>> +	int nr_irqs, i;
>> +
>> +	nr_irqs = vgic_copy_lpi_list(vcpu->kvm, &intids);
>> +	if (nr_irqs < 0)
>> +		return nr_irqs;
>> +
>> +	for (i = 0; i < nr_irqs; i++) {
>> +		int byte_offset, bit_nr;
>> +		int last_byte_offset = -1;
> 
> Nice try. But by keeping the last_byte_offset inside the loop, you've
> made sure that it is reset to -1 on each iteration. Wall <- head.

Ouch again.

Thanks for catching those! Fixed.

Cheers,
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers
  2016-07-13  1:59 ` [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers Andre Przywara
@ 2016-07-14 10:38   ` Marc Zyngier
  2016-07-14 15:35     ` Andre Przywara
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14 10:38 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:59, Andre Przywara wrote:
> The connection between a device, an event ID, the LPI number and the
> associated CPU is stored in in-memory tables in a GICv3, but their
> format is not specified by the spec. Instead software uses a command
> queue in a ring buffer to let an ITS implementation use its own
> format.
> Implement handlers for the various ITS commands and let them store
> the requested relation into our own data structures. Those data
> structures are protected by the its_lock mutex.
> Our internal ring buffer read and write pointers are protected by the
> its_cmd mutex, so that only one VCPU per ITS can handle commands at
> any given time.
> Error handling is very basic at the moment, as we don't have a good
> way of communicating errors to the guest (usually an SError).
> The INT command handler is missing from this patch, as we gain the
> capability of actually injecting MSIs into the guest only later on.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  virt/kvm/arm/vgic/vgic-its.c | 599 ++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 598 insertions(+), 1 deletion(-)
> 
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 60108f8..28abfcd 100644
> --- a/virt/kvm/arm/vgic/vgic-its.c
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -33,6 +33,67 @@
>  #include "vgic.h"
>  #include "vgic-mmio.h"
>  
> +/*
> + * Creates a new (reference to a) struct vgic_irq for a given LPI.
> + * If this LPI is already mapped on another ITS, we increase its refcount
> + * and return a pointer to the existing structure.
> + * If this is a "new" LPI, we allocate and initialize a new struct vgic_irq.
> + * This function returns a pointer to the _unlocked_ structure.
> + */
> +static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid)
> +{
> +	struct vgic_dist *dist = &kvm->arch.vgic;
> +	struct vgic_irq *irq = vgic_get_irq(kvm, NULL, intid), *oldirq;
> +
> +	/* In this case there is no put, since we keep the reference. */
> +	if (irq)
> +		return irq;
> +
> +	irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL);
> +	if (!irq)
> +		return NULL;
> +
> +	INIT_LIST_HEAD(&irq->lpi_list);
> +	INIT_LIST_HEAD(&irq->ap_list);
> +	spin_lock_init(&irq->irq_lock);
> +
> +	irq->config = VGIC_CONFIG_EDGE;
> +	kref_init(&irq->refcount);
> +	irq->intid = intid;
> +
> +	spin_lock(&dist->lpi_list_lock);
> +
> +	/*
> +	 * There could be a race with another vgic_add_lpi(), so we need to
> +	 * check that we don't add a second list entry with the same LPI.
> +	 */
> +	list_for_each_entry(oldirq, &dist->lpi_list_head, lpi_list) {
> +		if (oldirq->intid != intid)
> +			continue;
> +
> +		/* Someone was faster with adding this LPI, lets use that. */
> +		kfree(irq);
> +		irq = oldirq;
> +
> +		/*
> +		 * This increases the refcount, the caller is expected to
> +		 * call vgic_put_irq() on the returned pointer once it's
> +		 * finished with the IRQ.
> +		 */
> +		kref_get(&irq->refcount);
> +
> +		goto out_unlock;
> +	}
> +
> +	list_add_tail(&irq->lpi_list, &dist->lpi_list_head);
> +	dist->lpi_list_count++;
> +
> +out_unlock:
> +	spin_unlock(&dist->lpi_list_lock);
> +
> +	return irq;
> +}
> +
>  struct its_device {
>  	struct list_head dev_list;
>  
> @@ -62,12 +123,70 @@ struct its_itte {
>  	u32 event_id;
>  };
>  
> +/*
> + * Find and returns a device in the device table for an ITS.
> + * Must be called with the its_lock mutex held.
> + */
> +static struct its_device *find_its_device(struct vgic_its *its, u32 device_id)
> +{
> +	struct its_device *device;
> +
> +	list_for_each_entry(device, &its->device_list, dev_list)
> +		if (device_id == device->device_id)
> +			return device;
> +
> +	return NULL;
> +}
> +
> +/*
> + * Find and returns an interrupt translation table entry (ITTE) for a given
> + * Device ID/Event ID pair on an ITS.
> + * Must be called with the its_lock mutex held.
> + */
> +static struct its_itte *find_itte(struct vgic_its *its, u32 device_id,
> +				  u32 event_id)
> +{
> +	struct its_device *device;
> +	struct its_itte *itte;
> +
> +	device = find_its_device(its, device_id);
> +	if (device == NULL)
> +		return NULL;
> +
> +	list_for_each_entry(itte, &device->itt_head, itte_list)
> +		if (itte->event_id == event_id)
> +			return itte;
> +
> +	return NULL;
> +}
> +
> +/* To be used as an iterator this macro misses the enclosing parentheses */
> +#define for_each_lpi_its(dev, itte, its) \
> +	list_for_each_entry(dev, &(its)->device_list, dev_list) \
> +		list_for_each_entry(itte, &(dev)->itt_head, itte_list)
> +
>  #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>  #define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
>  #define PROPBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
>  
>  #define GIC_LPI_OFFSET 8192
>  
> +/*
> + * Finds and returns a collection in the ITS collection table.
> + * Must be called with the its_lock mutex held.
> + */
> +static struct its_collection *find_collection(struct vgic_its *its, int coll_id)
> +{
> +	struct its_collection *collection;
> +
> +	list_for_each_entry(collection, &its->collection_list, coll_list) {
> +		if (coll_id == collection->collection_id)
> +			return collection;
> +	}
> +
> +	return NULL;
> +}
> +
>  #define LPI_PROP_ENABLE_BIT(p)	((p) & LPI_PROP_ENABLED)
>  #define LPI_PROP_PRIORITY(p)	((p) & 0xfc)
>  
> @@ -141,6 +260,48 @@ static int vgic_copy_lpi_list(struct kvm *kvm, u32 **intid_ptr)
>  }
>  
>  /*
> + * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
> + * is targeting) to the VGIC's view, which deals with target VCPUs.
> + * Needs to be called whenever either the collection for a LPIs has
> + * changed or the collection itself got retargeted.
> + */
> +static void update_affinity_itte(struct kvm *kvm, struct its_itte *itte)
> +{
> +	struct kvm_vcpu *vcpu;
> +
> +	vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);

What happens if the collection hasn't been mapped yet? It is probably
worth checking before blindly assigning a NULL pointer, which would
corrupt the state set by another ITS.

> +
> +	spin_lock(&itte->irq->irq_lock);
> +	itte->irq->target_vcpu = vcpu;
> +	spin_unlock(&itte->irq->irq_lock);
> +}
> +
> +/*
> + * Updates the target VCPU for every LPI targeting this collection.
> + * Must be called with the its_lock mutex held.
> + */
> +static void update_affinity_collection(struct kvm *kvm, struct vgic_its *its,
> +				       struct its_collection *coll)
> +{
> +	struct its_device *device;
> +	struct its_itte *itte;
> +
> +	for_each_lpi_its(device, itte, its) {
> +		if (!itte->collection || coll != itte->collection)
> +			continue;
> +
> +		update_affinity_itte(kvm, itte);
> +	}
> +}
> +
> +static u32 max_lpis_propbaser(u64 propbaser)
> +{
> +	int nr_idbits = (propbaser & 0x1f) + 1;
> +
> +	return 1U << min(nr_idbits, INTERRUPT_ID_BITS_ITS);
> +}
> +
> +/*
>   * Scan the whole LPI pending table and sync the pending bit in there
>   * with our own data structures. This relies on the LPI being
>   * mapped before.
> @@ -275,10 +436,446 @@ static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
>  	kfree(itte);
>  }
>  
> +static u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size)
> +{
> +	return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1);
> +}
> +
> +#define its_cmd_get_command(cmd)	its_cmd_mask_field(cmd, 0,  0,  8)
> +#define its_cmd_get_deviceid(cmd)	its_cmd_mask_field(cmd, 0, 32, 32)
> +#define its_cmd_get_id(cmd)		its_cmd_mask_field(cmd, 1,  0, 32)
> +#define its_cmd_get_physical_id(cmd)	its_cmd_mask_field(cmd, 1, 32, 32)
> +#define its_cmd_get_collection(cmd)	its_cmd_mask_field(cmd, 2,  0, 16)
> +#define its_cmd_get_target_addr(cmd)	its_cmd_mask_field(cmd, 2, 16, 32)
> +#define its_cmd_get_validbit(cmd)	its_cmd_mask_field(cmd, 2, 63,  1)
> +
> +/*
> + * The DISCARD command frees an Interrupt Translation Table Entry (ITTE).
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
> +				       u64 *its_cmd)
> +{
> +	u32 device_id;
> +	u32 event_id;
> +	struct its_itte *itte;
> +
> +	device_id = its_cmd_get_deviceid(its_cmd);
> +	event_id = its_cmd_get_id(its_cmd);
> +
> +	itte = find_itte(its, device_id, event_id);
> +	if (itte && itte->collection) {
> +		/*
> +		 * Though the spec talks about removing the pending state, we
> +		 * don't bother here since we clear the ITTE anyway and the
> +		 * pending state is a property of the ITTE struct.
> +		 */
> +		its_free_itte(kvm, itte);
> +		return 0;
> +	}
> +
> +	return E_ITS_DISCARD_UNMAPPED_INTERRUPT;
> +}
> +
> +/*
> + * The MOVI command moves an ITTE to a different collection.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
> +				    u64 *its_cmd)
> +{
> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
> +	u32 event_id = its_cmd_get_id(its_cmd);
> +	u32 coll_id = its_cmd_get_collection(its_cmd);
> +	struct kvm_vcpu *vcpu;
> +	struct its_itte *itte;
> +	struct its_collection *collection;
> +
> +	itte = find_itte(its, device_id, event_id);
> +	if (!itte)
> +		return E_ITS_MOVI_UNMAPPED_INTERRUPT;
> +
> +	if (!its_is_collection_mapped(itte->collection))
> +		return E_ITS_MOVI_UNMAPPED_COLLECTION;
> +
> +	collection = find_collection(its, coll_id);
> +	if (!its_is_collection_mapped(collection))
> +		return E_ITS_MOVI_UNMAPPED_COLLECTION;
> +
> +	itte->collection = collection;
> +	vcpu = kvm_get_vcpu(kvm, collection->target_addr);
> +
> +	spin_lock(&itte->irq->irq_lock);
> +	itte->irq->target_vcpu = vcpu;
> +	spin_unlock(&itte->irq->irq_lock);
> +
> +	return 0;
> +}
> +
> +static void vgic_its_init_collection(struct vgic_its *its,
> +				     struct its_collection *collection,
> +				     u32 coll_id)
> +{
> +	collection->collection_id = coll_id;
> +	collection->target_addr = COLLECTION_NOT_MAPPED;
> +
> +	list_add_tail(&collection->coll_list, &its->collection_list);
> +}
> +
> +/*
> + * The MAPTI and MAPI commands map LPIs to ITTEs.
> + * Must be called with its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
> +				    u64 *its_cmd, u8 subcmd)
> +{
> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
> +	u32 event_id = its_cmd_get_id(its_cmd);
> +	u32 coll_id = its_cmd_get_collection(its_cmd);
> +	struct its_itte *itte;
> +	struct its_device *device;
> +	struct its_collection *collection, *new_coll = NULL;
> +	int lpi_nr;
> +
> +	device = find_its_device(its, device_id);
> +	if (!device)
> +		return E_ITS_MAPTI_UNMAPPED_DEVICE;
> +
> +	collection = find_collection(its, coll_id);

Don't you need to check the range of the collection ID, and whether it
would fit in the collection table?

> +	if (!collection) {
> +		new_coll = kzalloc(sizeof(struct its_collection), GFP_KERNEL);
> +		if (!new_coll)
> +			return -ENOMEM;
> +	}
> +
> +	if (subcmd == GITS_CMD_MAPTI)
> +		lpi_nr = its_cmd_get_physical_id(its_cmd);
> +	else
> +		lpi_nr = event_id;
> +	if (lpi_nr < GIC_LPI_OFFSET ||
> +	    lpi_nr >= max_lpis_propbaser(kvm->arch.vgic.propbaser)) {
> +		kfree(new_coll);
> +		return E_ITS_MAPTI_PHYSICALID_OOR;
> +	}
> +
> +	itte = find_itte(its, device_id, event_id);
> +	if (!itte) {
> +		itte = kzalloc(sizeof(struct its_itte), GFP_KERNEL);
> +		if (!itte) {
> +			kfree(new_coll);
> +			return -ENOMEM;
> +		}
> +
> +		itte->event_id	= event_id;
> +		list_add_tail(&itte->itte_list, &device->itt_head);
> +	}
> +
> +	if (!collection) {
> +		collection = new_coll;
> +		vgic_its_init_collection(its, collection, coll_id);
> +	}
> +
> +	itte->collection = collection;
> +	itte->lpi = lpi_nr;
> +	itte->irq = vgic_add_lpi(kvm, lpi_nr);
> +	update_affinity_itte(kvm, itte);
> +
> +	/*
> +	 * We "cache" the configuration table entries in out struct vgic_irq's.
> +	 * However we only have those structs for mapped IRQs, so we read in
> +	 * the respective config data from memory here upon mapping the LPI.
> +	 */
> +	update_lpi_config(kvm, itte->irq, NULL);
> +
> +	return 0;
> +}
> +
> +/* Requires the its_lock to be held. */
> +static void vgic_its_unmap_device(struct kvm *kvm, struct its_device *device)
> +{
> +	struct its_itte *itte, *temp;
> +
> +	/*
> +	 * The spec says that unmapping a device with still valid
> +	 * ITTEs associated is UNPREDICTABLE. We remove all ITTEs,
> +	 * since we cannot leave the memory unreferenced.
> +	 */
> +	list_for_each_entry_safe(itte, temp, &device->itt_head, itte_list)
> +		its_free_itte(kvm, itte);
> +
> +	list_del(&device->dev_list);
> +	kfree(device);
> +}
> +
> +/*
> + * MAPD maps or unmaps a device ID to Interrupt Translation Tables (ITTs).
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_mapd(struct kvm *kvm, struct vgic_its *its,
> +				    u64 *its_cmd)
> +{
> +	bool valid = its_cmd_get_validbit(its_cmd);
> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
> +	struct its_device *device;
> +
> +	device = find_its_device(its, device_id);
> +	if (device)
> +		vgic_its_unmap_device(kvm, device);

Maybe add a comment as to *why* it is legal to do so.

> +
> +	/*
> +	 * The spec does not say whether unmapping a not-mapped device
> +	 * is an error, so we are done in any case.
> +	 */
> +	if (!valid)
> +		return 0;
> +
> +	device = kzalloc(sizeof(struct its_device), GFP_KERNEL);
> +	if (!device)
> +		return -ENOMEM;
> +
> +	device->device_id = device_id;
> +	INIT_LIST_HEAD(&device->itt_head);
> +
> +	list_add_tail(&device->dev_list, &its->device_list);
> +
> +	return 0;
> +}
> +
> +/*
> + * The MAPC command maps collection IDs to redistributors.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_mapc(struct kvm *kvm, struct vgic_its *its,
> +				    u64 *its_cmd)
> +{
> +	u16 coll_id;
> +	u32 target_addr;
> +	struct its_collection *collection;
> +	bool valid;
> +
> +	valid = its_cmd_get_validbit(its_cmd);
> +	coll_id = its_cmd_get_collection(its_cmd);
> +	target_addr = its_cmd_get_target_addr(its_cmd);
> +
> +	if (target_addr >= atomic_read(&kvm->online_vcpus))
> +		return E_ITS_MAPC_PROCNUM_OOR;
> +
> +	collection = find_collection(its, coll_id);
> +
> +	if (!valid) {
> +		struct its_device *device;
> +		struct its_itte *itte;
> +		/*
> +		 * Clearing the mapping for that collection ID removes the
> +		 * entry from the list. If there wasn't any before, we can
> +		 * go home early.
> +		 */
> +		if (!collection)
> +			return 0;
> +
> +		for_each_lpi_its(device, itte, its)
> +			if (itte->collection &&
> +			    itte->collection->collection_id == coll_id)
> +				itte->collection = NULL;
> +
> +		list_del(&collection->coll_list);
> +		kfree(collection);
> +	} else {
> +		if (!collection) {
> +			collection = kzalloc(sizeof(struct its_collection),
> +					     GFP_KERNEL);
> +			if (!collection)
> +				return -ENOMEM;
> +
> +			vgic_its_init_collection(its, collection, coll_id);
> +			collection->target_addr = target_addr;
> +		} else {
> +			collection->target_addr = target_addr;
> +			update_affinity_collection(kvm, its, collection);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * The CLEAR command removes the pending state for a particular LPI.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its,
> +				     u64 *its_cmd)
> +{
> +	u32 device_id;
> +	u32 event_id;
> +	struct its_itte *itte;
> +
> +	device_id = its_cmd_get_deviceid(its_cmd);
> +	event_id = its_cmd_get_id(its_cmd);
> +
> +	itte = find_itte(its, device_id, event_id);
> +	if (!itte)
> +		return E_ITS_CLEAR_UNMAPPED_INTERRUPT;
> +
> +	itte->irq->pending = false;
> +
> +	return 0;
> +}
> +
> +/*
> + * The INV command syncs the configuration bits from the memory table.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
> +				   u64 *its_cmd)
> +{
> +	u32 device_id;
> +	u32 event_id;
> +	struct its_itte *itte;
> +
> +	device_id = its_cmd_get_deviceid(its_cmd);
> +	event_id = its_cmd_get_id(its_cmd);
> +
> +	itte = find_itte(its, device_id, event_id);
> +	if (!itte)
> +		return E_ITS_INV_UNMAPPED_INTERRUPT;
> +
> +	return update_lpi_config(kvm, itte->irq, NULL);
> +}
> +
> +/*
> + * The INVALL command requests flushing of all IRQ data in this collection.
> + * Find the VCPU mapped to that collection, then iterate over the VM's list
> + * of mapped LPIs and update the configuration for each IRQ which targets
> + * the specified vcpu. The configuration will be read from the in-memory
> + * configuration table.
> + * Must be called with the its_lock mutex held.
> + */
> +static int vgic_its_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
> +				      u64 *its_cmd)
> +{
> +	u32 coll_id = its_cmd_get_collection(its_cmd);
> +	struct its_collection *collection;
> +	struct kvm_vcpu *vcpu;
> +	struct vgic_irq *irq;
> +	u32 *intids;
> +	int irq_count, i;
> +
> +	collection = find_collection(its, coll_id);
> +	if (!its_is_collection_mapped(collection))
> +		return E_ITS_INVALL_UNMAPPED_COLLECTION;
> +
> +	vcpu = kvm_get_vcpu(kvm, collection->target_addr);
> +
> +	irq_count = vgic_copy_lpi_list(kvm, &intids);
> +	if (irq_count < 0)
> +		return irq_count;
> +
> +	for (i = 0; i < irq_count; i++) {
> +		irq = vgic_get_irq(kvm, NULL, intids[i]);
> +		if (!irq)
> +			continue;
> +		update_lpi_config(kvm, irq, vcpu);
> +		vgic_put_irq(kvm, irq);
> +	}
> +
> +	kfree(intids);
> +
> +	return 0;
> +}
> +
> +/*
> + * The MOVALL command moves the pending state of all IRQs targeting one
> + * redistributor to another. We don't hold the pending state in the VCPUs,
> + * but in the IRQs instead, so there is really not much to do for us here.
> + * However the spec says that no IRQ must target the old redistributor
> + * afterwards, so we make sure that no LPI is using the associated target_vcpu.
> + * This command affects all LPIs in the system.

Or rather all LPIs targetting the "old" redistributor.

> + */
> +static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
> +				      u64 *its_cmd)
> +{
> +	struct vgic_dist *dist = &kvm->arch.vgic;
> +	u32 target1_addr = its_cmd_get_target_addr(its_cmd);
> +	u32 target2_addr = its_cmd_mask_field(its_cmd, 3, 16, 32);
> +	struct kvm_vcpu *vcpu1, *vcpu2;
> +	struct vgic_irq *irq;
> +
> +	if (target1_addr >= atomic_read(&kvm->online_vcpus) ||
> +	    target2_addr >= atomic_read(&kvm->online_vcpus))
> +		return E_ITS_MOVALL_PROCNUM_OOR;
> +
> +	if (target1_addr == target2_addr)
> +		return 0;
> +
> +	vcpu1 = kvm_get_vcpu(kvm, target1_addr);
> +	vcpu2 = kvm_get_vcpu(kvm, target2_addr);
> +
> +	spin_lock(&dist->lpi_list_lock);
> +
> +	list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
> +		spin_lock(&irq->irq_lock);
> +
> +		if (irq->target_vcpu == vcpu1)
> +			irq->target_vcpu = vcpu2;
> +
> +		spin_unlock(&irq->irq_lock);
> +	}
> +
> +	spin_unlock(&dist->lpi_list_lock);
> +
> +	return 0;
> +}
> +
> +/*
> + * This function is called with the its_cmd lock held, but the ITS data
> + * structure lock dropped.
> + */
>  static int vgic_its_handle_command(struct kvm *kvm, struct vgic_its *its,
>  				   u64 *its_cmd)
>  {
> -	return -ENODEV;
> +	u8 cmd = its_cmd_get_command(its_cmd);
> +	int ret = -ENODEV;
> +
> +	mutex_lock(&its->its_lock);
> +	switch (cmd) {
> +	case GITS_CMD_MAPD:
> +		ret = vgic_its_cmd_handle_mapd(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_MAPC:
> +		ret = vgic_its_cmd_handle_mapc(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_MAPI:
> +		ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd, cmd);
> +		break;
> +	case GITS_CMD_MAPTI:
> +		ret = vgic_its_cmd_handle_mapi(kvm, its, its_cmd, cmd);
> +		break;
> +	case GITS_CMD_MOVI:
> +		ret = vgic_its_cmd_handle_movi(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_DISCARD:
> +		ret = vgic_its_cmd_handle_discard(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_CLEAR:
> +		ret = vgic_its_cmd_handle_clear(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_MOVALL:
> +		ret = vgic_its_cmd_handle_movall(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_INV:
> +		ret = vgic_its_cmd_handle_inv(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_INVALL:
> +		ret = vgic_its_cmd_handle_invall(kvm, its, its_cmd);
> +		break;
> +	case GITS_CMD_SYNC:
> +		/* we ignore this command: we are in sync all of the time */
> +		ret = 0;
> +		break;
> +	}
> +	mutex_unlock(&its->its_lock);
> +
> +	return ret;
>  }
>  
>  static u64 vgic_sanitise_its_baser(u64 reg)
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation
  2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
                   ` (17 preceding siblings ...)
  2016-07-13  7:57 ` [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Diana Madalina Craciun
@ 2016-07-14 10:52 ` Marc Zyngier
  2016-07-14 11:01   ` Paolo Bonzini
  18 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14 10:52 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/07/16 02:58, Andre Przywara wrote:
> Hi,
> 
> this series allows those KVM guests that use an emulated GICv3 to use LPIs
> as well, though in the moment this is limited to emulated PCI devices.
> This is based on kvmarm/queue, which now only features the new VGIC
> implementation.
> 
> Another round of changes after another round of review: some bugs fixed,
> some reworks in various places. The register sanitisation has been changed
> to be more readable and to add some missing masking.
> The MMIO dispatching has been simplified (region type no longer in each
> register description). Also the its_lock is now taken already in the
> command dispatching function instead of each command handler, which
> removes quite some "goto out_unlock;" and return value variables.
> Also it turned out we can get rid of the special vgic_put_irq_locked()
> variant. The changelog below holds a more detailed list.
> 
> You can find all of this code (and the prerequisites) in the
> its-emul/v9 branch of my repository [1].
> This has been briefly tested on the model and on GICv3 hardware, with
> lockdep debugging enabled. If you have GICv3 capable hardware, please
> test it on your setup.
> Also of course any review comments are very welcome!

I'm done with that round. Couple of annoying bugs, and still a bunch of
loose ends to be tied. Timing wise, this is getting pretty tight for
4.8, so unless you address the comments very quickly, it will be a miss
again.

My plan is to cut the 4.8 branch at the end of this week, so you have
until then to repost something that I can stick into -next.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation
  2016-07-14 10:52 ` Marc Zyngier
@ 2016-07-14 11:01   ` Paolo Bonzini
  0 siblings, 0 replies; 41+ messages in thread
From: Paolo Bonzini @ 2016-07-14 11:01 UTC (permalink / raw)
  To: linux-arm-kernel



On 14/07/2016 12:52, Marc Zyngier wrote:
> I'm done with that round. Couple of annoying bugs, and still a bunch of
> loose ends to be tied. Timing wise, this is getting pretty tight for
> 4.8, so unless you address the comments very quickly, it will be a miss
> again.
> 
> My plan is to cut the 4.8 branch at the end of this week, so you have
> until then to repost something that I can stick into -next.

FYI I'm on vacation on July 24-31, and will send the pull request to
Linus around Aug 2.  If necessary you can delay things by one more week
and send a pull request to Radim around the end of July after the merge
window has officially started already.

Paolo

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device
  2016-07-14  8:36   ` Marc Zyngier
@ 2016-07-14 11:11     ` Andre Przywara
  2016-07-14 12:57       ` Marc Zyngier
  2016-07-15  9:33     ` Andre Przywara
  1 sibling, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-14 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 14/07/16 09:36, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:
>> 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>
>> ---
>>  Documentation/virtual/kvm/devices/arm-vgic.txt |  25 +++--
>>  arch/arm/kvm/arm.c                             |   1 +
>>  arch/arm64/include/uapi/asm/kvm.h              |   2 +
>>  include/kvm/arm_vgic.h                         |   3 +
>>  include/uapi/linux/kvm.h                       |   2 +
>>  virt/kvm/arm/vgic/vgic-its.c                   | 144 ++++++++++++++++++++++++-
>>  virt/kvm/arm/vgic/vgic-kvm-device.c            |   7 +-
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c               |   2 +-
>>  virt/kvm/arm/vgic/vgic.h                       |   8 ++
>>  9 files changed, 183 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/virtual/kvm/devices/arm-vgic.txt b/Documentation/virtual/kvm/devices/arm-vgic.txt
>> index 59541d4..89182f8 100644
>> --- a/Documentation/virtual/kvm/devices/arm-vgic.txt
>> +++ b/Documentation/virtual/kvm/devices/arm-vgic.txt
>> @@ -4,16 +4,22 @@ ARM Virtual Generic Interrupt Controller (VGIC)
>>  Device types supported:
>>    KVM_DEV_TYPE_ARM_VGIC_V2     ARM Generic Interrupt Controller v2.0
>>    KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
>> +  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
>>  
>> -Only one VGIC instance may be instantiated through either this API or the
>> -legacy KVM_CREATE_IRQCHIP api.  The created VGIC will act as the VM interrupt
>> -controller, requiring emulated user-space devices to inject interrupts to the
>> -VGIC instead of directly to CPUs.
>> +Only one VGIC instance of the V2/V3 types above may be instantiated through
>> +either this API or the legacy KVM_CREATE_IRQCHIP api.  The created VGIC will
>> +act as the VM interrupt controller, requiring emulated user-space devices to
>> +inject interrupts to the VGIC instead of directly to CPUs.
>>  
>>  Creating a guest GICv3 device requires a host GICv3 as well.
>>  GICv3 implementations with hardware compatibility support allow a guest GICv2
>>  as well.
>>  
>> +Creating a virtual ITS controller requires a host GICv3 (but does not depend
>> +on having physical ITS controllers).
>> +There can be multiple ITS controllers per guest, each of them has to have
>> +a separate, non-overlapping MMIO region.
>> +
>>  Groups:
>>    KVM_DEV_ARM_VGIC_GRP_ADDR
>>    Attributes:
>> @@ -39,6 +45,13 @@ Groups:
>>        Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
>>        This address needs to be 64K aligned.
>>  
>> +    KVM_VGIC_V3_ADDR_TYPE_ITS (rw, 64-bit)
>> +      Base address in the guest physical address space of the GICv3 ITS
>> +      control register frame. The ITS allows MSI(-X) interrupts to be
>> +      injected into guests. This extension is optional. If the kernel
>> +      does not support the ITS, the call returns -ENODEV.
>> +      Only valid for KVM_DEV_TYPE_ARM_VGIC_ITS.
>> +      This address needs to be 64K aligned and the region covers 128K.
>>  
>>    KVM_DEV_ARM_VGIC_GRP_DIST_REGS
>>    Attributes:
>> @@ -109,8 +122,8 @@ Groups:
>>    KVM_DEV_ARM_VGIC_GRP_CTRL
>>    Attributes:
>>      KVM_DEV_ARM_VGIC_CTRL_INIT
>> -      request the initialization of the VGIC, no additional parameter in
>> -      kvm_device_attr.addr.
>> +      request the initialization of the VGIC or ITS, no additional parameter
>> +      in kvm_device_attr.addr.
>>    Errors:
>>      -ENXIO: VGIC not properly configured as required prior to calling
>>       this attribute
>> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
>> index 557e390..0d5c255 100644
>> --- a/arch/arm/kvm/arm.c
>> +++ b/arch/arm/kvm/arm.c
>> @@ -20,6 +20,7 @@
>>  #include <linux/errno.h>
>>  #include <linux/err.h>
>>  #include <linux/kvm_host.h>
>> +#include <linux/list.h>
>>  #include <linux/module.h>
>>  #include <linux/vmalloc.h>
>>  #include <linux/fs.h>
>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
>> index f209ea1..3051f86 100644
>> --- a/arch/arm64/include/uapi/asm/kvm.h
>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>> @@ -87,9 +87,11 @@ struct kvm_regs {
>>  /* Supported VGICv3 address types  */
>>  #define KVM_VGIC_V3_ADDR_TYPE_DIST	2
>>  #define KVM_VGIC_V3_ADDR_TYPE_REDIST	3
>> +#define KVM_VGIC_ITS_ADDR_TYPE		4
>>  
>>  #define KVM_VGIC_V3_DIST_SIZE		SZ_64K
>>  #define KVM_VGIC_V3_REDIST_SIZE		(2 * SZ_64K)
>> +#define KVM_VGIC_V3_ITS_SIZE		(2 * SZ_64K)
>>  
>>  #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
>>  #define KVM_ARM_VCPU_EL1_32BIT		1 /* CPU running a 32bit VM */
>> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
>> index 685f339..8609fac 100644
>> --- a/include/kvm/arm_vgic.h
>> +++ b/include/kvm/arm_vgic.h
>> @@ -134,6 +134,7 @@ struct vgic_its {
>>  	gpa_t			vgic_its_base;
>>  
>>  	bool			enabled;
>> +	bool			initialized;
>>  	struct vgic_io_device	iodev;
>>  };
>>  
>> @@ -167,6 +168,8 @@ struct vgic_dist {
>>  
>>  	struct vgic_io_device	dist_iodev;
>>  
>> +	bool			has_its;
>> +
>>  	/*
>>  	 * Contains the attributes and gpa of the LPI configuration table.
>>  	 * Since we report GICR_TYPER.CommonLPIAff as 0b00, we can share
>> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
>> index 7de96f5..d8c4c32 100644
>> --- a/include/uapi/linux/kvm.h
>> +++ b/include/uapi/linux/kvm.h
>> @@ -1077,6 +1077,8 @@ enum kvm_device_type {
>>  #define KVM_DEV_TYPE_FLIC		KVM_DEV_TYPE_FLIC
>>  	KVM_DEV_TYPE_ARM_VGIC_V3,
>>  #define KVM_DEV_TYPE_ARM_VGIC_V3	KVM_DEV_TYPE_ARM_VGIC_V3
>> +	KVM_DEV_TYPE_ARM_VGIC_ITS,
>> +#define KVM_DEV_TYPE_ARM_VGIC_ITS	KVM_DEV_TYPE_ARM_VGIC_ITS
>>  	KVM_DEV_TYPE_MAX,
>>  };
>>  
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 06ad94d..393ad3a 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -21,6 +21,7 @@
>>  #include <linux/kvm.h>
>>  #include <linux/kvm_host.h>
>>  #include <linux/interrupt.h>
>> +#include <linux/uaccess.h>
>>  
>>  #include <linux/irqchip/arm-gic-v3.h>
>>  
>> @@ -79,7 +80,7 @@ static struct vgic_register_region its_registers[] = {
>>  		VGIC_ACCESS_32bit),
>>  };
>>  
>> -int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>> +static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
> 
> Please move this hunk to the previous patch...

Doing this provokes a compilation warning about an unused function in
the previous patch then.
I tried forth and back to fix this (and a couple of other occasions),
but it was either hideous, invoked rewriting stuff needlessly, do a
massive reordering or squashing some patches together, all of which does
not help review.
So this was the least problematic solution I could come up with.

I just looked at deferring the addition of vgic-its.c to the Makefile
into the very last patch and get rid of all those lines then.
This seems to require only very small changes.

Would that be an acceptable solution?

Cheers,
Andre.

P.S. Will fix all of the other comments.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device
  2016-07-14 11:11     ` Andre Przywara
@ 2016-07-14 12:57       ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14 12:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/07/16 12:11, Andre Przywara wrote:
> Hi,
> 
> On 14/07/16 09:36, Marc Zyngier wrote:
>> On 13/07/16 02:59, Andre Przywara wrote:
>>> 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>
>>> ---
>>>  Documentation/virtual/kvm/devices/arm-vgic.txt |  25 +++--
>>>  arch/arm/kvm/arm.c                             |   1 +
>>>  arch/arm64/include/uapi/asm/kvm.h              |   2 +
>>>  include/kvm/arm_vgic.h                         |   3 +
>>>  include/uapi/linux/kvm.h                       |   2 +
>>>  virt/kvm/arm/vgic/vgic-its.c                   | 144 ++++++++++++++++++++++++-
>>>  virt/kvm/arm/vgic/vgic-kvm-device.c            |   7 +-
>>>  virt/kvm/arm/vgic/vgic-mmio-v3.c               |   2 +-
>>>  virt/kvm/arm/vgic/vgic.h                       |   8 ++
>>>  9 files changed, 183 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/Documentation/virtual/kvm/devices/arm-vgic.txt b/Documentation/virtual/kvm/devices/arm-vgic.txt
>>> index 59541d4..89182f8 100644
>>> --- a/Documentation/virtual/kvm/devices/arm-vgic.txt
>>> +++ b/Documentation/virtual/kvm/devices/arm-vgic.txt
>>> @@ -4,16 +4,22 @@ ARM Virtual Generic Interrupt Controller (VGIC)
>>>  Device types supported:
>>>    KVM_DEV_TYPE_ARM_VGIC_V2     ARM Generic Interrupt Controller v2.0
>>>    KVM_DEV_TYPE_ARM_VGIC_V3     ARM Generic Interrupt Controller v3.0
>>> +  KVM_DEV_TYPE_ARM_VGIC_ITS    ARM Interrupt Translation Service Controller
>>>  
>>> -Only one VGIC instance may be instantiated through either this API or the
>>> -legacy KVM_CREATE_IRQCHIP api.  The created VGIC will act as the VM interrupt
>>> -controller, requiring emulated user-space devices to inject interrupts to the
>>> -VGIC instead of directly to CPUs.
>>> +Only one VGIC instance of the V2/V3 types above may be instantiated through
>>> +either this API or the legacy KVM_CREATE_IRQCHIP api.  The created VGIC will
>>> +act as the VM interrupt controller, requiring emulated user-space devices to
>>> +inject interrupts to the VGIC instead of directly to CPUs.
>>>  
>>>  Creating a guest GICv3 device requires a host GICv3 as well.
>>>  GICv3 implementations with hardware compatibility support allow a guest GICv2
>>>  as well.
>>>  
>>> +Creating a virtual ITS controller requires a host GICv3 (but does not depend
>>> +on having physical ITS controllers).
>>> +There can be multiple ITS controllers per guest, each of them has to have
>>> +a separate, non-overlapping MMIO region.
>>> +
>>>  Groups:
>>>    KVM_DEV_ARM_VGIC_GRP_ADDR
>>>    Attributes:
>>> @@ -39,6 +45,13 @@ Groups:
>>>        Only valid for KVM_DEV_TYPE_ARM_VGIC_V3.
>>>        This address needs to be 64K aligned.
>>>  
>>> +    KVM_VGIC_V3_ADDR_TYPE_ITS (rw, 64-bit)
>>> +      Base address in the guest physical address space of the GICv3 ITS
>>> +      control register frame. The ITS allows MSI(-X) interrupts to be
>>> +      injected into guests. This extension is optional. If the kernel
>>> +      does not support the ITS, the call returns -ENODEV.
>>> +      Only valid for KVM_DEV_TYPE_ARM_VGIC_ITS.
>>> +      This address needs to be 64K aligned and the region covers 128K.
>>>  
>>>    KVM_DEV_ARM_VGIC_GRP_DIST_REGS
>>>    Attributes:
>>> @@ -109,8 +122,8 @@ Groups:
>>>    KVM_DEV_ARM_VGIC_GRP_CTRL
>>>    Attributes:
>>>      KVM_DEV_ARM_VGIC_CTRL_INIT
>>> -      request the initialization of the VGIC, no additional parameter in
>>> -      kvm_device_attr.addr.
>>> +      request the initialization of the VGIC or ITS, no additional parameter
>>> +      in kvm_device_attr.addr.
>>>    Errors:
>>>      -ENXIO: VGIC not properly configured as required prior to calling
>>>       this attribute
>>> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
>>> index 557e390..0d5c255 100644
>>> --- a/arch/arm/kvm/arm.c
>>> +++ b/arch/arm/kvm/arm.c
>>> @@ -20,6 +20,7 @@
>>>  #include <linux/errno.h>
>>>  #include <linux/err.h>
>>>  #include <linux/kvm_host.h>
>>> +#include <linux/list.h>
>>>  #include <linux/module.h>
>>>  #include <linux/vmalloc.h>
>>>  #include <linux/fs.h>
>>> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
>>> index f209ea1..3051f86 100644
>>> --- a/arch/arm64/include/uapi/asm/kvm.h
>>> +++ b/arch/arm64/include/uapi/asm/kvm.h
>>> @@ -87,9 +87,11 @@ struct kvm_regs {
>>>  /* Supported VGICv3 address types  */
>>>  #define KVM_VGIC_V3_ADDR_TYPE_DIST	2
>>>  #define KVM_VGIC_V3_ADDR_TYPE_REDIST	3
>>> +#define KVM_VGIC_ITS_ADDR_TYPE		4
>>>  
>>>  #define KVM_VGIC_V3_DIST_SIZE		SZ_64K
>>>  #define KVM_VGIC_V3_REDIST_SIZE		(2 * SZ_64K)
>>> +#define KVM_VGIC_V3_ITS_SIZE		(2 * SZ_64K)
>>>  
>>>  #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
>>>  #define KVM_ARM_VCPU_EL1_32BIT		1 /* CPU running a 32bit VM */
>>> diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
>>> index 685f339..8609fac 100644
>>> --- a/include/kvm/arm_vgic.h
>>> +++ b/include/kvm/arm_vgic.h
>>> @@ -134,6 +134,7 @@ struct vgic_its {
>>>  	gpa_t			vgic_its_base;
>>>  
>>>  	bool			enabled;
>>> +	bool			initialized;
>>>  	struct vgic_io_device	iodev;
>>>  };
>>>  
>>> @@ -167,6 +168,8 @@ struct vgic_dist {
>>>  
>>>  	struct vgic_io_device	dist_iodev;
>>>  
>>> +	bool			has_its;
>>> +
>>>  	/*
>>>  	 * Contains the attributes and gpa of the LPI configuration table.
>>>  	 * Since we report GICR_TYPER.CommonLPIAff as 0b00, we can share
>>> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
>>> index 7de96f5..d8c4c32 100644
>>> --- a/include/uapi/linux/kvm.h
>>> +++ b/include/uapi/linux/kvm.h
>>> @@ -1077,6 +1077,8 @@ enum kvm_device_type {
>>>  #define KVM_DEV_TYPE_FLIC		KVM_DEV_TYPE_FLIC
>>>  	KVM_DEV_TYPE_ARM_VGIC_V3,
>>>  #define KVM_DEV_TYPE_ARM_VGIC_V3	KVM_DEV_TYPE_ARM_VGIC_V3
>>> +	KVM_DEV_TYPE_ARM_VGIC_ITS,
>>> +#define KVM_DEV_TYPE_ARM_VGIC_ITS	KVM_DEV_TYPE_ARM_VGIC_ITS
>>>  	KVM_DEV_TYPE_MAX,
>>>  };
>>>  
>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>> index 06ad94d..393ad3a 100644
>>> --- a/virt/kvm/arm/vgic/vgic-its.c
>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>>> @@ -21,6 +21,7 @@
>>>  #include <linux/kvm.h>
>>>  #include <linux/kvm_host.h>
>>>  #include <linux/interrupt.h>
>>> +#include <linux/uaccess.h>
>>>  
>>>  #include <linux/irqchip/arm-gic-v3.h>
>>>  
>>> @@ -79,7 +80,7 @@ static struct vgic_register_region its_registers[] = {
>>>  		VGIC_ACCESS_32bit),
>>>  };
>>>  
>>> -int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>>> +static int vgic_its_register(struct kvm *kvm, struct vgic_its *its)
>>
>> Please move this hunk to the previous patch...
> 
> Doing this provokes a compilation warning about an unused function in
> the previous patch then.
> I tried forth and back to fix this (and a couple of other occasions),
> but it was either hideous, invoked rewriting stuff needlessly, do a
> massive reordering or squashing some patches together, all of which does
> not help review.
> So this was the least problematic solution I could come up with.

OK.

> I just looked at deferring the addition of vgic-its.c to the Makefile
> into the very last patch and get rid of all those lines then.
> This seems to require only very small changes.
> 
> Would that be an acceptable solution?

Yes, that'd be fine.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers
  2016-07-14 10:38   ` Marc Zyngier
@ 2016-07-14 15:35     ` Andre Przywara
  2016-07-14 16:33       ` Marc Zyngier
  0 siblings, 1 reply; 41+ messages in thread
From: Andre Przywara @ 2016-07-14 15:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On 14/07/16 11:38, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:
>> The connection between a device, an event ID, the LPI number and the
>> associated CPU is stored in in-memory tables in a GICv3, but their
>> format is not specified by the spec. Instead software uses a command
>> queue in a ring buffer to let an ITS implementation use its own
>> format.
>> Implement handlers for the various ITS commands and let them store
>> the requested relation into our own data structures. Those data
>> structures are protected by the its_lock mutex.
>> Our internal ring buffer read and write pointers are protected by the
>> its_cmd mutex, so that only one VCPU per ITS can handle commands at
>> any given time.
>> Error handling is very basic at the moment, as we don't have a good
>> way of communicating errors to the guest (usually an SError).
>> The INT command handler is missing from this patch, as we gain the
>> capability of actually injecting MSIs into the guest only later on.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>> ---
>>  virt/kvm/arm/vgic/vgic-its.c | 599 ++++++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 598 insertions(+), 1 deletion(-)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 60108f8..28abfcd 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c

[...]

>>  
>>  /*
>> + * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
>> + * is targeting) to the VGIC's view, which deals with target VCPUs.
>> + * Needs to be called whenever either the collection for a LPIs has
>> + * changed or the collection itself got retargeted.
>> + */
>> +static void update_affinity_itte(struct kvm *kvm, struct its_itte *itte)
>> +{
>> +	struct kvm_vcpu *vcpu;
>> +
>> +	vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
> 
> What happens if the collection hasn't been mapped yet? It is probably
> worth checking before blindly assigning a NULL pointer, which would
> corrupt the state set by another ITS.

OK, I can add an "if (!itte->collection) return;" for sanity. But this
is an internal function, intended to promote a new mapping to the struct
vgic_irqs, so both callers explicitly check for a mapped collection just
before calling this function. So This check would be a bit redundant.
Shall I instead add a comment documenting the requirement of the
collection already being mapped?

>> +
>> +	spin_lock(&itte->irq->irq_lock);
>> +	itte->irq->target_vcpu = vcpu;
>> +	spin_unlock(&itte->irq->irq_lock);
>> +}

[...]

>> +
>> +/*
>> + * The MAPTI and MAPI commands map LPIs to ITTEs.
>> + * Must be called with its_lock mutex held.
>> + */
>> +static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
>> +				    u64 *its_cmd, u8 subcmd)
>> +{
>> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
>> +	u32 event_id = its_cmd_get_id(its_cmd);
>> +	u32 coll_id = its_cmd_get_collection(its_cmd);
>> +	struct its_itte *itte;
>> +	struct its_device *device;
>> +	struct its_collection *collection, *new_coll = NULL;
>> +	int lpi_nr;
>> +
>> +	device = find_its_device(its, device_id);
>> +	if (!device)
>> +		return E_ITS_MAPTI_UNMAPPED_DEVICE;
>> +
>> +	collection = find_collection(its, coll_id);
> 
> Don't you need to check the range of the collection ID, and whether it
> would fit in the collection table?

We support the full range of 16 bits for the collection ID, and we can't
get more than 16 bits out of this field, so it always fits.
Does that sound right?

Cheers,
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers
  2016-07-14 15:35     ` Andre Przywara
@ 2016-07-14 16:33       ` Marc Zyngier
  2016-07-15  8:19         ` Marc Zyngier
  0 siblings, 1 reply; 41+ messages in thread
From: Marc Zyngier @ 2016-07-14 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/07/16 16:35, Andre Przywara wrote:
> Hi Marc,
> 
> On 14/07/16 11:38, Marc Zyngier wrote:
>> On 13/07/16 02:59, Andre Przywara wrote:
>>> The connection between a device, an event ID, the LPI number and the
>>> associated CPU is stored in in-memory tables in a GICv3, but their
>>> format is not specified by the spec. Instead software uses a command
>>> queue in a ring buffer to let an ITS implementation use its own
>>> format.
>>> Implement handlers for the various ITS commands and let them store
>>> the requested relation into our own data structures. Those data
>>> structures are protected by the its_lock mutex.
>>> Our internal ring buffer read and write pointers are protected by the
>>> its_cmd mutex, so that only one VCPU per ITS can handle commands at
>>> any given time.
>>> Error handling is very basic at the moment, as we don't have a good
>>> way of communicating errors to the guest (usually an SError).
>>> The INT command handler is missing from this patch, as we gain the
>>> capability of actually injecting MSIs into the guest only later on.
>>>
>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>> ---
>>>  virt/kvm/arm/vgic/vgic-its.c | 599 ++++++++++++++++++++++++++++++++++++++++++-
>>>  1 file changed, 598 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>> index 60108f8..28abfcd 100644
>>> --- a/virt/kvm/arm/vgic/vgic-its.c
>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
> 
> [...]
> 
>>>  
>>>  /*
>>> + * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
>>> + * is targeting) to the VGIC's view, which deals with target VCPUs.
>>> + * Needs to be called whenever either the collection for a LPIs has
>>> + * changed or the collection itself got retargeted.
>>> + */
>>> +static void update_affinity_itte(struct kvm *kvm, struct its_itte *itte)
>>> +{
>>> +	struct kvm_vcpu *vcpu;
>>> +
>>> +	vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
>>
>> What happens if the collection hasn't been mapped yet? It is probably
>> worth checking before blindly assigning a NULL pointer, which would
>> corrupt the state set by another ITS.
> 
> OK, I can add an "if (!itte->collection) return;" for sanity. But this
> is an internal function, intended to promote a new mapping to the struct
> vgic_irqs, so both callers explicitly check for a mapped collection just
> before calling this function. So This check would be a bit redundant.
> Shall I instead add a comment documenting the requirement of the
> collection already being mapped?

This is not the way I read it. In vgic_its_cmd_handle_mapi:

	if (!collection) {
		collection = new_coll;
		vgic_its_init_collection(its, collection, coll_id);
	}

	itte->collection = collection;
	itte->lpi = lpi_nr;
	itte->irq = vgic_add_lpi(kvm, lpi_nr);
	update_affinity_itte(kvm, itte);

If new_coll has never been mapped, you end up with the exact situation I
described, and I don't see how you make it work without checking for
target_addr being a valid vcpu index.

> 
>>> +
>>> +	spin_lock(&itte->irq->irq_lock);
>>> +	itte->irq->target_vcpu = vcpu;
>>> +	spin_unlock(&itte->irq->irq_lock);
>>> +}
> 
> [...]
> 
>>> +
>>> +/*
>>> + * The MAPTI and MAPI commands map LPIs to ITTEs.
>>> + * Must be called with its_lock mutex held.
>>> + */
>>> +static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
>>> +				    u64 *its_cmd, u8 subcmd)
>>> +{
>>> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
>>> +	u32 event_id = its_cmd_get_id(its_cmd);
>>> +	u32 coll_id = its_cmd_get_collection(its_cmd);
>>> +	struct its_itte *itte;
>>> +	struct its_device *device;
>>> +	struct its_collection *collection, *new_coll = NULL;
>>> +	int lpi_nr;
>>> +
>>> +	device = find_its_device(its, device_id);
>>> +	if (!device)
>>> +		return E_ITS_MAPTI_UNMAPPED_DEVICE;
>>> +
>>> +	collection = find_collection(its, coll_id);
>>
>> Don't you need to check the range of the collection ID, and whether it
>> would fit in the collection table?
> 
> We support the full range of 16 bits for the collection ID, and we can't
> get more than 16 bits out of this field, so it always fits.
> Does that sound right?

Let's try it. Collections are "stored" in the collection table, which
can contain at most TableSize/EntrySize entries, which is determined by
how many pages the guest has allocated. Eventually, we'll be able to
migrate the guest, and will need to write this into the allocated
memory.

To be able to map all 2^16 collections, at 8 bytes per entry, you'd need
512kB. Linux will only allocate 64kB for example.

So to answer your question: No, this doesn't sound right at all.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers
  2016-07-14 16:33       ` Marc Zyngier
@ 2016-07-15  8:19         ` Marc Zyngier
  0 siblings, 0 replies; 41+ messages in thread
From: Marc Zyngier @ 2016-07-15  8:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/07/16 17:33, Marc Zyngier wrote:
> On 14/07/16 16:35, Andre Przywara wrote:
>> Hi Marc,
>>
>> On 14/07/16 11:38, Marc Zyngier wrote:
>>> On 13/07/16 02:59, Andre Przywara wrote:
>>>> The connection between a device, an event ID, the LPI number and the
>>>> associated CPU is stored in in-memory tables in a GICv3, but their
>>>> format is not specified by the spec. Instead software uses a command
>>>> queue in a ring buffer to let an ITS implementation use its own
>>>> format.
>>>> Implement handlers for the various ITS commands and let them store
>>>> the requested relation into our own data structures. Those data
>>>> structures are protected by the its_lock mutex.
>>>> Our internal ring buffer read and write pointers are protected by the
>>>> its_cmd mutex, so that only one VCPU per ITS can handle commands at
>>>> any given time.
>>>> Error handling is very basic at the moment, as we don't have a good
>>>> way of communicating errors to the guest (usually an SError).
>>>> The INT command handler is missing from this patch, as we gain the
>>>> capability of actually injecting MSIs into the guest only later on.
>>>>
>>>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
>>>> ---
>>>>  virt/kvm/arm/vgic/vgic-its.c | 599 ++++++++++++++++++++++++++++++++++++++++++-
>>>>  1 file changed, 598 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>>> index 60108f8..28abfcd 100644
>>>> --- a/virt/kvm/arm/vgic/vgic-its.c
>>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>>
>> [...]
>>
>>>>  
>>>>  /*
>>>> + * Promotes the ITS view of affinity of an ITTE (which redistributor this LPI
>>>> + * is targeting) to the VGIC's view, which deals with target VCPUs.
>>>> + * Needs to be called whenever either the collection for a LPIs has
>>>> + * changed or the collection itself got retargeted.
>>>> + */
>>>> +static void update_affinity_itte(struct kvm *kvm, struct its_itte *itte)
>>>> +{
>>>> +	struct kvm_vcpu *vcpu;
>>>> +
>>>> +	vcpu = kvm_get_vcpu(kvm, itte->collection->target_addr);
>>>
>>> What happens if the collection hasn't been mapped yet? It is probably
>>> worth checking before blindly assigning a NULL pointer, which would
>>> corrupt the state set by another ITS.
>>
>> OK, I can add an "if (!itte->collection) return;" for sanity. But this
>> is an internal function, intended to promote a new mapping to the struct
>> vgic_irqs, so both callers explicitly check for a mapped collection just
>> before calling this function. So This check would be a bit redundant.
>> Shall I instead add a comment documenting the requirement of the
>> collection already being mapped?
> 
> This is not the way I read it. In vgic_its_cmd_handle_mapi:
> 
> 	if (!collection) {
> 		collection = new_coll;
> 		vgic_its_init_collection(its, collection, coll_id);
> 	}
> 
> 	itte->collection = collection;
> 	itte->lpi = lpi_nr;
> 	itte->irq = vgic_add_lpi(kvm, lpi_nr);
> 	update_affinity_itte(kvm, itte);
> 
> If new_coll has never been mapped, you end up with the exact situation I
> described, and I don't see how you make it work without checking for
> target_addr being a valid vcpu index.
> 
>>
>>>> +
>>>> +	spin_lock(&itte->irq->irq_lock);
>>>> +	itte->irq->target_vcpu = vcpu;
>>>> +	spin_unlock(&itte->irq->irq_lock);
>>>> +}
>>
>> [...]
>>
>>>> +
>>>> +/*
>>>> + * The MAPTI and MAPI commands map LPIs to ITTEs.
>>>> + * Must be called with its_lock mutex held.
>>>> + */
>>>> +static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
>>>> +				    u64 *its_cmd, u8 subcmd)
>>>> +{
>>>> +	u32 device_id = its_cmd_get_deviceid(its_cmd);
>>>> +	u32 event_id = its_cmd_get_id(its_cmd);
>>>> +	u32 coll_id = its_cmd_get_collection(its_cmd);
>>>> +	struct its_itte *itte;
>>>> +	struct its_device *device;
>>>> +	struct its_collection *collection, *new_coll = NULL;
>>>> +	int lpi_nr;
>>>> +
>>>> +	device = find_its_device(its, device_id);
>>>> +	if (!device)
>>>> +		return E_ITS_MAPTI_UNMAPPED_DEVICE;
>>>> +
>>>> +	collection = find_collection(its, coll_id);
>>>
>>> Don't you need to check the range of the collection ID, and whether it
>>> would fit in the collection table?
>>
>> We support the full range of 16 bits for the collection ID, and we can't
>> get more than 16 bits out of this field, so it always fits.
>> Does that sound right?
> 
> Let's try it. Collections are "stored" in the collection table, which
> can contain at most TableSize/EntrySize entries, which is determined by
> how many pages the guest has allocated. Eventually, we'll be able to
> migrate the guest, and will need to write this into the allocated
> memory.
> 
> To be able to map all 2^16 collections, at 8 bytes per entry, you'd need
> 512kB. Linux will only allocate 64kB for example.
> 
> So to answer your question: No, this doesn't sound right at all.

BTW: your MAPD implementation suffers from the exact same issue, only
complicated by the Indirect handling (you need to verify that the
level-1 page has a valid pointer to a level-2 table).

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device
  2016-07-14  8:36   ` Marc Zyngier
  2016-07-14 11:11     ` Andre Przywara
@ 2016-07-15  9:33     ` Andre Przywara
  1 sibling, 0 replies; 41+ messages in thread
From: Andre Przywara @ 2016-07-15  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 14/07/16 09:36, Marc Zyngier wrote:
> On 13/07/16 02:59, Andre Przywara wrote:

....

>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> index 06ad94d..393ad3a 100644
>> --- a/virt/kvm/arm/vgic/vgic-its.c
>> +++ b/virt/kvm/arm/vgic/vgic-its.c

...

>> +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);
> 
> Shouldn't that be the ITS size (128kB)?

No, this is the alignment requirement. See here:

...

>> diff --git a/virt/kvm/arm/vgic/vgic-kvm-device.c b/virt/kvm/arm/vgic/vgic-kvm-device.c
>> index 2f24f13..1813f93 100644
>> --- a/virt/kvm/arm/vgic/vgic-kvm-device.c
>> +++ b/virt/kvm/arm/vgic/vgic-kvm-device.c
>> @@ -21,8 +21,8 @@
>>  
>>  /* common helpers */
>>  
>> -static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
>> -			     phys_addr_t addr, phys_addr_t alignment)
>> +int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
>> +		      phys_addr_t addr, phys_addr_t alignment)
>>  {
>>  	if (addr & ~KVM_PHYS_MASK)
>>  		return -E2BIG;

Fixed the rest.

Cheers,
Andre.

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2016-07-15  9:33 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-13  1:58 [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Andre Przywara
2016-07-13  1:58 ` [PATCH v9 01/17] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
2016-07-13  1:58 ` [PATCH v9 02/17] KVM: arm/arm64: check return value for kvm_register_vgic_device Andre Przywara
2016-07-13  1:58 ` [PATCH v9 03/17] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
2016-07-13 10:28   ` Paolo Bonzini
2016-07-13  1:58 ` [PATCH v9 04/17] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
2016-07-13  1:58 ` [PATCH v9 05/17] KVM: kvm_io_bus: add kvm_io_bus_get_dev() call Andre Przywara
2016-07-13 10:29   ` Paolo Bonzini
2016-07-13  1:58 ` [PATCH v9 06/17] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
2016-07-13 12:15   ` Marc Zyngier
2016-07-13 13:50     ` Andre Przywara
2016-07-13  1:58 ` [PATCH v9 07/17] irqchip: refactor and add GICv3 definitions Andre Przywara
2016-07-13 12:28   ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 08/17] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
2016-07-13  1:59 ` [PATCH v9 09/17] KVM: arm64: introduce ITS emulation file with MMIO framework Andre Przywara
2016-07-13  1:59 ` [PATCH v9 10/17] KVM: arm64: introduce new KVM ITS device Andre Przywara
2016-07-14  8:36   ` Marc Zyngier
2016-07-14 11:11     ` Andre Przywara
2016-07-14 12:57       ` Marc Zyngier
2016-07-15  9:33     ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 11/17] KVM: arm64: implement basic ITS register handlers Andre Przywara
2016-07-14  9:13   ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 12/17] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
2016-07-13  1:59 ` [PATCH v9 13/17] KVM: arm64: read initial LPI pending table Andre Przywara
2016-07-14  9:34   ` Marc Zyngier
2016-07-14 10:16     ` Andre Przywara
2016-07-13  1:59 ` [PATCH v9 14/17] KVM: arm64: allow updates of LPI configuration table Andre Przywara
2016-07-14  9:46   ` Marc Zyngier
2016-07-14 10:00     ` Andre Przywara
2016-07-14 10:10       ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 15/17] KVM: arm64: implement ITS command queue command handlers Andre Przywara
2016-07-14 10:38   ` Marc Zyngier
2016-07-14 15:35     ` Andre Przywara
2016-07-14 16:33       ` Marc Zyngier
2016-07-15  8:19         ` Marc Zyngier
2016-07-13  1:59 ` [PATCH v9 16/17] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
2016-07-13  1:59 ` [PATCH v9 17/17] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
2016-07-13  7:57 ` [PATCH v9 00/17] KVM: arm64: GICv3 ITS emulation Diana Madalina Craciun
2016-07-13  8:15   ` Andre Przywara
2016-07-14 10:52 ` Marc Zyngier
2016-07-14 11:01   ` Paolo Bonzini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).