All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/15] KVM: arm64: GICv3 ITS emulation
@ 2016-06-17 12:08 ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, linux-arm-kernel

Hi,

another heavily reworked version of the KVM ITS emulation support.
It 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 kvm-arm-for-v4.7-rc2 and only works with the new VGIC
implementation.

Compared to the last drop there have been significant changes to the
code:
References to multiple ITSes are now solely held in the kvm_io_bus
framework, which knows about the base addresses and the struct vgic_its
pointers anyway. This removes the its_list from the VGIC code.
Also there is now a list of mapped LPIs per guest, an ITTE only holds
a reference to the struct vgic_irq's. To avoid situations where an LPI
gets unmapped while the VGIC is still dealing with it, we introduce 
some reference counting to IRQs (new patch 05/15). This is not the
classical textbook reference counting with atomic accesses, because we
lock the IRQs anyway and just accessing the refcount inside the lock
simplifies the whole code. I am open to change requests here, though I
tried to atomic_t way and it looked more involved than the current
solution (since we do get/lock and unlock/put all of the time).
Other changes include many command handlers, namely INV, INVALL and
MOVALL. They should now match the architecture better.
I am not entirely sure having MOVALL right now, but I added some
safeguarding which matches a recommendation in the spec.
The PROPBASE and PENDBASER registers are now sanitised, also we now
implement the BASER registers in preparation for migration support.
At the moment we just allow (sanitised) writes and reads to them, though.

You can find all of this code (and the prerequisites) in the
its-emul/v6 branch of my repository [1].
This has been briefly tested on the model and on GICv3 hardware.
Since there are again quite some fundamental changes in this version,
I expect some issues in the code, so if you have GICv3 capable hardware,
please test it on your setup.
Also of course any review comments are very welcome!

Cheers,
Andre.

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 kvm-arm-for-v4.7-rc2 and can be found at the
its-emul/v6 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/v6
[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://arminfo.emea.arm.com/help/topic/com.arm.doc.ihi0069a/IHI0069A_gic_architecture_specification.pdf

Andre Przywara (15):
  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: arm/arm64: VGIC: add refcounting for IRQs
  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                         |   12 +-
 include/kvm/vgic/vgic.h                        |   61 +-
 include/linux/irqchip/arm-gic-v3.h             |   32 +-
 include/uapi/linux/kvm.h                       |    7 +-
 virt/kvm/arm/vgic.c                            |    5 +
 virt/kvm/arm/vgic/vgic-init.c                  |    9 +-
 virt/kvm/arm/vgic/vgic-its.c                   | 1393 ++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-kvm-device.c            |   22 +-
 virt/kvm/arm/vgic/vgic-mmio-v2.c               |   21 +-
 virt/kvm/arm/vgic/vgic-mmio-v3.c               |  213 +++-
 virt/kvm/arm/vgic/vgic-mmio.c                  |   80 +-
 virt/kvm/arm/vgic/vgic-mmio.h                  |   14 +
 virt/kvm/arm/vgic/vgic-v2.c                    |   11 +-
 virt/kvm/arm/vgic/vgic-v3.c                    |   21 +-
 virt/kvm/arm/vgic/vgic.c                       |   98 +-
 virt/kvm/arm/vgic/vgic.h                       |   42 +-
 24 files changed, 1947 insertions(+), 144 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

-- 
2.8.2


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

* [PATCH v6 00/15] KVM: arm64: GICv3 ITS emulation
@ 2016-06-17 12:08 ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

another heavily reworked version of the KVM ITS emulation support.
It 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 kvm-arm-for-v4.7-rc2 and only works with the new VGIC
implementation.

Compared to the last drop there have been significant changes to the
code:
References to multiple ITSes are now solely held in the kvm_io_bus
framework, which knows about the base addresses and the struct vgic_its
pointers anyway. This removes the its_list from the VGIC code.
Also there is now a list of mapped LPIs per guest, an ITTE only holds
a reference to the struct vgic_irq's. To avoid situations where an LPI
gets unmapped while the VGIC is still dealing with it, we introduce 
some reference counting to IRQs (new patch 05/15). This is not the
classical textbook reference counting with atomic accesses, because we
lock the IRQs anyway and just accessing the refcount inside the lock
simplifies the whole code. I am open to change requests here, though I
tried to atomic_t way and it looked more involved than the current
solution (since we do get/lock and unlock/put all of the time).
Other changes include many command handlers, namely INV, INVALL and
MOVALL. They should now match the architecture better.
I am not entirely sure having MOVALL right now, but I added some
safeguarding which matches a recommendation in the spec.
The PROPBASE and PENDBASER registers are now sanitised, also we now
implement the BASER registers in preparation for migration support.
At the moment we just allow (sanitised) writes and reads to them, though.

You can find all of this code (and the prerequisites) in the
its-emul/v6 branch of my repository [1].
This has been briefly tested on the model and on GICv3 hardware.
Since there are again quite some fundamental changes in this version,
I expect some issues in the code, so if you have GICv3 capable hardware,
please test it on your setup.
Also of course any review comments are very welcome!

Cheers,
Andre.

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 kvm-arm-for-v4.7-rc2 and can be found at the
its-emul/v6 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/v6
[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://arminfo.emea.arm.com/help/topic/com.arm.doc.ihi0069a/IHI0069A_gic_architecture_specification.pdf

Andre Przywara (15):
  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: arm/arm64: VGIC: add refcounting for IRQs
  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                         |   12 +-
 include/kvm/vgic/vgic.h                        |   61 +-
 include/linux/irqchip/arm-gic-v3.h             |   32 +-
 include/uapi/linux/kvm.h                       |    7 +-
 virt/kvm/arm/vgic.c                            |    5 +
 virt/kvm/arm/vgic/vgic-init.c                  |    9 +-
 virt/kvm/arm/vgic/vgic-its.c                   | 1393 ++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-kvm-device.c            |   22 +-
 virt/kvm/arm/vgic/vgic-mmio-v2.c               |   21 +-
 virt/kvm/arm/vgic/vgic-mmio-v3.c               |  213 +++-
 virt/kvm/arm/vgic/vgic-mmio.c                  |   80 +-
 virt/kvm/arm/vgic/vgic-mmio.h                  |   14 +
 virt/kvm/arm/vgic/vgic-v2.c                    |   11 +-
 virt/kvm/arm/vgic/vgic-v3.c                    |   21 +-
 virt/kvm/arm/vgic/vgic.c                       |   98 +-
 virt/kvm/arm/vgic/vgic.h                       |   42 +-
 24 files changed, 1947 insertions(+), 144 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

-- 
2.8.2

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

* [PATCH v6 01/15] KVM: arm/arm64: move redistributor kvm_io_devices
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, 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>
---
 include/kvm/vgic/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/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 3fbd175..2f26f37 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/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.8.2


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

* [PATCH v6 01/15] KVM: arm/arm64: move redistributor kvm_io_devices
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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>
---
 include/kvm/vgic/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/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 3fbd175..2f26f37 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/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.8.2

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

* [PATCH v6 02/15] KVM: arm/arm64: check return value for kvm_register_vgic_device
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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         |  7 ++++++-
 virt/kvm/arm/vgic/vgic-v3.c         | 15 +++++++++++++--
 virt/kvm/arm/vgic/vgic.h            |  2 +-
 4 files changed, 29 insertions(+), 10 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..80313de 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -344,7 +344,12 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
 	}
 
 	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");
+		iounmap(kvm_vgic_global_state.vctrl_base);
+		return ret;
+	}
 
 	kvm_vgic_global_state.vcpu_base = info->vcpu.start;
 	kvm_vgic_global_state.type = VGIC_V2;
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.8.2

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

* [PATCH v6 02/15] KVM: arm/arm64: check return value for kvm_register_vgic_device
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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         |  7 ++++++-
 virt/kvm/arm/vgic/vgic-v3.c         | 15 +++++++++++++--
 virt/kvm/arm/vgic/vgic.h            |  2 +-
 4 files changed, 29 insertions(+), 10 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..80313de 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -344,7 +344,12 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
 	}
 
 	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");
+		iounmap(kvm_vgic_global_state.vctrl_base);
+		return ret;
+	}
 
 	kvm_vgic_global_state.vcpu_base = info->vcpu.start;
 	kvm_vgic_global_state.type = VGIC_V2;
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.8.2

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

* [PATCH v6 03/15] KVM: extend struct kvm_msi to hold a 32-bit device ID
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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>
---
 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 a4482cc..bf76639 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2169,10 +2169,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.8.2

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

* [PATCH v6 03/15] KVM: extend struct kvm_msi to hold a 32-bit device ID
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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>
---
 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 a4482cc..bf76639 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2169,10 +2169,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.8.2

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

* [PATCH v6 04/15] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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 893941e..a268c85 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 b1ad730..6ec9dfe 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.8.2

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

* [PATCH v6 04/15] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 893941e..a268c85 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 b1ad730..6ec9dfe 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.8.2

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

* [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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 to
those structures. Since private IRQs and SPIs are statically allocated,
the reqcount 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.
Also vgic_get_irq() gets changed to take the irq_lock already, this is
later needed to avoid a race between a potential LPI unmap and the
window between us getting the pointer and locking the IRQ.
This introduces vgic_put_irq(), which just does the unlock and makes
the new locking sequence look more symmetrical.
This approach deviates from the classical Linux refcounting with using
atomic_* types and functions, because the users of vgic_get_irq() take
the irq_lock anyway, so we just use an int and adjust the refcount while
holding the lock.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/vgic/vgic.h          |  1 +
 virt/kvm/arm/vgic/vgic-init.c    |  2 +
 virt/kvm/arm/vgic/vgic-mmio-v2.c | 21 +++++------
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 26 ++++++-------
 virt/kvm/arm/vgic/vgic-mmio.c    | 55 ++++++++++++++++-----------
 virt/kvm/arm/vgic/vgic-v2.c      |  4 +-
 virt/kvm/arm/vgic/vgic-v3.c      |  4 +-
 virt/kvm/arm/vgic/vgic.c         | 81 +++++++++++++++++++++++++---------------
 virt/kvm/arm/vgic/vgic.h         |  7 +++-
 9 files changed, 116 insertions(+), 85 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 2f26f37..e488a369 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -96,6 +96,7 @@ struct vgic_irq {
 	bool active;			/* not used for LPIs */
 	bool enabled;
 	bool hw;			/* Tied to HW IRQ */
+	int refcnt;			/* Used only 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..c4a8df6 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;
+		irq->refcnt = 1;
 		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;
+		irq->refcnt = 1;
 		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..7bb3e94 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
@@ -97,11 +97,10 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
 
 		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 		irq->source |= 1U << source_vcpu->vcpu_id;
 
-		vgic_queue_irq_unlock(source_vcpu->kvm, irq);
+		vgic_queue_irq_put(source_vcpu->kvm, irq);
 	}
 }
 
@@ -116,6 +115,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(irq);
 	}
 
 	return val;
@@ -136,13 +137,11 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
 		int target;
 
-		spin_lock(&irq->irq_lock);
-
 		irq->targets = (val >> (i * 8)) & 0xff;
 		target = irq->targets ? __ffs(irq->targets) : 0;
 		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -157,6 +156,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(irq);
 	}
 	return val;
 }
@@ -171,13 +172,11 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->source &= ~((val >> (i * 8)) & 0xff);
 		if (!irq->source)
 			irq->pending = false;
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -191,15 +190,13 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->source |= (val >> (i * 8)) & 0xff;
 
 		if (irq->source) {
 			irq->pending = true;
-			vgic_queue_irq_unlock(vcpu->kvm, irq);
+			vgic_queue_irq_put(vcpu->kvm, irq);
 		} else {
-			spin_unlock(&irq->irq_lock);
+			vgic_put_irq(irq);
 		}
 	}
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index fc7b6c9..c38302d 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(irq);
+	return ret;
 }
 
 static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
@@ -102,16 +104,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 		return;
 
 	/* The upper word is WI for us since we don't implement Aff3. */
-	if (addr & 4)
-		return;
-
-	spin_lock(&irq->irq_lock);
-
-	/* We only care about and preserve Aff0, Aff1 and Aff2. */
-	irq->mpidr = val & GENMASK(23, 0);
-	irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
+	if (!(addr & 4)) {
+		/* We only care about and preserve Aff0, Aff1 and Aff2. */
+		irq->mpidr = val & GENMASK(23, 0);
+		irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
+	}
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 }
 
 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
@@ -441,9 +440,8 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
 
 		irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 9f6fab7..4050c1c 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(irq);
 	}
 
 	return value;
@@ -71,9 +73,9 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		irq->enabled = true;
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
 
@@ -87,11 +89,9 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->enabled = false;
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -108,6 +108,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
 
 		if (irq->pending)
 			value |= (1U << i);
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
@@ -123,12 +125,11 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 		if (irq->config == VGIC_CONFIG_LEVEL)
 			irq->soft_pending = true;
 
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
 
@@ -142,8 +143,6 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		if (irq->config == VGIC_CONFIG_LEVEL) {
 			irq->soft_pending = false;
 			irq->pending = irq->line_level;
@@ -151,7 +150,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
 			irq->pending = false;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -168,15 +167,17 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
 
 		if (irq->active)
 			value |= (1U << i);
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
 }
 
-static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
-				    bool new_active_state)
+static void vgic_mmio_change_active_put(struct kvm_vcpu *vcpu,
+					struct vgic_irq *irq,
+					bool new_active_state)
 {
-	spin_lock(&irq->irq_lock);
 	/*
 	 * If this virtual IRQ was written into a list register, we
 	 * have to make sure the CPU that runs the VCPU thread has
@@ -190,15 +191,17 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
 	 * IRQ, so we release and re-acquire the spin_lock to let the
 	 * other thread sync back the IRQ.
 	 */
+	irq->refcnt++;
 	while (irq->vcpu && /* IRQ may have state in an LR somewhere */
 	       irq->vcpu->cpu != -1) /* VCPU thread is running */
 		cond_resched_lock(&irq->irq_lock);
 
+	irq->refcnt--;
 	irq->active = new_active_state;
 	if (new_active_state)
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	else
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 }
 
 /*
@@ -241,7 +244,8 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
 	vgic_change_active_prepare(vcpu, intid);
 	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_mmio_change_active_put(vcpu, irq, false);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -256,7 +260,8 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
 	vgic_change_active_prepare(vcpu, intid);
 	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_mmio_change_active_put(vcpu, irq, true);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -272,6 +277,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(irq);
 	}
 
 	return val;
@@ -294,10 +301,10 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		/* 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(irq);
 	}
 }
 
@@ -313,6 +320,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
 
 		if (irq->config == VGIC_CONFIG_EDGE)
 			value |= (2U << (i * 2));
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
@@ -326,7 +335,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 +346,16 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
 		if (intid + i < VGIC_NR_PRIVATE_IRQS)
 			continue;
 
-		spin_lock(&irq->irq_lock);
+		irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
+
 		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(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 80313de..2147576 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -94,8 +94,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 
 		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
-
 		/* Always preserve the active bit */
 		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
 
@@ -123,7 +121,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 			irq->pending = irq->line_level || irq->soft_pending;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index e48a22e..21d84e9 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -82,8 +82,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 			intid = val & GICH_LR_VIRTUALID;
 		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
-
 		/* Always preserve the active bit */
 		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
 
@@ -112,7 +110,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 			irq->pending = irq->line_level || irq->soft_pending;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 69b61ab..90f2543 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];
+		spin_lock(&irq->irq_lock);
+		return irq;
+	}
+
+	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
+		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
+		spin_lock(&irq->irq_lock);
+		return irq;
+	}
 
 	/* LPIs are not yet covered */
 	if (intid >= VGIC_MIN_LPI)
@@ -183,9 +190,10 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
  * Needs to be entered with the IRQ lock already held, but will return
  * with all locks dropped.
  */
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
+bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq)
 {
-	struct kvm_vcpu *vcpu;
+	struct kvm_vcpu *vcpu, *irq_vcpu = irq->target_vcpu;
+	u32 intid = irq->intid;
 
 	DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&irq->irq_lock));
 
@@ -201,7 +209,7 @@ retry:
 		 * not need to be inserted into an ap_list and there is also
 		 * no more work for us to do.
 		 */
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 		return false;
 	}
 
@@ -209,12 +217,18 @@ retry:
 	 * We must unlock the irq lock to take the ap_list_lock where
 	 * we are going to insert this new pending interrupt.
 	 */
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	/* someone can do stuff here, which we re-check below */
 
 	spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
-	spin_lock(&irq->irq_lock);
+	irq = vgic_get_irq(kvm, irq_vcpu, intid);
+
+	if (!irq) {
+		/* The LPI has been unmapped, nothing left to do. */
+		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
+		return false;
+	}
 
 	/*
 	 * Did something change behind our backs?
@@ -229,17 +243,21 @@ retry:
 	 */
 
 	if (unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq))) {
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
 
-		spin_lock(&irq->irq_lock);
+		irq = vgic_get_irq(kvm, irq_vcpu, intid);
+		if (!irq)
+			return false;
+
 		goto retry;
 	}
 
 	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
+	irq->refcnt++;
 	irq->vcpu = vcpu;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 	spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
 
 	kvm_vcpu_kick(vcpu);
@@ -269,14 +287,14 @@ 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(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(irq);
 		return 0;
 	}
 
@@ -287,7 +305,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 		irq->pending = true;
 	}
 
-	vgic_queue_irq_unlock(kvm, irq);
+	vgic_queue_irq_put(kvm, irq);
 
 	return 0;
 }
@@ -324,31 +342,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
 
 	BUG_ON(!irq);
 
-	spin_lock(&irq->irq_lock);
-
 	irq->hw = true;
 	irq->hwintid = phys_irq;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(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;
 
-	spin_lock(&irq->irq_lock);
+	irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
+	BUG_ON(!irq);
 
 	irq->hw = false;
 	irq->hwintid = 0;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	return 0;
 }
@@ -378,14 +393,21 @@ retry:
 
 		target_vcpu = vgic_target_oracle(irq);
 
-		if (!target_vcpu) {
+		if (!target_vcpu || irq->refcnt == 1) {
+			bool free_irq = false;
+
 			/*
 			 * We don't need to process this interrupt any
 			 * further, move it off the list.
 			 */
 			list_del(&irq->ap_list);
 			irq->vcpu = NULL;
+			irq->refcnt--;
+			if (!irq->refcnt)
+				free_irq = true;
 			spin_unlock(&irq->irq_lock);
+			if (free_irq)
+				kfree(irq);
 			continue;
 		}
 
@@ -611,9 +633,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
 	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
 	bool map_is_active;
 
-	spin_lock(&irq->irq_lock);
 	map_is_active = irq->hw && irq->active;
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	return map_is_active;
 }
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index c752152..fa2d225 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -38,7 +38,12 @@ struct vgic_vmcr {
 
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid);
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
+static inline void vgic_put_irq(struct vgic_irq *irq)
+{
+	spin_unlock(&irq->irq_lock);
+}
+
+bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq);
 void vgic_kick_vcpus(struct kvm *kvm);
 
 void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
-- 
2.8.2

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

* [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 to
those structures. Since private IRQs and SPIs are statically allocated,
the reqcount 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.
Also vgic_get_irq() gets changed to take the irq_lock already, this is
later needed to avoid a race between a potential LPI unmap and the
window between us getting the pointer and locking the IRQ.
This introduces vgic_put_irq(), which just does the unlock and makes
the new locking sequence look more symmetrical.
This approach deviates from the classical Linux refcounting with using
atomic_* types and functions, because the users of vgic_get_irq() take
the irq_lock anyway, so we just use an int and adjust the refcount while
holding the lock.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/kvm/vgic/vgic.h          |  1 +
 virt/kvm/arm/vgic/vgic-init.c    |  2 +
 virt/kvm/arm/vgic/vgic-mmio-v2.c | 21 +++++------
 virt/kvm/arm/vgic/vgic-mmio-v3.c | 26 ++++++-------
 virt/kvm/arm/vgic/vgic-mmio.c    | 55 ++++++++++++++++-----------
 virt/kvm/arm/vgic/vgic-v2.c      |  4 +-
 virt/kvm/arm/vgic/vgic-v3.c      |  4 +-
 virt/kvm/arm/vgic/vgic.c         | 81 +++++++++++++++++++++++++---------------
 virt/kvm/arm/vgic/vgic.h         |  7 +++-
 9 files changed, 116 insertions(+), 85 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 2f26f37..e488a369 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -96,6 +96,7 @@ struct vgic_irq {
 	bool active;			/* not used for LPIs */
 	bool enabled;
 	bool hw;			/* Tied to HW IRQ */
+	int refcnt;			/* Used only 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..c4a8df6 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;
+		irq->refcnt = 1;
 		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;
+		irq->refcnt = 1;
 		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..7bb3e94 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
@@ -97,11 +97,10 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
 
 		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 		irq->source |= 1U << source_vcpu->vcpu_id;
 
-		vgic_queue_irq_unlock(source_vcpu->kvm, irq);
+		vgic_queue_irq_put(source_vcpu->kvm, irq);
 	}
 }
 
@@ -116,6 +115,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(irq);
 	}
 
 	return val;
@@ -136,13 +137,11 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
 		int target;
 
-		spin_lock(&irq->irq_lock);
-
 		irq->targets = (val >> (i * 8)) & 0xff;
 		target = irq->targets ? __ffs(irq->targets) : 0;
 		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -157,6 +156,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(irq);
 	}
 	return val;
 }
@@ -171,13 +172,11 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->source &= ~((val >> (i * 8)) & 0xff);
 		if (!irq->source)
 			irq->pending = false;
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -191,15 +190,13 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->source |= (val >> (i * 8)) & 0xff;
 
 		if (irq->source) {
 			irq->pending = true;
-			vgic_queue_irq_unlock(vcpu->kvm, irq);
+			vgic_queue_irq_put(vcpu->kvm, irq);
 		} else {
-			spin_unlock(&irq->irq_lock);
+			vgic_put_irq(irq);
 		}
 	}
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index fc7b6c9..c38302d 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(irq);
+	return ret;
 }
 
 static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
@@ -102,16 +104,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 		return;
 
 	/* The upper word is WI for us since we don't implement Aff3. */
-	if (addr & 4)
-		return;
-
-	spin_lock(&irq->irq_lock);
-
-	/* We only care about and preserve Aff0, Aff1 and Aff2. */
-	irq->mpidr = val & GENMASK(23, 0);
-	irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
+	if (!(addr & 4)) {
+		/* We only care about and preserve Aff0, Aff1 and Aff2. */
+		irq->mpidr = val & GENMASK(23, 0);
+		irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
+	}
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 }
 
 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
@@ -441,9 +440,8 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
 
 		irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 9f6fab7..4050c1c 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(irq);
 	}
 
 	return value;
@@ -71,9 +73,9 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		irq->enabled = true;
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
 
@@ -87,11 +89,9 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		irq->enabled = false;
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -108,6 +108,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
 
 		if (irq->pending)
 			value |= (1U << i);
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
@@ -123,12 +125,11 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		irq->pending = true;
 		if (irq->config == VGIC_CONFIG_LEVEL)
 			irq->soft_pending = true;
 
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	}
 }
 
@@ -142,8 +143,6 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
 	for_each_set_bit(i, &val, len * 8) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
-
 		if (irq->config == VGIC_CONFIG_LEVEL) {
 			irq->soft_pending = false;
 			irq->pending = irq->line_level;
@@ -151,7 +150,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
 			irq->pending = false;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
@@ -168,15 +167,17 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
 
 		if (irq->active)
 			value |= (1U << i);
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
 }
 
-static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
-				    bool new_active_state)
+static void vgic_mmio_change_active_put(struct kvm_vcpu *vcpu,
+					struct vgic_irq *irq,
+					bool new_active_state)
 {
-	spin_lock(&irq->irq_lock);
 	/*
 	 * If this virtual IRQ was written into a list register, we
 	 * have to make sure the CPU that runs the VCPU thread has
@@ -190,15 +191,17 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
 	 * IRQ, so we release and re-acquire the spin_lock to let the
 	 * other thread sync back the IRQ.
 	 */
+	irq->refcnt++;
 	while (irq->vcpu && /* IRQ may have state in an LR somewhere */
 	       irq->vcpu->cpu != -1) /* VCPU thread is running */
 		cond_resched_lock(&irq->irq_lock);
 
+	irq->refcnt--;
 	irq->active = new_active_state;
 	if (new_active_state)
-		vgic_queue_irq_unlock(vcpu->kvm, irq);
+		vgic_queue_irq_put(vcpu->kvm, irq);
 	else
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 }
 
 /*
@@ -241,7 +244,8 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
 	vgic_change_active_prepare(vcpu, intid);
 	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_mmio_change_active_put(vcpu, irq, false);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -256,7 +260,8 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
 	vgic_change_active_prepare(vcpu, intid);
 	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_mmio_change_active_put(vcpu, irq, true);
 	}
 	vgic_change_active_finish(vcpu, intid);
 }
@@ -272,6 +277,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(irq);
 	}
 
 	return val;
@@ -294,10 +301,10 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
 	for (i = 0; i < len; i++) {
 		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-		spin_lock(&irq->irq_lock);
 		/* 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(irq);
 	}
 }
 
@@ -313,6 +320,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
 
 		if (irq->config == VGIC_CONFIG_EDGE)
 			value |= (2U << (i * 2));
+
+		vgic_put_irq(irq);
 	}
 
 	return value;
@@ -326,7 +335,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 +346,16 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
 		if (intid + i < VGIC_NR_PRIVATE_IRQS)
 			continue;
 
-		spin_lock(&irq->irq_lock);
+		irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
+
 		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(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 80313de..2147576 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -94,8 +94,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 
 		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
-
 		/* Always preserve the active bit */
 		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
 
@@ -123,7 +121,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
 			irq->pending = irq->line_level || irq->soft_pending;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index e48a22e..21d84e9 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -82,8 +82,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 			intid = val & GICH_LR_VIRTUALID;
 		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
 
-		spin_lock(&irq->irq_lock);
-
 		/* Always preserve the active bit */
 		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
 
@@ -112,7 +110,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
 			irq->pending = irq->line_level || irq->soft_pending;
 		}
 
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 	}
 }
 
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 69b61ab..90f2543 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];
+		spin_lock(&irq->irq_lock);
+		return irq;
+	}
+
+	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
+		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
+		spin_lock(&irq->irq_lock);
+		return irq;
+	}
 
 	/* LPIs are not yet covered */
 	if (intid >= VGIC_MIN_LPI)
@@ -183,9 +190,10 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
  * Needs to be entered with the IRQ lock already held, but will return
  * with all locks dropped.
  */
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
+bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq)
 {
-	struct kvm_vcpu *vcpu;
+	struct kvm_vcpu *vcpu, *irq_vcpu = irq->target_vcpu;
+	u32 intid = irq->intid;
 
 	DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&irq->irq_lock));
 
@@ -201,7 +209,7 @@ retry:
 		 * not need to be inserted into an ap_list and there is also
 		 * no more work for us to do.
 		 */
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 		return false;
 	}
 
@@ -209,12 +217,18 @@ retry:
 	 * We must unlock the irq lock to take the ap_list_lock where
 	 * we are going to insert this new pending interrupt.
 	 */
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	/* someone can do stuff here, which we re-check below */
 
 	spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
-	spin_lock(&irq->irq_lock);
+	irq = vgic_get_irq(kvm, irq_vcpu, intid);
+
+	if (!irq) {
+		/* The LPI has been unmapped, nothing left to do. */
+		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
+		return false;
+	}
 
 	/*
 	 * Did something change behind our backs?
@@ -229,17 +243,21 @@ retry:
 	 */
 
 	if (unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq))) {
-		spin_unlock(&irq->irq_lock);
+		vgic_put_irq(irq);
 		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
 
-		spin_lock(&irq->irq_lock);
+		irq = vgic_get_irq(kvm, irq_vcpu, intid);
+		if (!irq)
+			return false;
+
 		goto retry;
 	}
 
 	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
+	irq->refcnt++;
 	irq->vcpu = vcpu;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 	spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
 
 	kvm_vcpu_kick(vcpu);
@@ -269,14 +287,14 @@ 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(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(irq);
 		return 0;
 	}
 
@@ -287,7 +305,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
 		irq->pending = true;
 	}
 
-	vgic_queue_irq_unlock(kvm, irq);
+	vgic_queue_irq_put(kvm, irq);
 
 	return 0;
 }
@@ -324,31 +342,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
 
 	BUG_ON(!irq);
 
-	spin_lock(&irq->irq_lock);
-
 	irq->hw = true;
 	irq->hwintid = phys_irq;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(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;
 
-	spin_lock(&irq->irq_lock);
+	irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
+	BUG_ON(!irq);
 
 	irq->hw = false;
 	irq->hwintid = 0;
 
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	return 0;
 }
@@ -378,14 +393,21 @@ retry:
 
 		target_vcpu = vgic_target_oracle(irq);
 
-		if (!target_vcpu) {
+		if (!target_vcpu || irq->refcnt == 1) {
+			bool free_irq = false;
+
 			/*
 			 * We don't need to process this interrupt any
 			 * further, move it off the list.
 			 */
 			list_del(&irq->ap_list);
 			irq->vcpu = NULL;
+			irq->refcnt--;
+			if (!irq->refcnt)
+				free_irq = true;
 			spin_unlock(&irq->irq_lock);
+			if (free_irq)
+				kfree(irq);
 			continue;
 		}
 
@@ -611,9 +633,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
 	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
 	bool map_is_active;
 
-	spin_lock(&irq->irq_lock);
 	map_is_active = irq->hw && irq->active;
-	spin_unlock(&irq->irq_lock);
+	vgic_put_irq(irq);
 
 	return map_is_active;
 }
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index c752152..fa2d225 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -38,7 +38,12 @@ struct vgic_vmcr {
 
 struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
 			      u32 intid);
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
+static inline void vgic_put_irq(struct vgic_irq *irq)
+{
+	spin_unlock(&irq->irq_lock);
+}
+
+bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq);
 void vgic_kick_vcpus(struct kvm *kvm);
 
 void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
-- 
2.8.2

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

* [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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/vgic/vgic.h            |  13 +++++
 include/linux/irqchip/arm-gic-v3.h |   1 +
 virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index e488a369..dc7f2fd 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -146,6 +146,14 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
+
+	/*
+	 * Contains the address 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;
+
+	/* Points to the LPI pending tables for the redistributor */
+	u64 pendbaser;
+
+	bool lpis_enabled;
 };
 
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index bfbd707..64e8c70 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -124,6 +124,7 @@
 #define GICR_PROPBASER_WaWb		(5U << 7)
 #define GICR_PROPBASER_RaWaWt		(6U << 7)
 #define GICR_PROPBASER_RaWaWb		(7U << 7)
+#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
 #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
 #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index c38302d..8cd7190 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)
 {
@@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/* We don't have any constraints about the shareability attributes. */
+static void vgic_sanitise_shareability(u64 *reg)
+{
+}
+
+#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
+#define GIC_CACHE_PROP_MASK \
+	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
+
+static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
+{
+	switch (*reg >> reg_shift) {
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
+		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
+		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
+		break;
+	default:
+		/* We are fine with the other attributes. */
+		break;
+	}
+}
+
+static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
+{
+	switch (*reg >> reg_shift) {
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
+		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
+		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
+		break;
+	default:
+		/* We are fine with the other attributes. */
+		break;
+	}
+}
+
+static void vgic_sanitise_redist_baser(u64 *reg)
+{
+	vgic_sanitise_shareability(reg);
+	vgic_sanitise_inner_cacheability(reg,
+					 GICR_PROPBASER_CACHEABILITY_SHIFT);
+	vgic_sanitise_outer_cacheability(reg, 56);
+}
+
+#define PROPBASER_RES0_MASK 0xf8f0000000000060
+#define PENDBASER_RES0_MASK 0xb8f000000000f07f
+
+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;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
+	dist->propbaser &= ~PROPBASER_RES0_MASK;
+	vgic_sanitise_redist_baser(&dist->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;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
+					       addr & 4, len, val);
+	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
+	vgic_sanitise_redist_baser(&vgic_cpu->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
@@ -226,10 +334,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,
-- 
2.8.2

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

* [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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/vgic/vgic.h            |  13 +++++
 include/linux/irqchip/arm-gic-v3.h |   1 +
 virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 124 insertions(+), 2 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index e488a369..dc7f2fd 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -146,6 +146,14 @@ struct vgic_dist {
 	struct vgic_irq		*spis;
 
 	struct vgic_io_device	dist_iodev;
+
+	/*
+	 * Contains the address 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;
+
+	/* Points to the LPI pending tables for the redistributor */
+	u64 pendbaser;
+
+	bool lpis_enabled;
 };
 
 int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index bfbd707..64e8c70 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -124,6 +124,7 @@
 #define GICR_PROPBASER_WaWb		(5U << 7)
 #define GICR_PROPBASER_RaWaWt		(6U << 7)
 #define GICR_PROPBASER_RaWaWb		(7U << 7)
+#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
 #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
 #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index c38302d..8cd7190 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)
 {
@@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+/* We don't have any constraints about the shareability attributes. */
+static void vgic_sanitise_shareability(u64 *reg)
+{
+}
+
+#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
+#define GIC_CACHE_PROP_MASK \
+	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
+
+static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
+{
+	switch (*reg >> reg_shift) {
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
+		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
+		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
+		break;
+	default:
+		/* We are fine with the other attributes. */
+		break;
+	}
+}
+
+static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
+{
+	switch (*reg >> reg_shift) {
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
+	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
+		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
+		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
+		break;
+	default:
+		/* We are fine with the other attributes. */
+		break;
+	}
+}
+
+static void vgic_sanitise_redist_baser(u64 *reg)
+{
+	vgic_sanitise_shareability(reg);
+	vgic_sanitise_inner_cacheability(reg,
+					 GICR_PROPBASER_CACHEABILITY_SHIFT);
+	vgic_sanitise_outer_cacheability(reg, 56);
+}
+
+#define PROPBASER_RES0_MASK 0xf8f0000000000060
+#define PENDBASER_RES0_MASK 0xb8f000000000f07f
+
+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;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
+	dist->propbaser &= ~PROPBASER_RES0_MASK;
+	vgic_sanitise_redist_baser(&dist->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;
+
+	/* Storing a value with LPIs already enabled is undefined */
+	if (vgic_cpu->lpis_enabled)
+		return;
+
+	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
+					       addr & 4, len, val);
+	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
+	vgic_sanitise_redist_baser(&vgic_cpu->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
@@ -226,10 +334,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,
-- 
2.8.2

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

* [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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/vgic/vgic.h            |  13 ++++-
 include/linux/irqchip/arm-gic-v3.h |   1 +
 virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
 virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
 virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
 virt/kvm/arm/vgic/vgic.h           |   7 +++
 8 files changed, 187 insertions(+), 9 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index a7a958c..2755d17 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -30,6 +30,7 @@ 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
 else
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index dc7f2fd..a66ba9a 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -111,12 +111,23 @@ struct vgic_register_region;
 
 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;
 	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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 64e8c70..92d5da8 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -176,6 +176,7 @@
 #define GITS_CWRITER			0x0088
 #define GITS_CREADR			0x0090
 #define GITS_BASER			0x0100
+#define GITS_IDREGS_BASE		0xffd0
 #define GITS_PIDR2			GICR_PIDR2
 
 #define GITS_TRANSLATER			0x10040
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
new file mode 100644
index 0000000..c02d9df
--- /dev/null
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -0,0 +1,107 @@
+/*
+ * 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,					\
+	.read = NULL,						\
+	.write = NULL,						\
+	.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 */
+}
+
+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 vits_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->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;
+}
+
+void vits_destroy(struct kvm *kvm, struct vgic_its *its)
+{
+
+	its->enabled = false;
+}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 8cd7190..4eee0d7 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)
 {
@@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 	vgic_put_irq(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)
 {
@@ -325,7 +361,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,
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 4050c1c..c98a4fd 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -461,8 +461,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);
@@ -471,8 +470,15 @@ 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);
+	if (region->read) {
+		struct kvm_vcpu *r_vcpu;
+
+		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
+		data = region->read(r_vcpu, addr, len);
+	} else if (region->its_read) {
+		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
+	}
+
 	vgic_data_host_to_mmio_bus(val, len, data);
 	return 0;
 }
@@ -482,7 +488,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,
@@ -493,8 +498,14 @@ 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);
+	if (region->write) {
+		struct kvm_vcpu *r_vcpu;
+
+		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
+		region->write(r_vcpu, addr, len, data);
+	} else if (region->its_write) {
+		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
+	}
 	return 0;
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 8509014..6dfc2f0 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -25,6 +25,10 @@ struct vgic_register_region {
 			      unsigned int len);
 	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
 		      unsigned long val);
+	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
+				  gpa_t addr, unsigned int len);
+	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 fa2d225..b09590d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -76,6 +76,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)
 {
@@ -127,6 +128,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.8.2

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

* [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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/vgic/vgic.h            |  13 ++++-
 include/linux/irqchip/arm-gic-v3.h |   1 +
 virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
 virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
 virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
 virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
 virt/kvm/arm/vgic/vgic.h           |   7 +++
 8 files changed, 187 insertions(+), 9 deletions(-)
 create mode 100644 virt/kvm/arm/vgic/vgic-its.c

diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index a7a958c..2755d17 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -30,6 +30,7 @@ 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
 else
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index dc7f2fd..a66ba9a 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -111,12 +111,23 @@ struct vgic_register_region;
 
 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;
 	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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 64e8c70..92d5da8 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -176,6 +176,7 @@
 #define GITS_CWRITER			0x0088
 #define GITS_CREADR			0x0090
 #define GITS_BASER			0x0100
+#define GITS_IDREGS_BASE		0xffd0
 #define GITS_PIDR2			GICR_PIDR2
 
 #define GITS_TRANSLATER			0x10040
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
new file mode 100644
index 0000000..c02d9df
--- /dev/null
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -0,0 +1,107 @@
+/*
+ * 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,					\
+	.read = NULL,						\
+	.write = NULL,						\
+	.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 */
+}
+
+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 vits_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->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;
+}
+
+void vits_destroy(struct kvm *kvm, struct vgic_its *its)
+{
+
+	its->enabled = false;
+}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 8cd7190..4eee0d7 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)
 {
@@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
 	vgic_put_irq(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)
 {
@@ -325,7 +361,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,
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index 4050c1c..c98a4fd 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -461,8 +461,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);
@@ -471,8 +470,15 @@ 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);
+	if (region->read) {
+		struct kvm_vcpu *r_vcpu;
+
+		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
+		data = region->read(r_vcpu, addr, len);
+	} else if (region->its_read) {
+		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
+	}
+
 	vgic_data_host_to_mmio_bus(val, len, data);
 	return 0;
 }
@@ -482,7 +488,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,
@@ -493,8 +498,14 @@ 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);
+	if (region->write) {
+		struct kvm_vcpu *r_vcpu;
+
+		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
+		region->write(r_vcpu, addr, len, data);
+	} else if (region->its_write) {
+		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
+	}
 	return 0;
 }
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 8509014..6dfc2f0 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -25,6 +25,10 @@ struct vgic_register_region {
 			      unsigned int len);
 	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
 		      unsigned long val);
+	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
+				  gpa_t addr, unsigned int len);
+	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 fa2d225..b09590d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -76,6 +76,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)
 {
@@ -127,6 +128,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.8.2

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

* [PATCH v6 08/15] KVM: arm64: introduce new KVM ITS device
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, 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 malloc 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 MMIO accesses.

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/vgic/vgic.h                        |   1 +
 include/uapi/linux/kvm.h                       |   2 +
 virt/kvm/arm/vgic/vgic-its.c                   | 131 ++++++++++++++++++++++++-
 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, 168 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 a268c85..f4a953e 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..f8c257b 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		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/vgic/vgic.h b/include/kvm/vgic/vgic.h
index a66ba9a..979d8c3 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -158,6 +158,7 @@ struct vgic_dist {
 
 	struct vgic_io_device	dist_iodev;
 
+	bool			has_its;
 	/*
 	 * Contains the address 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 c02d9df..4ae3c82 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>
 
@@ -81,7 +82,7 @@ struct vgic_register_region its_registers[] = {
 		VGIC_ACCESS_32bit),
 };
 
-int vits_register(struct kvm *kvm, struct vgic_its *its)
+static int vits_register(struct kvm *kvm, struct vgic_its *its)
 {
 	struct vgic_io_device *iodev = &its->iodev;
 	int ret;
@@ -100,8 +101,134 @@ int vits_register(struct kvm *kvm, struct vgic_its *its)
 	return ret;
 }
 
-void vits_destroy(struct kvm *kvm, struct vgic_its *its)
+static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 {
 
 	its->enabled = false;
 }
+
+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->enabled = false;
+
+	dev->private = its;
+
+	return 0;
+}
+
+static void vgic_its_destroy(struct kvm_device *dev)
+{
+	struct vgic_its *its = dev->private;
+
+	vits_destroy(dev->kvm, its);
+
+	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 (copy_from_user(&addr, uaddr, sizeof(addr)))
+			return -EFAULT;
+
+		ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base,
+					addr, SZ_64K);
+		if (ret)
+			return ret;
+
+		its->vgic_its_base = addr;
+
+		return 0;
+	}
+	case KVM_DEV_ARM_VGIC_GRP_CTRL:
+		switch (attr->attr) {
+		case KVM_DEV_ARM_VGIC_CTRL_INIT:
+			return vits_register(dev->kvm, its);
+		}
+		break;
+	}
+	return -ENXIO;
+}
+
+static int vgic_its_get_attr(struct kvm_device *dev,
+			     struct kvm_device_attr *attr)
+{
+	switch (attr->group) {
+	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
+		struct vgic_its *its = dev->private;
+		u64 addr = its->vgic_its_base;
+		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
+		unsigned long type = (unsigned long)attr->attr;
+
+		if (type != KVM_VGIC_ITS_ADDR_TYPE)
+			return -ENODEV;
+
+		if (copy_to_user(uaddr, &addr, sizeof(addr)))
+			return -EFAULT;
+		break;
+	default:
+		return -ENXIO;
+	}
+	}
+
+	return 0;
+}
+
+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 4eee0d7..3a72308 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 b09590d..24150f7 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -46,6 +46,9 @@ static inline void vgic_put_irq(struct vgic_irq *irq)
 bool vgic_queue_irq_put(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);
@@ -77,6 +80,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)
 {
@@ -134,6 +138,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.8.2


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

* [PATCH v6 08/15] KVM: arm64: introduce new KVM ITS device
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 malloc 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 MMIO accesses.

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/vgic/vgic.h                        |   1 +
 include/uapi/linux/kvm.h                       |   2 +
 virt/kvm/arm/vgic/vgic-its.c                   | 131 ++++++++++++++++++++++++-
 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, 168 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 a268c85..f4a953e 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..f8c257b 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		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/vgic/vgic.h b/include/kvm/vgic/vgic.h
index a66ba9a..979d8c3 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -158,6 +158,7 @@ struct vgic_dist {
 
 	struct vgic_io_device	dist_iodev;
 
+	bool			has_its;
 	/*
 	 * Contains the address 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 c02d9df..4ae3c82 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>
 
@@ -81,7 +82,7 @@ struct vgic_register_region its_registers[] = {
 		VGIC_ACCESS_32bit),
 };
 
-int vits_register(struct kvm *kvm, struct vgic_its *its)
+static int vits_register(struct kvm *kvm, struct vgic_its *its)
 {
 	struct vgic_io_device *iodev = &its->iodev;
 	int ret;
@@ -100,8 +101,134 @@ int vits_register(struct kvm *kvm, struct vgic_its *its)
 	return ret;
 }
 
-void vits_destroy(struct kvm *kvm, struct vgic_its *its)
+static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 {
 
 	its->enabled = false;
 }
+
+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->enabled = false;
+
+	dev->private = its;
+
+	return 0;
+}
+
+static void vgic_its_destroy(struct kvm_device *dev)
+{
+	struct vgic_its *its = dev->private;
+
+	vits_destroy(dev->kvm, its);
+
+	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 (copy_from_user(&addr, uaddr, sizeof(addr)))
+			return -EFAULT;
+
+		ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base,
+					addr, SZ_64K);
+		if (ret)
+			return ret;
+
+		its->vgic_its_base = addr;
+
+		return 0;
+	}
+	case KVM_DEV_ARM_VGIC_GRP_CTRL:
+		switch (attr->attr) {
+		case KVM_DEV_ARM_VGIC_CTRL_INIT:
+			return vits_register(dev->kvm, its);
+		}
+		break;
+	}
+	return -ENXIO;
+}
+
+static int vgic_its_get_attr(struct kvm_device *dev,
+			     struct kvm_device_attr *attr)
+{
+	switch (attr->group) {
+	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
+		struct vgic_its *its = dev->private;
+		u64 addr = its->vgic_its_base;
+		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
+		unsigned long type = (unsigned long)attr->attr;
+
+		if (type != KVM_VGIC_ITS_ADDR_TYPE)
+			return -ENODEV;
+
+		if (copy_to_user(uaddr, &addr, sizeof(addr)))
+			return -EFAULT;
+		break;
+	default:
+		return -ENXIO;
+	}
+	}
+
+	return 0;
+}
+
+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 4eee0d7..3a72308 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 b09590d..24150f7 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -46,6 +46,9 @@ static inline void vgic_put_irq(struct vgic_irq *irq)
 bool vgic_queue_irq_put(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);
@@ -77,6 +80,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)
 {
@@ -134,6 +138,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.8.2

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

* [PATCH v6 09/15] KVM: arm64: implement basic ITS register handlers
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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/vgic/vgic.h            |  15 ++
 include/linux/irqchip/arm-gic-v3.h |  11 ++
 virt/kvm/arm/vgic/vgic-its.c       | 343 +++++++++++++++++++++++++++++++++++--
 virt/kvm/arm/vgic/vgic-mmio-v3.c   |  15 +-
 virt/kvm/arm/vgic/vgic-mmio.h      |  10 ++
 5 files changed, 380 insertions(+), 14 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 979d8c3..891775e 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/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
@@ -126,6 +127,20 @@ struct vgic_its {
 
 	bool			enabled;
 	struct vgic_io_device	iodev;
+
+	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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 92d5da8..fe5a7fe 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -177,16 +177,26 @@
 #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_TYPER_HWCOLLCNT_SHIFT	24
 
 #define GITS_CBASER_VALID		(1UL << 63)
 #define GITS_CBASER_nCnB		(0UL << 59)
@@ -214,6 +224,7 @@
 #define GITS_BASER_WaWb			(5UL << 59)
 #define GITS_BASER_RaWaWt		(6UL << 59)
 #define GITS_BASER_RaWaWb		(7UL << 59)
+#define GITS_BASER_CACHEABILITY_SHIFT	(59)
 #define GITS_BASER_CACHEABILITY_MASK	(7UL << 59)
 #define GITS_BASER_TYPE_SHIFT		(56)
 #define GITS_BASER_TYPE(r)		(((r) >> GITS_BASER_TYPE_SHIFT) & 7)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 4ae3c82..d7f8834 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,289 @@
 #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)-1)
+
+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))
+#define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
+
+#define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
+
+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.
+	 * As we hold all LPI mapping related data structures in the kernel
+	 * (mimicing what the spec describes as "held in hardware"), we can
+	 * claim to support a high number of "hardware" mapped collections
+	 * (since we use linked lists to store them).
+	 * However to avoid memory waste, we keep the number of IDBits and
+	 * DevBits low - as least for the time being.
+	 */
+	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;
+	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;
+}
+
+static void its_free_itte(struct its_itte *itte)
+{
+	list_del(&itte->itte_list);
+	kfree(itte);
+}
+
+static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	return -ENODEV;
+}
+
+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);
+	vgic_sanitise_its_baser(&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)
+
+/*
+ * 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;
+	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, 32);
+		if (ret) {
+			/*
+			 * Gah, we are screwed. Either the guest programmed
+			 * bogus values in CBASER or something else went
+			 * wrong from which we cannot easily recover.
+			 * Reset CWRITER to the command that we have finished
+			 * processing and return.
+			 */
+			its->cwriter = its->creadr;
+			break;
+		}
+		vits_handle_command(kvm, its, cmd_buf);
+
+		its->creadr += 32;
+		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	\
+	((0x1fLL << GITS_BASER_ENTRY_SIZE_SHIFT) | \
+	 (0x07LL << GITS_BASER_TYPE_SHIFT))
+static void vgic_mmio_write_its_baser(struct kvm *kvm,
+				      struct vgic_its *its,
+				      gpa_t addr, unsigned int len,
+				      unsigned long val)
+{
+	u64 reg, *regptr;
+	u64 entry_size, device_type;
+
+	/* 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;
+		break;
+	default:
+		return;
+	}
+
+	reg = update_64bit_reg(*regptr, addr & 7, len, val);
+	reg &= ~GITS_BASER_RO_MASK;
+	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+	reg |= device_type << GITS_BASER_TYPE_SHIFT;
+	vgic_sanitise_its_baser(&reg);
+
+	*regptr = reg;
+}
+
 #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
 {								\
 	.reg_offset = off,					\
@@ -43,8 +327,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;
 }
@@ -57,28 +341,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
 
 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),
 };
 
@@ -103,10 +387,40 @@ static int vits_register(struct kvm *kvm, struct vgic_its *its)
 
 static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 {
+	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(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);
 
 	its->enabled = false;
 }
 
+#define INITIAL_BASER_VALUE	(GITS_BASER_WaWb | GITS_BASER_PAGE_SIZE_64K)
+
 static int vgic_its_create(struct kvm_device *dev, u32 type)
 {
 	struct vgic_its *its;
@@ -118,11 +432,20 @@ 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->enabled = false;
 
+	its->baser_device_table = INITIAL_BASER_VALUE;
+	its->baser_coll_table = INITIAL_BASER_VALUE;
+
 	dev->private = its;
 
 	return 0;
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 3a72308..da74d67 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;
@@ -239,6 +239,13 @@ static void vgic_sanitise_redist_baser(u64 *reg)
 	vgic_sanitise_outer_cacheability(reg, 56);
 }
 
+void vgic_sanitise_its_baser(u64 *reg)
+{
+	vgic_sanitise_shareability(reg);
+	vgic_sanitise_inner_cacheability(reg, GITS_BASER_CACHEABILITY_SHIFT);
+	vgic_sanitise_outer_cacheability(reg, 53);
+}
+
 #define PROPBASER_RES0_MASK 0xf8f0000000000060
 #define PENDBASER_RES0_MASK 0xb8f000000000f07f
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 6dfc2f0..f6fd662 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -91,6 +91,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);
 
@@ -151,4 +157,8 @@ 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
+void vgic_sanitise_its_baser(u64 *reg);
+#endif
+
 #endif
-- 
2.8.2

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

* [PATCH v6 09/15] KVM: arm64: implement basic ITS register handlers
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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/vgic/vgic.h            |  15 ++
 include/linux/irqchip/arm-gic-v3.h |  11 ++
 virt/kvm/arm/vgic/vgic-its.c       | 343 +++++++++++++++++++++++++++++++++++--
 virt/kvm/arm/vgic/vgic-mmio-v3.c   |  15 +-
 virt/kvm/arm/vgic/vgic-mmio.h      |  10 ++
 5 files changed, 380 insertions(+), 14 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 979d8c3..891775e 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/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
@@ -126,6 +127,20 @@ struct vgic_its {
 
 	bool			enabled;
 	struct vgic_io_device	iodev;
+
+	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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 92d5da8..fe5a7fe 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -177,16 +177,26 @@
 #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_TYPER_HWCOLLCNT_SHIFT	24
 
 #define GITS_CBASER_VALID		(1UL << 63)
 #define GITS_CBASER_nCnB		(0UL << 59)
@@ -214,6 +224,7 @@
 #define GITS_BASER_WaWb			(5UL << 59)
 #define GITS_BASER_RaWaWt		(6UL << 59)
 #define GITS_BASER_RaWaWb		(7UL << 59)
+#define GITS_BASER_CACHEABILITY_SHIFT	(59)
 #define GITS_BASER_CACHEABILITY_MASK	(7UL << 59)
 #define GITS_BASER_TYPE_SHIFT		(56)
 #define GITS_BASER_TYPE(r)		(((r) >> GITS_BASER_TYPE_SHIFT) & 7)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 4ae3c82..d7f8834 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,289 @@
 #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)-1)
+
+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))
+#define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
+
+#define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
+
+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.
+	 * As we hold all LPI mapping related data structures in the kernel
+	 * (mimicing what the spec describes as "held in hardware"), we can
+	 * claim to support a high number of "hardware" mapped collections
+	 * (since we use linked lists to store them).
+	 * However to avoid memory waste, we keep the number of IDBits and
+	 * DevBits low - as least for the time being.
+	 */
+	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;
+	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;
+}
+
+static void its_free_itte(struct its_itte *itte)
+{
+	list_del(&itte->itte_list);
+	kfree(itte);
+}
+
+static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	return -ENODEV;
+}
+
+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);
+	vgic_sanitise_its_baser(&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)
+
+/*
+ * 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, 32);
+		if (ret) {
+			/*
+			 * Gah, we are screwed. Either the guest programmed
+			 * bogus values in CBASER or something else went
+			 * wrong from which we cannot easily recover.
+			 * Reset CWRITER to the command that we have finished
+			 * processing and return.
+			 */
+			its->cwriter = its->creadr;
+			break;
+		}
+		vits_handle_command(kvm, its, cmd_buf);
+
+		its->creadr += 32;
+		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	\
+	((0x1fLL << GITS_BASER_ENTRY_SIZE_SHIFT) | \
+	 (0x07LL << GITS_BASER_TYPE_SHIFT))
+static void vgic_mmio_write_its_baser(struct kvm *kvm,
+				      struct vgic_its *its,
+				      gpa_t addr, unsigned int len,
+				      unsigned long val)
+{
+	u64 reg, *regptr;
+	u64 entry_size, device_type;
+
+	/* 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;
+		break;
+	default:
+		return;
+	}
+
+	reg = update_64bit_reg(*regptr, addr & 7, len, val);
+	reg &= ~GITS_BASER_RO_MASK;
+	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
+	reg |= device_type << GITS_BASER_TYPE_SHIFT;
+	vgic_sanitise_its_baser(&reg);
+
+	*regptr = reg;
+}
+
 #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
 {								\
 	.reg_offset = off,					\
@@ -43,8 +327,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;
 }
@@ -57,28 +341,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
 
 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),
 };
 
@@ -103,10 +387,40 @@ static int vits_register(struct kvm *kvm, struct vgic_its *its)
 
 static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 {
+	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(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);
 
 	its->enabled = false;
 }
 
+#define INITIAL_BASER_VALUE	(GITS_BASER_WaWb | GITS_BASER_PAGE_SIZE_64K)
+
 static int vgic_its_create(struct kvm_device *dev, u32 type)
 {
 	struct vgic_its *its;
@@ -118,11 +432,20 @@ 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->enabled = false;
 
+	its->baser_device_table = INITIAL_BASER_VALUE;
+	its->baser_coll_table = INITIAL_BASER_VALUE;
+
 	dev->private = its;
 
 	return 0;
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 3a72308..da74d67 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;
@@ -239,6 +239,13 @@ static void vgic_sanitise_redist_baser(u64 *reg)
 	vgic_sanitise_outer_cacheability(reg, 56);
 }
 
+void vgic_sanitise_its_baser(u64 *reg)
+{
+	vgic_sanitise_shareability(reg);
+	vgic_sanitise_inner_cacheability(reg, GITS_BASER_CACHEABILITY_SHIFT);
+	vgic_sanitise_outer_cacheability(reg, 53);
+}
+
 #define PROPBASER_RES0_MASK 0xf8f0000000000060
 #define PENDBASER_RES0_MASK 0xb8f000000000f07f
 
diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
index 6dfc2f0..f6fd662 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.h
+++ b/virt/kvm/arm/vgic/vgic-mmio.h
@@ -91,6 +91,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);
 
@@ -151,4 +157,8 @@ 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
+void vgic_sanitise_its_baser(u64 *reg);
+#endif
+
 #endif
-- 
2.8.2

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

* [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
iterates the list using RCU list primitives, so it's safe to be called
from an non-preemptible context like the KVM exit/entry path.
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_its_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/vgic/vgic.h       |  5 +++
 virt/kvm/arm/vgic/vgic-init.c |  3 ++
 virt/kvm/arm/vgic/vgic-its.c  | 92 ++++++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-v3.c   |  2 +
 virt/kvm/arm/vgic/vgic.c      |  9 +++--
 virt/kvm/arm/vgic/vgic.h      |  6 +++
 6 files changed, 111 insertions(+), 6 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 891775e..fdc2781 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -77,6 +77,8 @@ enum vgic_irq_config {
 
 struct vgic_irq {
 	spinlock_t irq_lock;		/* Protects the content of the struct */
+	struct list_head lpi_entry;	/* Used to link all LPIs together */
+	struct rcu_head  rcu;
 	struct list_head ap_list;
 
 	struct kvm_vcpu *vcpu;		/* SGIs and PPIs: The VCPU
@@ -181,6 +183,9 @@ struct vgic_dist {
 	 * GICv3 spec: 6.1.2 "LPI Configuration tables"
 	 */
 	u64			propbaser;
+
+	struct list_head	lpi_list_head;
+	struct mutex		lpi_list_lock;
 };
 
 struct vgic_v2_cpu_if {
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index c4a8df6..d22b094 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);
+	mutex_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 d7f8834..9ef951f7 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -22,6 +22,7 @@
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/rculist.h>
 #include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
@@ -33,6 +34,85 @@
 #include "vgic.h"
 #include "vgic-mmio.h"
 
+/*
+ * Iterate over the VM's list of mapped LPIs to find the one with a
+ * matching INTID, _lock that_ and return a pointer to the IRQ structure.
+ * Although the corresponding LPI could be unmapped (removed from the ITS
+ * data structures) even when someone holds the IRQ lock, the reference to
+ * this struct vgic_irq stays valid within the VGIC context at least until
+ * the lock gets dropped.
+ */
+struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq = NULL;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(irq, &dist->lpi_list_head, lpi_entry) {
+		if (irq->intid != intid)
+			continue;
+
+		spin_lock(&irq->irq_lock);
+		goto out_unlock;
+	}
+	irq = NULL;
+
+out_unlock:
+	rcu_read_unlock();
+
+	return irq;
+}
+
+static void vgic_free_irq_rcu(struct rcu_head *head)
+{
+	struct vgic_irq *irq = container_of(head, struct vgic_irq, rcu);
+
+	kfree(irq);
+}
+
+/*
+ * Drops a reference to an LPI. This is called whenever one ITS unmaps
+ * the LPI: it decreases the refcount and frees the structure if the
+ * refcount has reached zero.
+ * If this LPI is still referenced by another LPI and is on a VCPU at the
+ * moment, the removal will be deferred until those references go away as well.
+ */
+int vgic_drop_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq, *temp;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_for_each_entry_safe(irq, temp, &dist->lpi_list_head, lpi_entry) {
+		if (irq->intid != intid)
+			continue;
+
+		spin_lock(&irq->irq_lock);
+
+		irq->refcnt--;
+		if (!irq->refcnt) {
+			list_del_rcu(&irq->lpi_entry);
+			spin_unlock(&irq->irq_lock);
+			call_rcu(&irq->rcu, vgic_free_irq_rcu);
+		} else {
+			/*
+			 * If the refcnt is not zero here, this could be due to
+			 * two cases:
+			 * 1) it's referenced by another ITS: then the last ITS
+			 *    will free the IRQ once it is done with it
+			 * 2) it's on a VCPU at the moment: upon returning from
+			 *    the guest vgic_prune_ap_list() will free the IRQ
+			 */
+			spin_unlock(&irq->irq_lock);
+		}
+		break;
+	}
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return 0;
+}
+
 struct its_device {
 	struct list_head dev_list;
 
@@ -56,11 +136,17 @@ struct its_collection {
 struct its_itte {
 	struct list_head itte_list;
 
+	struct vgic_irq *irq;
 	struct its_collection *collection;
 	u32 lpi;
 	u32 event_id;
 };
 
+/* 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 BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
 
@@ -148,9 +234,11 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 	return 0;
 }
 
-static void its_free_itte(struct its_itte *itte)
+static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
 	list_del(&itte->itte_list);
+	vgic_drop_lpi(kvm, itte->lpi);
+
 	kfree(itte);
 }
 
@@ -404,7 +492,7 @@ static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 		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(itte);
+			its_free_itte(kvm, itte);
 		}
 		list_del(dev_cur);
 		kfree(dev);
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 21d84e9..74ab7a6 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;
 
 		/* Always preserve the active bit */
 		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 90f2543..4655ae4 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -63,12 +63,13 @@ 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)
+	if (intid < VGIC_MIN_LPI) {
+		WARN(1, "Looking up struct vgic_irq for reserved INTID");
 		return NULL;
+	}
 
-	WARN(1, "Looking up struct vgic_irq for reserved INTID");
-	return NULL;
+	/* LPIs */
+	return vgic_its_get_lpi(kvm, intid);
 }
 
 /**
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 24150f7..558252d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -81,6 +81,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);
+struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -142,6 +143,11 @@ static inline int kvm_vgic_register_its_device(void)
 {
 	return -ENODEV;
 }
+
+static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
+{
+	return NULL;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2


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

* [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
iterates the list using RCU list primitives, so it's safe to be called
from an non-preemptible context like the KVM exit/entry path.
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_its_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/vgic/vgic.h       |  5 +++
 virt/kvm/arm/vgic/vgic-init.c |  3 ++
 virt/kvm/arm/vgic/vgic-its.c  | 92 ++++++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic-v3.c   |  2 +
 virt/kvm/arm/vgic/vgic.c      |  9 +++--
 virt/kvm/arm/vgic/vgic.h      |  6 +++
 6 files changed, 111 insertions(+), 6 deletions(-)

diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index 891775e..fdc2781 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -77,6 +77,8 @@ enum vgic_irq_config {
 
 struct vgic_irq {
 	spinlock_t irq_lock;		/* Protects the content of the struct */
+	struct list_head lpi_entry;	/* Used to link all LPIs together */
+	struct rcu_head  rcu;
 	struct list_head ap_list;
 
 	struct kvm_vcpu *vcpu;		/* SGIs and PPIs: The VCPU
@@ -181,6 +183,9 @@ struct vgic_dist {
 	 * GICv3 spec: 6.1.2 "LPI Configuration tables"
 	 */
 	u64			propbaser;
+
+	struct list_head	lpi_list_head;
+	struct mutex		lpi_list_lock;
 };
 
 struct vgic_v2_cpu_if {
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index c4a8df6..d22b094 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);
+	mutex_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 d7f8834..9ef951f7 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -22,6 +22,7 @@
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/rculist.h>
 #include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
@@ -33,6 +34,85 @@
 #include "vgic.h"
 #include "vgic-mmio.h"
 
+/*
+ * Iterate over the VM's list of mapped LPIs to find the one with a
+ * matching INTID, _lock that_ and return a pointer to the IRQ structure.
+ * Although the corresponding LPI could be unmapped (removed from the ITS
+ * data structures) even when someone holds the IRQ lock, the reference to
+ * this struct vgic_irq stays valid within the VGIC context at least until
+ * the lock gets dropped.
+ */
+struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq = NULL;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(irq, &dist->lpi_list_head, lpi_entry) {
+		if (irq->intid != intid)
+			continue;
+
+		spin_lock(&irq->irq_lock);
+		goto out_unlock;
+	}
+	irq = NULL;
+
+out_unlock:
+	rcu_read_unlock();
+
+	return irq;
+}
+
+static void vgic_free_irq_rcu(struct rcu_head *head)
+{
+	struct vgic_irq *irq = container_of(head, struct vgic_irq, rcu);
+
+	kfree(irq);
+}
+
+/*
+ * Drops a reference to an LPI. This is called whenever one ITS unmaps
+ * the LPI: it decreases the refcount and frees the structure if the
+ * refcount has reached zero.
+ * If this LPI is still referenced by another LPI and is on a VCPU at the
+ * moment, the removal will be deferred until those references go away as well.
+ */
+int vgic_drop_lpi(struct kvm *kvm, u32 intid)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct vgic_irq *irq, *temp;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_for_each_entry_safe(irq, temp, &dist->lpi_list_head, lpi_entry) {
+		if (irq->intid != intid)
+			continue;
+
+		spin_lock(&irq->irq_lock);
+
+		irq->refcnt--;
+		if (!irq->refcnt) {
+			list_del_rcu(&irq->lpi_entry);
+			spin_unlock(&irq->irq_lock);
+			call_rcu(&irq->rcu, vgic_free_irq_rcu);
+		} else {
+			/*
+			 * If the refcnt is not zero here, this could be due to
+			 * two cases:
+			 * 1) it's referenced by another ITS: then the last ITS
+			 *    will free the IRQ once it is done with it
+			 * 2) it's on a VCPU at the moment: upon returning from
+			 *    the guest vgic_prune_ap_list() will free the IRQ
+			 */
+			spin_unlock(&irq->irq_lock);
+		}
+		break;
+	}
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return 0;
+}
+
 struct its_device {
 	struct list_head dev_list;
 
@@ -56,11 +136,17 @@ struct its_collection {
 struct its_itte {
 	struct list_head itte_list;
 
+	struct vgic_irq *irq;
 	struct its_collection *collection;
 	u32 lpi;
 	u32 event_id;
 };
 
+/* 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 BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
 
@@ -148,9 +234,11 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 	return 0;
 }
 
-static void its_free_itte(struct its_itte *itte)
+static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
 	list_del(&itte->itte_list);
+	vgic_drop_lpi(kvm, itte->lpi);
+
 	kfree(itte);
 }
 
@@ -404,7 +492,7 @@ static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
 		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(itte);
+			its_free_itte(kvm, itte);
 		}
 		list_del(dev_cur);
 		kfree(dev);
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 21d84e9..74ab7a6 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;
 
 		/* Always preserve the active bit */
 		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 90f2543..4655ae4 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -63,12 +63,13 @@ 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)
+	if (intid < VGIC_MIN_LPI) {
+		WARN(1, "Looking up struct vgic_irq for reserved INTID");
 		return NULL;
+	}
 
-	WARN(1, "Looking up struct vgic_irq for reserved INTID");
-	return NULL;
+	/* LPIs */
+	return vgic_its_get_lpi(kvm, intid);
 }
 
 /**
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 24150f7..558252d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -81,6 +81,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);
+struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -142,6 +143,11 @@ static inline int kvm_vgic_register_its_device(void)
 {
 	return -ENODEV;
 }
+
+static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
+{
+	return NULL;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2

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

* [PATCH v6 11/15] KVM: arm64: read initial LPI pending table
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

The LPI pending status for a GICv3 redistributor is held in a table
in (guest) memory. To achieve reasonable performance, we cache this
data in our struct vgic_irq. The initial pending state must be read
from guest memory upon enabling LPIs for this redistributor.

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

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 9ef951f7..ba2af85 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -149,6 +149,43 @@ struct its_itte {
 
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
+#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
+
+/*
+ * 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_dist *dist = &vcpu->kvm->arch.vgic;
+	struct vgic_irq *irq;
+	u8 pendmask;
+	int ret = 0;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry) {
+		int byte_offset, bit_nr;
+
+		byte_offset = irq->intid / BITS_PER_BYTE;
+		bit_nr = irq->intid % BITS_PER_BYTE;
+
+		ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
+				     &pendmask, 1);
+		if (ret)
+			goto out_unlock;
+
+		spin_lock(&irq->irq_lock);
+		irq->pending = pendmask & (1U << bit_nr);
+		vgic_queue_irq_put(vcpu->kvm, irq);
+	}
+
+out_unlock:
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return ret;
+}
 
 #define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
 
@@ -454,6 +491,12 @@ 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)
+{
+	its_sync_lpi_pending_table(vcpu);
+}
+
 static int vits_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 558252d..8c2105a 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)
@@ -82,6 +83,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);
 struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
+void vgic_enable_lpis(struct kvm_vcpu *vcpu);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -148,6 +150,10 @@ static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
 {
 	return NULL;
 }
+
+static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
+{
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2

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

* [PATCH v6 11/15] KVM: arm64: read initial LPI pending table
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 this
data in our struct vgic_irq. The initial pending state must be read
from guest memory upon enabling LPIs for this redistributor.

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

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 9ef951f7..ba2af85 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -149,6 +149,43 @@ struct its_itte {
 
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))
+#define PENDBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 16))
+
+/*
+ * 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_dist *dist = &vcpu->kvm->arch.vgic;
+	struct vgic_irq *irq;
+	u8 pendmask;
+	int ret = 0;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry) {
+		int byte_offset, bit_nr;
+
+		byte_offset = irq->intid / BITS_PER_BYTE;
+		bit_nr = irq->intid % BITS_PER_BYTE;
+
+		ret = kvm_read_guest(vcpu->kvm, pendbase + byte_offset,
+				     &pendmask, 1);
+		if (ret)
+			goto out_unlock;
+
+		spin_lock(&irq->irq_lock);
+		irq->pending = pendmask & (1U << bit_nr);
+		vgic_queue_irq_put(vcpu->kvm, irq);
+	}
+
+out_unlock:
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return ret;
+}
 
 #define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
 
@@ -454,6 +491,12 @@ 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)
+{
+	its_sync_lpi_pending_table(vcpu);
+}
+
 static int vits_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 558252d..8c2105a 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)
@@ -82,6 +83,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);
 struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
+void vgic_enable_lpis(struct kvm_vcpu *vcpu);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -148,6 +150,10 @@ static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
 {
 	return NULL;
 }
+
+static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
+{
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2

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

* [PATCH v6 12/15] KVM: arm64: allow updates of LPI configuration table
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

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 | 45 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index ba2af85..285cbe6 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -150,6 +150,51 @@ struct its_itte {
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 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_filtered(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_put(kvm, irq);
+	} else {
+		spin_unlock(&irq->irq_lock);
+	}
+
+	return 0;
+}
+
+/* Updates the priority and enable bit for a given LPI. */
+int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
+{
+	return update_lpi_config_filtered(kvm, irq, NULL);
+}
 
 /*
  * Scan the whole LPI pending table and sync the pending bit in there
-- 
2.8.2

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

* [PATCH v6 12/15] KVM: arm64: allow updates of LPI configuration table
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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 | 45 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index ba2af85..285cbe6 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -150,6 +150,51 @@ struct its_itte {
 #define CBASER_ADDRESS(x)	((x) & GENMASK_ULL(51, 12))
 #define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 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_filtered(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_put(kvm, irq);
+	} else {
+		spin_unlock(&irq->irq_lock);
+	}
+
+	return 0;
+}
+
+/* Updates the priority and enable bit for a given LPI. */
+int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
+{
+	return update_lpi_config_filtered(kvm, irq, NULL);
+}
 
 /*
  * Scan the whole LPI pending table and sync the pending bit in there
-- 
2.8.2

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

* [PATCH v6 13/15] KVM: arm64: implement ITS command queue command handlers
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

The connection between a device, an event ID, the LPI number and the
allocated 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 the ITS implementation use their 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 at most 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 a SError).
The INT command handler is missing at this point, as we gain the
capability of actually injecting MSIs into the guest only later on.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/linux/irqchip/arm-gic-v3.h |  19 +-
 virt/kvm/arm/vgic/vgic-its.c       | 594 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 610 insertions(+), 3 deletions(-)

diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index fe5a7fe..4b165e2 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -256,7 +256,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
@@ -267,6 +270,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)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 285cbe6..c2a4b88 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
+#include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
@@ -63,6 +64,45 @@ out_unlock:
 	return irq;
 }
 
+/*
+ * 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_its_get_lpi(kvm, intid);
+
+	if (irq) {
+		irq->refcnt++;
+		spin_unlock(&irq->irq_lock);
+
+		return irq;
+	}
+
+	irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL);
+
+	if (!irq)
+		return NULL;
+
+	INIT_LIST_HEAD(&irq->lpi_entry);
+	INIT_LIST_HEAD(&irq->ap_list);
+	spin_lock_init(&irq->irq_lock);
+
+	irq->config = VGIC_CONFIG_EDGE;
+	irq->refcnt = 1;
+	irq->intid = intid;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_add_tail_rcu(&irq->lpi_entry, &dist->lpi_list_head);
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return irq;
+}
+
 static void vgic_free_irq_rcu(struct rcu_head *head)
 {
 	struct vgic_irq *irq = container_of(head, struct vgic_irq, rcu);
@@ -142,6 +182,43 @@ 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 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 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) \
@@ -154,6 +231,22 @@ struct its_itte {
 
 #define GIC_LPI_OFFSET 8192
 
+/*
+ * Finds and returns a collection in the ITS collection table.
+ * Must be called with the its_lock 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)
 
@@ -191,12 +284,54 @@ static int update_lpi_config_filtered(struct kvm *kvm, struct vgic_irq *irq,
 }
 
 /* Updates the priority and enable bit for a given LPI. */
-int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
+static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
 {
 	return update_lpi_config_filtered(kvm, irq, NULL);
 }
 
 /*
+ * 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 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.
@@ -324,10 +459,465 @@ 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). */
+static int vits_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
+				   u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret = E_ITS_DISCARD_UNMAPPED_INTERRUPT;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+	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);
+		ret = 0;
+	}
+
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The MOVI command moves an ITTE to a different collection. */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_MOVI_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+	if (!its_is_collection_mapped(itte->collection)) {
+		ret = E_ITS_MOVI_UNMAPPED_COLLECTION;
+		goto out_unlock;
+	}
+
+	collection = find_collection(its, coll_id);
+	if (!its_is_collection_mapped(collection)) {
+		ret = E_ITS_MOVI_UNMAPPED_COLLECTION;
+		goto out_unlock;
+	}
+
+	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);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+static void vits_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. */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+
+	device = find_its_device(its, device_id);
+	if (!device) {
+		ret = E_ITS_MAPTI_UNMAPPED_DEVICE;
+		goto out_unlock;
+	}
+
+	collection = find_collection(its, coll_id);
+	if (!collection) {
+		new_coll = kzalloc(sizeof(struct its_collection), GFP_KERNEL);
+		if (!new_coll) {
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+	}
+
+	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))
+		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);
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+
+		itte->event_id	= event_id;
+		list_add_tail(&itte->itte_list, &device->itt_head);
+	}
+
+	if (!collection) {
+		collection = new_coll;
+		vits_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);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+
+	return 0;
+}
+
+/* Requires the its_lock to be held. */
+static void vits_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). */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+
+	device = find_its_device(its, device_id);
+	if (device)
+		vits_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)
+		goto out_unlock;
+
+	device = kzalloc(sizeof(struct its_device), GFP_KERNEL);
+	if (!device) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	device->device_id = device_id;
+	INIT_LIST_HEAD(&device->itt_head);
+
+	list_add_tail(&device->dev_list, &its->device_list);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The MAPC command maps collection IDs to redistributors. */
+static int vits_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;
+	int ret = 0;
+
+	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;
+
+	mutex_lock(&its->its_lock);
+
+	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)
+			goto out_unlock;
+
+		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) {
+				ret = -ENOMEM;
+				goto out_unlock;
+			}
+		}
+
+		vits_init_collection(its, collection, coll_id);
+		collection->target_addr = target_addr;
+		update_affinity_collection(kvm, its, collection);
+	}
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+
+	return ret;
+}
+
+/* The CLEAR command removes the pending state for a particular LPI. */
+static int vits_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its,
+				 u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret = 0;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_CLEAR_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+
+	itte->irq->pending = false;
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The INV command syncs the configuration bits from the memory table. */
+static int vits_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_INV_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+
+	ret = update_lpi_config(kvm, itte->irq);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/*
+ * 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.
+ */
+static int vits_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
+				  u64 *its_cmd)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct its_collection *collection;
+	struct kvm_vcpu *vcpu;
+	struct vgic_irq *irq;
+
+	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);
+
+	mutex_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry)
+		update_lpi_config_filtered(kvm, irq, vcpu);
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	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 vits_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);
+
+	mutex_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry) {
+		spin_lock(&irq->irq_lock);
+
+		if (irq->target_vcpu == vcpu1)
+			irq->target_vcpu = vcpu2;
+
+		spin_unlock(&irq->irq_lock);
+	}
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return 0;
+}
+
+/*
+ * This function is called with the its_cmd lock held, but the ITS data
+ * structure lock dropped. It is within the responsibility of the actual
+ * command handlers to take care of proper locking when needed.
+ */
 static int vits_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;
+
+	switch (cmd) {
+	case GITS_CMD_MAPD:
+		ret = vits_cmd_handle_mapd(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPC:
+		ret = vits_cmd_handle_mapc(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPI:
+		ret = vits_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MAPTI:
+		ret = vits_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MOVI:
+		ret = vits_cmd_handle_movi(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_DISCARD:
+		ret = vits_cmd_handle_discard(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_CLEAR:
+		ret = vits_cmd_handle_clear(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MOVALL:
+		ret = vits_cmd_handle_movall(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INV:
+		ret = vits_cmd_handle_inv(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INVALL:
+		ret = vits_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;
+	}
+
+	return ret;
 }
 
 static unsigned long vgic_mmio_read_its_cbaser(struct kvm *kvm,
-- 
2.8.2

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

* [PATCH v6 13/15] KVM: arm64: implement ITS command queue command handlers
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: linux-arm-kernel

The connection between a device, an event ID, the LPI number and the
allocated 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 the ITS implementation use their 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 at most 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 a SError).
The INT command handler is missing at this point, as we gain the
capability of actually injecting MSIs into the guest only later on.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 include/linux/irqchip/arm-gic-v3.h |  19 +-
 virt/kvm/arm/vgic/vgic-its.c       | 594 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 610 insertions(+), 3 deletions(-)

diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index fe5a7fe..4b165e2 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -256,7 +256,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
@@ -267,6 +270,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)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 285cbe6..c2a4b88 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
+#include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include <linux/irqchip/arm-gic-v3.h>
@@ -63,6 +64,45 @@ out_unlock:
 	return irq;
 }
 
+/*
+ * 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_its_get_lpi(kvm, intid);
+
+	if (irq) {
+		irq->refcnt++;
+		spin_unlock(&irq->irq_lock);
+
+		return irq;
+	}
+
+	irq = kzalloc(sizeof(struct vgic_irq), GFP_KERNEL);
+
+	if (!irq)
+		return NULL;
+
+	INIT_LIST_HEAD(&irq->lpi_entry);
+	INIT_LIST_HEAD(&irq->ap_list);
+	spin_lock_init(&irq->irq_lock);
+
+	irq->config = VGIC_CONFIG_EDGE;
+	irq->refcnt = 1;
+	irq->intid = intid;
+
+	mutex_lock(&dist->lpi_list_lock);
+	list_add_tail_rcu(&irq->lpi_entry, &dist->lpi_list_head);
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return irq;
+}
+
 static void vgic_free_irq_rcu(struct rcu_head *head)
 {
 	struct vgic_irq *irq = container_of(head, struct vgic_irq, rcu);
@@ -142,6 +182,43 @@ 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 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 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) \
@@ -154,6 +231,22 @@ struct its_itte {
 
 #define GIC_LPI_OFFSET 8192
 
+/*
+ * Finds and returns a collection in the ITS collection table.
+ * Must be called with the its_lock 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)
 
@@ -191,12 +284,54 @@ static int update_lpi_config_filtered(struct kvm *kvm, struct vgic_irq *irq,
 }
 
 /* Updates the priority and enable bit for a given LPI. */
-int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
+static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq)
 {
 	return update_lpi_config_filtered(kvm, irq, NULL);
 }
 
 /*
+ * 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 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.
@@ -324,10 +459,465 @@ 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). */
+static int vits_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
+				   u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret = E_ITS_DISCARD_UNMAPPED_INTERRUPT;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+	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);
+		ret = 0;
+	}
+
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The MOVI command moves an ITTE to a different collection. */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_MOVI_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+	if (!its_is_collection_mapped(itte->collection)) {
+		ret = E_ITS_MOVI_UNMAPPED_COLLECTION;
+		goto out_unlock;
+	}
+
+	collection = find_collection(its, coll_id);
+	if (!its_is_collection_mapped(collection)) {
+		ret = E_ITS_MOVI_UNMAPPED_COLLECTION;
+		goto out_unlock;
+	}
+
+	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);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+static void vits_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. */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+
+	device = find_its_device(its, device_id);
+	if (!device) {
+		ret = E_ITS_MAPTI_UNMAPPED_DEVICE;
+		goto out_unlock;
+	}
+
+	collection = find_collection(its, coll_id);
+	if (!collection) {
+		new_coll = kzalloc(sizeof(struct its_collection), GFP_KERNEL);
+		if (!new_coll) {
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+	}
+
+	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))
+		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);
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+
+		itte->event_id	= event_id;
+		list_add_tail(&itte->itte_list, &device->itt_head);
+	}
+
+	if (!collection) {
+		collection = new_coll;
+		vits_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);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+
+	return 0;
+}
+
+/* Requires the its_lock to be held. */
+static void vits_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). */
+static int vits_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;
+	int ret = 0;
+
+	mutex_lock(&its->its_lock);
+
+	device = find_its_device(its, device_id);
+	if (device)
+		vits_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)
+		goto out_unlock;
+
+	device = kzalloc(sizeof(struct its_device), GFP_KERNEL);
+	if (!device) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+
+	device->device_id = device_id;
+	INIT_LIST_HEAD(&device->itt_head);
+
+	list_add_tail(&device->dev_list, &its->device_list);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The MAPC command maps collection IDs to redistributors. */
+static int vits_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;
+	int ret = 0;
+
+	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;
+
+	mutex_lock(&its->its_lock);
+
+	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)
+			goto out_unlock;
+
+		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) {
+				ret = -ENOMEM;
+				goto out_unlock;
+			}
+		}
+
+		vits_init_collection(its, collection, coll_id);
+		collection->target_addr = target_addr;
+		update_affinity_collection(kvm, its, collection);
+	}
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+
+	return ret;
+}
+
+/* The CLEAR command removes the pending state for a particular LPI. */
+static int vits_cmd_handle_clear(struct kvm *kvm, struct vgic_its *its,
+				 u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret = 0;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_CLEAR_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+
+	itte->irq->pending = false;
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/* The INV command syncs the configuration bits from the memory table. */
+static int vits_cmd_handle_inv(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	u32 device_id;
+	u32 event_id;
+	struct its_itte *itte;
+	int ret;
+
+	device_id = its_cmd_get_deviceid(its_cmd);
+	event_id = its_cmd_get_id(its_cmd);
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, device_id, event_id);
+	if (!itte) {
+		ret = E_ITS_INV_UNMAPPED_INTERRUPT;
+		goto out_unlock;
+	}
+
+	ret = update_lpi_config(kvm, itte->irq);
+
+out_unlock:
+	mutex_unlock(&its->its_lock);
+	return ret;
+}
+
+/*
+ * 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.
+ */
+static int vits_cmd_handle_invall(struct kvm *kvm, struct vgic_its *its,
+				  u64 *its_cmd)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	u32 coll_id = its_cmd_get_collection(its_cmd);
+	struct its_collection *collection;
+	struct kvm_vcpu *vcpu;
+	struct vgic_irq *irq;
+
+	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);
+
+	mutex_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry)
+		update_lpi_config_filtered(kvm, irq, vcpu);
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	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 vits_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);
+
+	mutex_lock(&dist->lpi_list_lock);
+
+	list_for_each_entry(irq, &dist->lpi_list_head, lpi_entry) {
+		spin_lock(&irq->irq_lock);
+
+		if (irq->target_vcpu == vcpu1)
+			irq->target_vcpu = vcpu2;
+
+		spin_unlock(&irq->irq_lock);
+	}
+
+	mutex_unlock(&dist->lpi_list_lock);
+
+	return 0;
+}
+
+/*
+ * This function is called with the its_cmd lock held, but the ITS data
+ * structure lock dropped. It is within the responsibility of the actual
+ * command handlers to take care of proper locking when needed.
+ */
 static int vits_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;
+
+	switch (cmd) {
+	case GITS_CMD_MAPD:
+		ret = vits_cmd_handle_mapd(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPC:
+		ret = vits_cmd_handle_mapc(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MAPI:
+		ret = vits_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MAPTI:
+		ret = vits_cmd_handle_mapi(kvm, its, its_cmd, cmd);
+		break;
+	case GITS_CMD_MOVI:
+		ret = vits_cmd_handle_movi(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_DISCARD:
+		ret = vits_cmd_handle_discard(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_CLEAR:
+		ret = vits_cmd_handle_clear(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_MOVALL:
+		ret = vits_cmd_handle_movall(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INV:
+		ret = vits_cmd_handle_inv(kvm, its, its_cmd);
+		break;
+	case GITS_CMD_INVALL:
+		ret = vits_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;
+	}
+
+	return ret;
 }
 
 static unsigned long vgic_mmio_read_its_cbaser(struct kvm *kvm,
-- 
2.8.2

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

* [PATCH v6 14/15] KVM: arm64: implement MSI injection in ITS emulation
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, linux-arm-kernel

When userland wants to inject a MSI into the guest, it uses the
KVM_SIGNAL_MSI ioctl, which carries the doorbell address along with
the payload and the device ID.
We convert this into an MMIO write to the ITS translation register,
so we can use the knowledge of the kvm_io_bus framework about the
different ITSes and magically end up in the right ITS.
The device ID is combined with the payload into a 64-bit write.
Inside the handler we use our wrapper functions to iterate the linked
lists and find the proper Interrupt Translation Table Entry and thus
the corresponding struct vgic_irq to finally set the pending bit.
We provide a VGIC emulation model specific routine for the actual
MSI injection. The wrapper functions return an error for models not
(yet) implementing MSIs (like the GICv2 emulation).
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 | 72 +++++++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic.h     |  6 ++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index c2a4b88..3b7adee 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -451,6 +451,56 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 	return 0;
 }
 
+static void vgic_mmio_write_its_translater(struct kvm *kvm,
+					   struct vgic_its *its,
+					   gpa_t addr, unsigned int len,
+					   unsigned long val)
+{
+	struct its_itte *itte;
+	u32 data = val & 0xffffffff, devid = val >> 32;
+
+	if (!its->enabled)
+		return;
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, devid, data);
+	/* 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_put(kvm, itte->irq);
+		}
+	}
+
+	mutex_unlock(&its->its_lock);
+}
+
+/*
+ * Dispatches an incoming MSI request to the KVM IO bus, which will redirect
+ * it for us to the proper ITS and the translation register write handler.
+ */
+int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	u64 address, data;
+
+	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;
+	data = msi->data | ((u64)msi->devid << 32);
+
+	return kvm_io_bus_write(kvm_get_vcpu(kvm, 0), KVM_MMIO_BUS,
+				address, 8, &data);
+}
+
 static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
 	list_del(&itte->itte_list);
@@ -869,6 +919,20 @@ static int vits_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
 	return 0;
 }
 
+#define ITS_DOORBELL_OFFSET (SZ_64K + 0x40)
+/* The INT command injects the LPI associated with that DevID/EvID pair. */
+static int vits_cmd_handle_int(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	u64 doorbell = its->vgic_its_base + ITS_DOORBELL_OFFSET;
+	u32 msi_data = its_cmd_get_id(its_cmd);
+	u64 msi_devid = its_cmd_get_deviceid(its_cmd);
+
+	vgic_mmio_write_its_translater(kvm, its, doorbell, 8,
+				       msi_devid << 32 | msi_data);
+	return 0;
+}
+
 /*
  * This function is called with the its_cmd lock held, but the ITS data
  * structure lock dropped. It is within the responsibility of the actual
@@ -905,6 +969,9 @@ static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
 	case GITS_CMD_MOVALL:
 		ret = vits_cmd_handle_movall(kvm, its, its_cmd);
 		break;
+	case GITS_CMD_INT:
+		ret = vits_cmd_handle_int(kvm, its, its_cmd);
+		break;
 	case GITS_CMD_INV:
 		ret = vits_cmd_handle_inv(kvm, its, its_cmd);
 		break;
@@ -1124,6 +1191,9 @@ struct vgic_register_region its_registers[] = {
 	REGISTER_ITS_DESC(GITS_IDREGS_BASE,
 		vgic_mmio_read_its_idregs, its_mmio_write_wi, 0x30,
 		VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(0x10040,
+		its_mmio_read_raz, vgic_mmio_write_its_translater, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 };
 
 /* This is called on setting the LPI enable bit in the redistributor. */
@@ -1145,7 +1215,7 @@ static int vits_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);
+				      SZ_128K, &iodev->dev);
 	mutex_unlock(&kvm->slots_lock);
 
 	return ret;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 8c2105a..26b7a0d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -84,6 +84,7 @@ bool vgic_has_its(struct kvm *kvm);
 int kvm_vgic_register_its_device(void);
 struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
 void vgic_enable_lpis(struct kvm_vcpu *vcpu);
+int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -154,6 +155,11 @@ static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
 static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
 {
 }
+
+static inline int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	return -ENODEV;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2


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

* [PATCH v6 14/15] KVM: arm64: implement MSI injection in ITS emulation
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: linux-arm-kernel

When userland wants to inject a MSI into the guest, it uses the
KVM_SIGNAL_MSI ioctl, which carries the doorbell address along with
the payload and the device ID.
We convert this into an MMIO write to the ITS translation register,
so we can use the knowledge of the kvm_io_bus framework about the
different ITSes and magically end up in the right ITS.
The device ID is combined with the payload into a 64-bit write.
Inside the handler we use our wrapper functions to iterate the linked
lists and find the proper Interrupt Translation Table Entry and thus
the corresponding struct vgic_irq to finally set the pending bit.
We provide a VGIC emulation model specific routine for the actual
MSI injection. The wrapper functions return an error for models not
(yet) implementing MSIs (like the GICv2 emulation).
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 | 72 +++++++++++++++++++++++++++++++++++++++++++-
 virt/kvm/arm/vgic/vgic.h     |  6 ++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index c2a4b88..3b7adee 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -451,6 +451,56 @@ static unsigned long vgic_mmio_read_its_idregs(struct kvm *kvm,
 	return 0;
 }
 
+static void vgic_mmio_write_its_translater(struct kvm *kvm,
+					   struct vgic_its *its,
+					   gpa_t addr, unsigned int len,
+					   unsigned long val)
+{
+	struct its_itte *itte;
+	u32 data = val & 0xffffffff, devid = val >> 32;
+
+	if (!its->enabled)
+		return;
+
+	mutex_lock(&its->its_lock);
+
+	itte = find_itte(its, devid, data);
+	/* 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_put(kvm, itte->irq);
+		}
+	}
+
+	mutex_unlock(&its->its_lock);
+}
+
+/*
+ * Dispatches an incoming MSI request to the KVM IO bus, which will redirect
+ * it for us to the proper ITS and the translation register write handler.
+ */
+int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	u64 address, data;
+
+	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;
+	data = msi->data | ((u64)msi->devid << 32);
+
+	return kvm_io_bus_write(kvm_get_vcpu(kvm, 0), KVM_MMIO_BUS,
+				address, 8, &data);
+}
+
 static void its_free_itte(struct kvm *kvm, struct its_itte *itte)
 {
 	list_del(&itte->itte_list);
@@ -869,6 +919,20 @@ static int vits_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
 	return 0;
 }
 
+#define ITS_DOORBELL_OFFSET (SZ_64K + 0x40)
+/* The INT command injects the LPI associated with that DevID/EvID pair. */
+static int vits_cmd_handle_int(struct kvm *kvm, struct vgic_its *its,
+			       u64 *its_cmd)
+{
+	u64 doorbell = its->vgic_its_base + ITS_DOORBELL_OFFSET;
+	u32 msi_data = its_cmd_get_id(its_cmd);
+	u64 msi_devid = its_cmd_get_deviceid(its_cmd);
+
+	vgic_mmio_write_its_translater(kvm, its, doorbell, 8,
+				       msi_devid << 32 | msi_data);
+	return 0;
+}
+
 /*
  * This function is called with the its_cmd lock held, but the ITS data
  * structure lock dropped. It is within the responsibility of the actual
@@ -905,6 +969,9 @@ static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
 	case GITS_CMD_MOVALL:
 		ret = vits_cmd_handle_movall(kvm, its, its_cmd);
 		break;
+	case GITS_CMD_INT:
+		ret = vits_cmd_handle_int(kvm, its, its_cmd);
+		break;
 	case GITS_CMD_INV:
 		ret = vits_cmd_handle_inv(kvm, its, its_cmd);
 		break;
@@ -1124,6 +1191,9 @@ struct vgic_register_region its_registers[] = {
 	REGISTER_ITS_DESC(GITS_IDREGS_BASE,
 		vgic_mmio_read_its_idregs, its_mmio_write_wi, 0x30,
 		VGIC_ACCESS_32bit),
+	REGISTER_ITS_DESC(0x10040,
+		its_mmio_read_raz, vgic_mmio_write_its_translater, 8,
+		VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
 };
 
 /* This is called on setting the LPI enable bit in the redistributor. */
@@ -1145,7 +1215,7 @@ static int vits_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);
+				      SZ_128K, &iodev->dev);
 	mutex_unlock(&kvm->slots_lock);
 
 	return ret;
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 8c2105a..26b7a0d 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -84,6 +84,7 @@ bool vgic_has_its(struct kvm *kvm);
 int kvm_vgic_register_its_device(void);
 struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid);
 void vgic_enable_lpis(struct kvm_vcpu *vcpu);
+int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
 #else
 static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
 {
@@ -154,6 +155,11 @@ static inline struct vgic_irq *vgic_its_get_lpi(struct kvm *kvm, u32 intid)
 static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
 {
 }
+
+static inline int vits_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	return -ENODEV;
+}
 #endif
 
 int kvm_register_vgic_device(unsigned long type);
-- 
2.8.2

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

* [PATCH v6 15/15] KVM: arm64: enable ITS emulation as a virtual MSI controller
  2016-06-17 12:08 ` Andre Przywara
@ 2016-06-17 12:08   ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, kvm, 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            | 10 ++++++++++
 include/kvm/vgic/vgic.h           |  5 +++++
 virt/kvm/arm/vgic.c               |  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 ++++++++
 8 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index bf76639..f60b137 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2156,7 +2156,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 c4f26ef..446686a 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 6ec9dfe..409d188 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -86,6 +86,16 @@ 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:
+#ifdef CONFIG_KVM_NEW_VGIC
+		if (!kvm)
+			r = -EINVAL;
+		else
+			r = kvm->arch.vgic.msis_require_devid;
+#else
+		r = -EINVAL;
+#endif
+		break;
 	default:
 		r = 0;
 	}
diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index fdc2781..f9ba335 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -153,6 +153,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 */
@@ -295,4 +298,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 /* __ASM_ARM_KVM_VGIC_VGIC_H */
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index c3bfbb9..26db30a 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -2438,3 +2438,8 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
 {
 	return 0;
 }
+
+int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	return -ENODEV;
+}
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index d22b094..af76e9c 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 da74d67..cad81ae 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);
@@ -157,9 +162,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,
@@ -173,6 +177,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 4655ae4..4deded6 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -639,3 +639,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 vits_inject_msi(kvm, msi);
+	else
+		return -ENODEV;
+}
-- 
2.8.2


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

* [PATCH v6 15/15] KVM: arm64: enable ITS emulation as a virtual MSI controller
@ 2016-06-17 12:08   ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-17 12:08 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            | 10 ++++++++++
 include/kvm/vgic/vgic.h           |  5 +++++
 virt/kvm/arm/vgic.c               |  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 ++++++++
 8 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index bf76639..f60b137 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2156,7 +2156,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 c4f26ef..446686a 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 6ec9dfe..409d188 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -86,6 +86,16 @@ 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:
+#ifdef CONFIG_KVM_NEW_VGIC
+		if (!kvm)
+			r = -EINVAL;
+		else
+			r = kvm->arch.vgic.msis_require_devid;
+#else
+		r = -EINVAL;
+#endif
+		break;
 	default:
 		r = 0;
 	}
diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
index fdc2781..f9ba335 100644
--- a/include/kvm/vgic/vgic.h
+++ b/include/kvm/vgic/vgic.h
@@ -153,6 +153,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 */
@@ -295,4 +298,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 /* __ASM_ARM_KVM_VGIC_VGIC_H */
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index c3bfbb9..26db30a 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -2438,3 +2438,8 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
 {
 	return 0;
 }
+
+int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
+{
+	return -ENODEV;
+}
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index d22b094..af76e9c 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 da74d67..cad81ae 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);
@@ -157,9 +162,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,
@@ -173,6 +177,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 4655ae4..4deded6 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -639,3 +639,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 vits_inject_msi(kvm, msi);
+	else
+		return -ENODEV;
+}
-- 
2.8.2

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

* Re: [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22  8:18     ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22  8:18 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: kvmarm, linux-arm-kernel, kvm

Hi Marc,

On 17/06/16 13:08, 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 to
> those structures. Since private IRQs and SPIs are statically allocated,
> the reqcount 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.
> Also vgic_get_irq() gets changed to take the irq_lock already, this is
> later needed to avoid a race between a potential LPI unmap and the
> window between us getting the pointer and locking the IRQ.
> This introduces vgic_put_irq(), which just does the unlock and makes
> the new locking sequence look more symmetrical.
> This approach deviates from the classical Linux refcounting with using
> atomic_* types and functions, because the users of vgic_get_irq() take
> the irq_lock anyway, so we just use an int and adjust the refcount while
> holding the lock.

As discussed verbally, I am almost done on an implementation that uses
the kernel's kref_* infrastructure to implement the ref-counting, on top
of the existing vgic_irq locking.
Since we consider the atomic inc/dec operations comparably cheap, that
shouldn't sacrifice performance, but will increase readability and
avoids nasty bugs in our own refcounting implementation by using well
tested and reviewed code.

Cheers,
Andre.

> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  include/kvm/vgic/vgic.h          |  1 +
>  virt/kvm/arm/vgic/vgic-init.c    |  2 +
>  virt/kvm/arm/vgic/vgic-mmio-v2.c | 21 +++++------
>  virt/kvm/arm/vgic/vgic-mmio-v3.c | 26 ++++++-------
>  virt/kvm/arm/vgic/vgic-mmio.c    | 55 ++++++++++++++++-----------
>  virt/kvm/arm/vgic/vgic-v2.c      |  4 +-
>  virt/kvm/arm/vgic/vgic-v3.c      |  4 +-
>  virt/kvm/arm/vgic/vgic.c         | 81 +++++++++++++++++++++++++---------------
>  virt/kvm/arm/vgic/vgic.h         |  7 +++-
>  9 files changed, 116 insertions(+), 85 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index 2f26f37..e488a369 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -96,6 +96,7 @@ struct vgic_irq {
>  	bool active;			/* not used for LPIs */
>  	bool enabled;
>  	bool hw;			/* Tied to HW IRQ */
> +	int refcnt;			/* Used only 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..c4a8df6 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;
> +		irq->refcnt = 1;
>  		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;
> +		irq->refcnt = 1;
>  		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..7bb3e94 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> @@ -97,11 +97,10 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
>  
>  		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  		irq->source |= 1U << source_vcpu->vcpu_id;
>  
> -		vgic_queue_irq_unlock(source_vcpu->kvm, irq);
> +		vgic_queue_irq_put(source_vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -116,6 +115,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(irq);
>  	}
>  
>  	return val;
> @@ -136,13 +137,11 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
>  		int target;
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->targets = (val >> (i * 8)) & 0xff;
>  		target = irq->targets ? __ffs(irq->targets) : 0;
>  		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -157,6 +156,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(irq);
>  	}
>  	return val;
>  }
> @@ -171,13 +172,11 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->source &= ~((val >> (i * 8)) & 0xff);
>  		if (!irq->source)
>  			irq->pending = false;
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -191,15 +190,13 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->source |= (val >> (i * 8)) & 0xff;
>  
>  		if (irq->source) {
>  			irq->pending = true;
> -			vgic_queue_irq_unlock(vcpu->kvm, irq);
> +			vgic_queue_irq_put(vcpu->kvm, irq);
>  		} else {
> -			spin_unlock(&irq->irq_lock);
> +			vgic_put_irq(irq);
>  		}
>  	}
>  }
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index fc7b6c9..c38302d 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(irq);
> +	return ret;
>  }
>  
>  static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
> @@ -102,16 +104,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>  		return;
>  
>  	/* The upper word is WI for us since we don't implement Aff3. */
> -	if (addr & 4)
> -		return;
> -
> -	spin_lock(&irq->irq_lock);
> -
> -	/* We only care about and preserve Aff0, Aff1 and Aff2. */
> -	irq->mpidr = val & GENMASK(23, 0);
> -	irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
> +	if (!(addr & 4)) {
> +		/* We only care about and preserve Aff0, Aff1 and Aff2. */
> +		irq->mpidr = val & GENMASK(23, 0);
> +		irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
> +	}
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  }
>  
>  static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
> @@ -441,9 +440,8 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
>  
>  		irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
> index 9f6fab7..4050c1c 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(irq);
>  	}
>  
>  	return value;
> @@ -71,9 +73,9 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->enabled = true;
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -87,11 +89,9 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->enabled = false;
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -108,6 +108,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
>  
>  		if (irq->pending)
>  			value |= (1U << i);
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
> @@ -123,12 +125,11 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  		if (irq->config == VGIC_CONFIG_LEVEL)
>  			irq->soft_pending = true;
>  
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -142,8 +143,6 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		if (irq->config == VGIC_CONFIG_LEVEL) {
>  			irq->soft_pending = false;
>  			irq->pending = irq->line_level;
> @@ -151,7 +150,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
>  			irq->pending = false;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -168,15 +167,17 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
>  
>  		if (irq->active)
>  			value |= (1U << i);
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
>  }
>  
> -static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
> -				    bool new_active_state)
> +static void vgic_mmio_change_active_put(struct kvm_vcpu *vcpu,
> +					struct vgic_irq *irq,
> +					bool new_active_state)
>  {
> -	spin_lock(&irq->irq_lock);
>  	/*
>  	 * If this virtual IRQ was written into a list register, we
>  	 * have to make sure the CPU that runs the VCPU thread has
> @@ -190,15 +191,17 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
>  	 * IRQ, so we release and re-acquire the spin_lock to let the
>  	 * other thread sync back the IRQ.
>  	 */
> +	irq->refcnt++;
>  	while (irq->vcpu && /* IRQ may have state in an LR somewhere */
>  	       irq->vcpu->cpu != -1) /* VCPU thread is running */
>  		cond_resched_lock(&irq->irq_lock);
>  
> +	irq->refcnt--;
>  	irq->active = new_active_state;
>  	if (new_active_state)
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	else
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  }
>  
>  /*
> @@ -241,7 +244,8 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
>  	vgic_change_active_prepare(vcpu, intid);
>  	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_mmio_change_active_put(vcpu, irq, false);
>  	}
>  	vgic_change_active_finish(vcpu, intid);
>  }
> @@ -256,7 +260,8 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
>  	vgic_change_active_prepare(vcpu, intid);
>  	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_mmio_change_active_put(vcpu, irq, true);
>  	}
>  	vgic_change_active_finish(vcpu, intid);
>  }
> @@ -272,6 +277,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(irq);
>  	}
>  
>  	return val;
> @@ -294,10 +301,10 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		/* 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(irq);
>  	}
>  }
>  
> @@ -313,6 +320,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
>  
>  		if (irq->config == VGIC_CONFIG_EDGE)
>  			value |= (2U << (i * 2));
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
> @@ -326,7 +335,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 +346,16 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
>  		if (intid + i < VGIC_NR_PRIVATE_IRQS)
>  			continue;
>  
> -		spin_lock(&irq->irq_lock);
> +		irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
> +
>  		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(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 80313de..2147576 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -94,8 +94,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
>  
>  		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		/* Always preserve the active bit */
>  		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
>  
> @@ -123,7 +121,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
>  			irq->pending = irq->line_level || irq->soft_pending;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
> index e48a22e..21d84e9 100644
> --- a/virt/kvm/arm/vgic/vgic-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-v3.c
> @@ -82,8 +82,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
>  			intid = val & GICH_LR_VIRTUALID;
>  		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		/* Always preserve the active bit */
>  		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
>  
> @@ -112,7 +110,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
>  			irq->pending = irq->line_level || irq->soft_pending;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
> index 69b61ab..90f2543 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];
> +		spin_lock(&irq->irq_lock);
> +		return irq;
> +	}
> +
> +	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
> +		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
> +		spin_lock(&irq->irq_lock);
> +		return irq;
> +	}
>  
>  	/* LPIs are not yet covered */
>  	if (intid >= VGIC_MIN_LPI)
> @@ -183,9 +190,10 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
>   * Needs to be entered with the IRQ lock already held, but will return
>   * with all locks dropped.
>   */
> -bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
> +bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq)
>  {
> -	struct kvm_vcpu *vcpu;
> +	struct kvm_vcpu *vcpu, *irq_vcpu = irq->target_vcpu;
> +	u32 intid = irq->intid;
>  
>  	DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&irq->irq_lock));
>  
> @@ -201,7 +209,7 @@ retry:
>  		 * not need to be inserted into an ap_list and there is also
>  		 * no more work for us to do.
>  		 */
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  		return false;
>  	}
>  
> @@ -209,12 +217,18 @@ retry:
>  	 * We must unlock the irq lock to take the ap_list_lock where
>  	 * we are going to insert this new pending interrupt.
>  	 */
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	/* someone can do stuff here, which we re-check below */
>  
>  	spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
> -	spin_lock(&irq->irq_lock);
> +	irq = vgic_get_irq(kvm, irq_vcpu, intid);
> +
> +	if (!irq) {
> +		/* The LPI has been unmapped, nothing left to do. */
> +		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
> +		return false;
> +	}
>  
>  	/*
>  	 * Did something change behind our backs?
> @@ -229,17 +243,21 @@ retry:
>  	 */
>  
>  	if (unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq))) {
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
>  
> -		spin_lock(&irq->irq_lock);
> +		irq = vgic_get_irq(kvm, irq_vcpu, intid);
> +		if (!irq)
> +			return false;
> +
>  		goto retry;
>  	}
>  
>  	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
> +	irq->refcnt++;
>  	irq->vcpu = vcpu;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  	spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
>  
>  	kvm_vcpu_kick(vcpu);
> @@ -269,14 +287,14 @@ 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(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(irq);
>  		return 0;
>  	}
>  
> @@ -287,7 +305,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
>  		irq->pending = true;
>  	}
>  
> -	vgic_queue_irq_unlock(kvm, irq);
> +	vgic_queue_irq_put(kvm, irq);
>  
>  	return 0;
>  }
> @@ -324,31 +342,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
>  
>  	BUG_ON(!irq);
>  
> -	spin_lock(&irq->irq_lock);
> -
>  	irq->hw = true;
>  	irq->hwintid = phys_irq;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(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;
>  
> -	spin_lock(&irq->irq_lock);
> +	irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
> +	BUG_ON(!irq);
>  
>  	irq->hw = false;
>  	irq->hwintid = 0;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	return 0;
>  }
> @@ -378,14 +393,21 @@ retry:
>  
>  		target_vcpu = vgic_target_oracle(irq);
>  
> -		if (!target_vcpu) {
> +		if (!target_vcpu || irq->refcnt == 1) {
> +			bool free_irq = false;
> +
>  			/*
>  			 * We don't need to process this interrupt any
>  			 * further, move it off the list.
>  			 */
>  			list_del(&irq->ap_list);
>  			irq->vcpu = NULL;
> +			irq->refcnt--;
> +			if (!irq->refcnt)
> +				free_irq = true;
>  			spin_unlock(&irq->irq_lock);
> +			if (free_irq)
> +				kfree(irq);
>  			continue;
>  		}
>  
> @@ -611,9 +633,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
>  	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
>  	bool map_is_active;
>  
> -	spin_lock(&irq->irq_lock);
>  	map_is_active = irq->hw && irq->active;
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	return map_is_active;
>  }
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index c752152..fa2d225 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -38,7 +38,12 @@ struct vgic_vmcr {
>  
>  struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  			      u32 intid);
> -bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
> +static inline void vgic_put_irq(struct vgic_irq *irq)
> +{
> +	spin_unlock(&irq->irq_lock);
> +}
> +
> +bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq);
>  void vgic_kick_vcpus(struct kvm *kvm);
>  
>  void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
> 

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

* [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
@ 2016-06-22  8:18     ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22  8:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On 17/06/16 13:08, 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 to
> those structures. Since private IRQs and SPIs are statically allocated,
> the reqcount 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.
> Also vgic_get_irq() gets changed to take the irq_lock already, this is
> later needed to avoid a race between a potential LPI unmap and the
> window between us getting the pointer and locking the IRQ.
> This introduces vgic_put_irq(), which just does the unlock and makes
> the new locking sequence look more symmetrical.
> This approach deviates from the classical Linux refcounting with using
> atomic_* types and functions, because the users of vgic_get_irq() take
> the irq_lock anyway, so we just use an int and adjust the refcount while
> holding the lock.

As discussed verbally, I am almost done on an implementation that uses
the kernel's kref_* infrastructure to implement the ref-counting, on top
of the existing vgic_irq locking.
Since we consider the atomic inc/dec operations comparably cheap, that
shouldn't sacrifice performance, but will increase readability and
avoids nasty bugs in our own refcounting implementation by using well
tested and reviewed code.

Cheers,
Andre.

> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  include/kvm/vgic/vgic.h          |  1 +
>  virt/kvm/arm/vgic/vgic-init.c    |  2 +
>  virt/kvm/arm/vgic/vgic-mmio-v2.c | 21 +++++------
>  virt/kvm/arm/vgic/vgic-mmio-v3.c | 26 ++++++-------
>  virt/kvm/arm/vgic/vgic-mmio.c    | 55 ++++++++++++++++-----------
>  virt/kvm/arm/vgic/vgic-v2.c      |  4 +-
>  virt/kvm/arm/vgic/vgic-v3.c      |  4 +-
>  virt/kvm/arm/vgic/vgic.c         | 81 +++++++++++++++++++++++++---------------
>  virt/kvm/arm/vgic/vgic.h         |  7 +++-
>  9 files changed, 116 insertions(+), 85 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index 2f26f37..e488a369 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -96,6 +96,7 @@ struct vgic_irq {
>  	bool active;			/* not used for LPIs */
>  	bool enabled;
>  	bool hw;			/* Tied to HW IRQ */
> +	int refcnt;			/* Used only 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..c4a8df6 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;
> +		irq->refcnt = 1;
>  		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;
> +		irq->refcnt = 1;
>  		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..7bb3e94 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
> @@ -97,11 +97,10 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
>  
>  		irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  		irq->source |= 1U << source_vcpu->vcpu_id;
>  
> -		vgic_queue_irq_unlock(source_vcpu->kvm, irq);
> +		vgic_queue_irq_put(source_vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -116,6 +115,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(irq);
>  	}
>  
>  	return val;
> @@ -136,13 +137,11 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
>  		int target;
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->targets = (val >> (i * 8)) & 0xff;
>  		target = irq->targets ? __ffs(irq->targets) : 0;
>  		irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -157,6 +156,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(irq);
>  	}
>  	return val;
>  }
> @@ -171,13 +172,11 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->source &= ~((val >> (i * 8)) & 0xff);
>  		if (!irq->source)
>  			irq->pending = false;
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -191,15 +190,13 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->source |= (val >> (i * 8)) & 0xff;
>  
>  		if (irq->source) {
>  			irq->pending = true;
> -			vgic_queue_irq_unlock(vcpu->kvm, irq);
> +			vgic_queue_irq_put(vcpu->kvm, irq);
>  		} else {
> -			spin_unlock(&irq->irq_lock);
> +			vgic_put_irq(irq);
>  		}
>  	}
>  }
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index fc7b6c9..c38302d 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(irq);
> +	return ret;
>  }
>  
>  static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
> @@ -102,16 +104,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>  		return;
>  
>  	/* The upper word is WI for us since we don't implement Aff3. */
> -	if (addr & 4)
> -		return;
> -
> -	spin_lock(&irq->irq_lock);
> -
> -	/* We only care about and preserve Aff0, Aff1 and Aff2. */
> -	irq->mpidr = val & GENMASK(23, 0);
> -	irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
> +	if (!(addr & 4)) {
> +		/* We only care about and preserve Aff0, Aff1 and Aff2. */
> +		irq->mpidr = val & GENMASK(23, 0);
> +		irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
> +	}
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  }
>  
>  static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
> @@ -441,9 +440,8 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
>  
>  		irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
> index 9f6fab7..4050c1c 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(irq);
>  	}
>  
>  	return value;
> @@ -71,9 +73,9 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->enabled = true;
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -87,11 +89,9 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		irq->enabled = false;
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -108,6 +108,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
>  
>  		if (irq->pending)
>  			value |= (1U << i);
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
> @@ -123,12 +125,11 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		irq->pending = true;
>  		if (irq->config == VGIC_CONFIG_LEVEL)
>  			irq->soft_pending = true;
>  
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	}
>  }
>  
> @@ -142,8 +143,6 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
>  	for_each_set_bit(i, &val, len * 8) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		if (irq->config == VGIC_CONFIG_LEVEL) {
>  			irq->soft_pending = false;
>  			irq->pending = irq->line_level;
> @@ -151,7 +150,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
>  			irq->pending = false;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> @@ -168,15 +167,17 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
>  
>  		if (irq->active)
>  			value |= (1U << i);
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
>  }
>  
> -static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
> -				    bool new_active_state)
> +static void vgic_mmio_change_active_put(struct kvm_vcpu *vcpu,
> +					struct vgic_irq *irq,
> +					bool new_active_state)
>  {
> -	spin_lock(&irq->irq_lock);
>  	/*
>  	 * If this virtual IRQ was written into a list register, we
>  	 * have to make sure the CPU that runs the VCPU thread has
> @@ -190,15 +191,17 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
>  	 * IRQ, so we release and re-acquire the spin_lock to let the
>  	 * other thread sync back the IRQ.
>  	 */
> +	irq->refcnt++;
>  	while (irq->vcpu && /* IRQ may have state in an LR somewhere */
>  	       irq->vcpu->cpu != -1) /* VCPU thread is running */
>  		cond_resched_lock(&irq->irq_lock);
>  
> +	irq->refcnt--;
>  	irq->active = new_active_state;
>  	if (new_active_state)
> -		vgic_queue_irq_unlock(vcpu->kvm, irq);
> +		vgic_queue_irq_put(vcpu->kvm, irq);
>  	else
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  }
>  
>  /*
> @@ -241,7 +244,8 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
>  	vgic_change_active_prepare(vcpu, intid);
>  	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_mmio_change_active_put(vcpu, irq, false);
>  	}
>  	vgic_change_active_finish(vcpu, intid);
>  }
> @@ -256,7 +260,8 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
>  	vgic_change_active_prepare(vcpu, intid);
>  	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_mmio_change_active_put(vcpu, irq, true);
>  	}
>  	vgic_change_active_finish(vcpu, intid);
>  }
> @@ -272,6 +277,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(irq);
>  	}
>  
>  	return val;
> @@ -294,10 +301,10 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
>  	for (i = 0; i < len; i++) {
>  		struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
>  
> -		spin_lock(&irq->irq_lock);
>  		/* 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(irq);
>  	}
>  }
>  
> @@ -313,6 +320,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
>  
>  		if (irq->config == VGIC_CONFIG_EDGE)
>  			value |= (2U << (i * 2));
> +
> +		vgic_put_irq(irq);
>  	}
>  
>  	return value;
> @@ -326,7 +335,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 +346,16 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
>  		if (intid + i < VGIC_NR_PRIVATE_IRQS)
>  			continue;
>  
> -		spin_lock(&irq->irq_lock);
> +		irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
> +
>  		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(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 80313de..2147576 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -94,8 +94,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
>  
>  		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		/* Always preserve the active bit */
>  		irq->active = !!(val & GICH_LR_ACTIVE_BIT);
>  
> @@ -123,7 +121,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
>  			irq->pending = irq->line_level || irq->soft_pending;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
> index e48a22e..21d84e9 100644
> --- a/virt/kvm/arm/vgic/vgic-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-v3.c
> @@ -82,8 +82,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
>  			intid = val & GICH_LR_VIRTUALID;
>  		irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
>  
> -		spin_lock(&irq->irq_lock);
> -
>  		/* Always preserve the active bit */
>  		irq->active = !!(val & ICH_LR_ACTIVE_BIT);
>  
> @@ -112,7 +110,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
>  			irq->pending = irq->line_level || irq->soft_pending;
>  		}
>  
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  	}
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
> index 69b61ab..90f2543 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];
> +		spin_lock(&irq->irq_lock);
> +		return irq;
> +	}
> +
> +	if (intid <= VGIC_MAX_SPI) {            /* SPIs */
> +		irq = &dist->spis[intid - VGIC_NR_PRIVATE_IRQS];
> +		spin_lock(&irq->irq_lock);
> +		return irq;
> +	}
>  
>  	/* LPIs are not yet covered */
>  	if (intid >= VGIC_MIN_LPI)
> @@ -183,9 +190,10 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
>   * Needs to be entered with the IRQ lock already held, but will return
>   * with all locks dropped.
>   */
> -bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
> +bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq)
>  {
> -	struct kvm_vcpu *vcpu;
> +	struct kvm_vcpu *vcpu, *irq_vcpu = irq->target_vcpu;
> +	u32 intid = irq->intid;
>  
>  	DEBUG_SPINLOCK_BUG_ON(!spin_is_locked(&irq->irq_lock));
>  
> @@ -201,7 +209,7 @@ retry:
>  		 * not need to be inserted into an ap_list and there is also
>  		 * no more work for us to do.
>  		 */
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  		return false;
>  	}
>  
> @@ -209,12 +217,18 @@ retry:
>  	 * We must unlock the irq lock to take the ap_list_lock where
>  	 * we are going to insert this new pending interrupt.
>  	 */
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	/* someone can do stuff here, which we re-check below */
>  
>  	spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
> -	spin_lock(&irq->irq_lock);
> +	irq = vgic_get_irq(kvm, irq_vcpu, intid);
> +
> +	if (!irq) {
> +		/* The LPI has been unmapped, nothing left to do. */
> +		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
> +		return false;
> +	}
>  
>  	/*
>  	 * Did something change behind our backs?
> @@ -229,17 +243,21 @@ retry:
>  	 */
>  
>  	if (unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq))) {
> -		spin_unlock(&irq->irq_lock);
> +		vgic_put_irq(irq);
>  		spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
>  
> -		spin_lock(&irq->irq_lock);
> +		irq = vgic_get_irq(kvm, irq_vcpu, intid);
> +		if (!irq)
> +			return false;
> +
>  		goto retry;
>  	}
>  
>  	list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
> +	irq->refcnt++;
>  	irq->vcpu = vcpu;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  	spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
>  
>  	kvm_vcpu_kick(vcpu);
> @@ -269,14 +287,14 @@ 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(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(irq);
>  		return 0;
>  	}
>  
> @@ -287,7 +305,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
>  		irq->pending = true;
>  	}
>  
> -	vgic_queue_irq_unlock(kvm, irq);
> +	vgic_queue_irq_put(kvm, irq);
>  
>  	return 0;
>  }
> @@ -324,31 +342,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
>  
>  	BUG_ON(!irq);
>  
> -	spin_lock(&irq->irq_lock);
> -
>  	irq->hw = true;
>  	irq->hwintid = phys_irq;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(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;
>  
> -	spin_lock(&irq->irq_lock);
> +	irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
> +	BUG_ON(!irq);
>  
>  	irq->hw = false;
>  	irq->hwintid = 0;
>  
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	return 0;
>  }
> @@ -378,14 +393,21 @@ retry:
>  
>  		target_vcpu = vgic_target_oracle(irq);
>  
> -		if (!target_vcpu) {
> +		if (!target_vcpu || irq->refcnt == 1) {
> +			bool free_irq = false;
> +
>  			/*
>  			 * We don't need to process this interrupt any
>  			 * further, move it off the list.
>  			 */
>  			list_del(&irq->ap_list);
>  			irq->vcpu = NULL;
> +			irq->refcnt--;
> +			if (!irq->refcnt)
> +				free_irq = true;
>  			spin_unlock(&irq->irq_lock);
> +			if (free_irq)
> +				kfree(irq);
>  			continue;
>  		}
>  
> @@ -611,9 +633,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
>  	struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
>  	bool map_is_active;
>  
> -	spin_lock(&irq->irq_lock);
>  	map_is_active = irq->hw && irq->active;
> -	spin_unlock(&irq->irq_lock);
> +	vgic_put_irq(irq);
>  
>  	return map_is_active;
>  }
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index c752152..fa2d225 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -38,7 +38,12 @@ struct vgic_vmcr {
>  
>  struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  			      u32 intid);
> -bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
> +static inline void vgic_put_irq(struct vgic_irq *irq)
> +{
> +	spin_unlock(&irq->irq_lock);
> +}
> +
> +bool vgic_queue_irq_put(struct kvm *kvm, struct vgic_irq *irq);
>  void vgic_kick_vcpus(struct kvm *kvm);
>  
>  void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
> 

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

* Re: [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
  2016-06-22  8:18     ` Andre Przywara
@ 2016-06-22  8:26       ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22  8:26 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: kvmarm, linux-arm-kernel, kvm

On 22/06/16 09:18, Andre Przywara wrote:
> Hi Marc,
> 
> On 17/06/16 13:08, 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 to
>> those structures. Since private IRQs and SPIs are statically allocated,
>> the reqcount 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.
>> Also vgic_get_irq() gets changed to take the irq_lock already, this is
>> later needed to avoid a race between a potential LPI unmap and the
>> window between us getting the pointer and locking the IRQ.
>> This introduces vgic_put_irq(), which just does the unlock and makes
>> the new locking sequence look more symmetrical.
>> This approach deviates from the classical Linux refcounting with using
>> atomic_* types and functions, because the users of vgic_get_irq() take
>> the irq_lock anyway, so we just use an int and adjust the refcount while
>> holding the lock.
> 
> As discussed verbally, I am almost done on an implementation that uses
> the kernel's kref_* infrastructure to implement the ref-counting, on top
> of the existing vgic_irq locking.
> Since we consider the atomic inc/dec operations comparably cheap, that
> shouldn't sacrifice performance, but will increase readability and
> avoids nasty bugs in our own refcounting implementation by using well
> tested and reviewed code.

Indeed. And for further reference, here's the reason why I'm pushing for
kref being used: https://lwn.net/Articles/75920/

Thanks,

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

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

* [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs
@ 2016-06-22  8:26       ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22  8:26 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/06/16 09:18, Andre Przywara wrote:
> Hi Marc,
> 
> On 17/06/16 13:08, 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 to
>> those structures. Since private IRQs and SPIs are statically allocated,
>> the reqcount 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.
>> Also vgic_get_irq() gets changed to take the irq_lock already, this is
>> later needed to avoid a race between a potential LPI unmap and the
>> window between us getting the pointer and locking the IRQ.
>> This introduces vgic_put_irq(), which just does the unlock and makes
>> the new locking sequence look more symmetrical.
>> This approach deviates from the classical Linux refcounting with using
>> atomic_* types and functions, because the users of vgic_get_irq() take
>> the irq_lock anyway, so we just use an int and adjust the refcount while
>> holding the lock.
> 
> As discussed verbally, I am almost done on an implementation that uses
> the kernel's kref_* infrastructure to implement the ref-counting, on top
> of the existing vgic_irq locking.
> Since we consider the atomic inc/dec operations comparably cheap, that
> shouldn't sacrifice performance, but will increase readability and
> avoids nasty bugs in our own refcounting implementation by using well
> tested and reviewed code.

Indeed. And for further reference, here's the reason why I'm pushing for
kref being used: https://lwn.net/Articles/75920/

Thanks,

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

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

* Re: [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22 14:07     ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:07 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: linux-arm-kernel, kvmarm, kvm

On 17/06/16 13:08, Andre Przywara wrote:
> 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/vgic/vgic.h            |  13 +++++
>  include/linux/irqchip/arm-gic-v3.h |   1 +
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>  3 files changed, 124 insertions(+), 2 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index e488a369..dc7f2fd 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -146,6 +146,14 @@ struct vgic_dist {
>  	struct vgic_irq		*spis;
>  
>  	struct vgic_io_device	dist_iodev;
> +
> +	/*
> +	 * Contains the address 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;
> +
> +	/* Points to the LPI pending tables for the redistributor */
> +	u64 pendbaser;
> +
> +	bool lpis_enabled;
>  };
>  
>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index bfbd707..64e8c70 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -124,6 +124,7 @@
>  #define GICR_PROPBASER_WaWb		(5U << 7)
>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index c38302d..8cd7190 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)
>  {
> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>  	return 0;
>  }
>  
> +/* We don't have any constraints about the shareability attributes. */
> +static void vgic_sanitise_shareability(u64 *reg)

Which register is that for?

> +{
> +}

We may not have constraints, but we don't want to let the luser go wild
either... ;-) The notion of outer sharable is pretty useless on ARMv8,
and I'd rather not pretend it is supported in any way. Can you please
make this support all values but OS?

> +
> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
> +#define GIC_CACHE_PROP_MASK \
> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
> +
> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)

I assume this is for GICR_PROPBASER?

> +{
> +	switch (*reg >> reg_shift) {

What about the upper bits?

> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
> +		break;

Why would you force things to be cacheable? Specially outer cacheable?

Frankly, I'd rather stick to either 000 (same as inner cacheability) and
001 (outer non-cacheable).

> +	default:
> +		/* We are fine with the other attributes. */
> +		break;
> +	}
> +}
> +
> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
> +{
> +	switch (*reg >> reg_shift) {
> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
> +		break;
> +	default:
> +		/* We are fine with the other attributes. */
> +		break;
> +	}

I fail to see the difference with the previous function, and it has the
same bugs. As for the cacheability, we definitely want to enforce it, so
you should make both Device-nGnRnE and Normal-nC unsupported.

> +}
> +
> +static void vgic_sanitise_redist_baser(u64 *reg)

Which base register? Please name them entierely (gicr_propbaser) so that
we know what you are messing with.

> +{
> +	vgic_sanitise_shareability(reg);
> +	vgic_sanitise_inner_cacheability(reg,
> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
> +	vgic_sanitise_outer_cacheability(reg, 56);

Care to define this bit field?

> +}
> +
> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f

Please build those using GENMASK_ULL. I can't process long hex values,
but I can read something that matches the spec.

> +
> +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;
> +
> +	/* Storing a value with LPIs already enabled is undefined */
> +	if (vgic_cpu->lpis_enabled)
> +		return;
> +
> +	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
> +	dist->propbaser &= ~PROPBASER_RES0_MASK;
> +	vgic_sanitise_redist_baser(&dist->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;
> +
> +	/* Storing a value with LPIs already enabled is undefined */
> +	if (vgic_cpu->lpis_enabled)
> +		return;
> +
> +	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
> +					       addr & 4, len, val);
> +	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
> +	vgic_sanitise_redist_baser(&vgic_cpu->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
> @@ -226,10 +334,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,
> 

Thanks,

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

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

* [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
@ 2016-06-22 14:07     ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:07 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/06/16 13:08, Andre Przywara wrote:
> 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/vgic/vgic.h            |  13 +++++
>  include/linux/irqchip/arm-gic-v3.h |   1 +
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>  3 files changed, 124 insertions(+), 2 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index e488a369..dc7f2fd 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -146,6 +146,14 @@ struct vgic_dist {
>  	struct vgic_irq		*spis;
>  
>  	struct vgic_io_device	dist_iodev;
> +
> +	/*
> +	 * Contains the address 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;
> +
> +	/* Points to the LPI pending tables for the redistributor */
> +	u64 pendbaser;
> +
> +	bool lpis_enabled;
>  };
>  
>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index bfbd707..64e8c70 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -124,6 +124,7 @@
>  #define GICR_PROPBASER_WaWb		(5U << 7)
>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index c38302d..8cd7190 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)
>  {
> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>  	return 0;
>  }
>  
> +/* We don't have any constraints about the shareability attributes. */
> +static void vgic_sanitise_shareability(u64 *reg)

Which register is that for?

> +{
> +}

We may not have constraints, but we don't want to let the luser go wild
either... ;-) The notion of outer sharable is pretty useless on ARMv8,
and I'd rather not pretend it is supported in any way. Can you please
make this support all values but OS?

> +
> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
> +#define GIC_CACHE_PROP_MASK \
> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
> +
> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)

I assume this is for GICR_PROPBASER?

> +{
> +	switch (*reg >> reg_shift) {

What about the upper bits?

> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
> +		break;

Why would you force things to be cacheable? Specially outer cacheable?

Frankly, I'd rather stick to either 000 (same as inner cacheability) and
001 (outer non-cacheable).

> +	default:
> +		/* We are fine with the other attributes. */
> +		break;
> +	}
> +}
> +
> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
> +{
> +	switch (*reg >> reg_shift) {
> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
> +		break;
> +	default:
> +		/* We are fine with the other attributes. */
> +		break;
> +	}

I fail to see the difference with the previous function, and it has the
same bugs. As for the cacheability, we definitely want to enforce it, so
you should make both Device-nGnRnE and Normal-nC unsupported.

> +}
> +
> +static void vgic_sanitise_redist_baser(u64 *reg)

Which base register? Please name them entierely (gicr_propbaser) so that
we know what you are messing with.

> +{
> +	vgic_sanitise_shareability(reg);
> +	vgic_sanitise_inner_cacheability(reg,
> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
> +	vgic_sanitise_outer_cacheability(reg, 56);

Care to define this bit field?

> +}
> +
> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f

Please build those using GENMASK_ULL. I can't process long hex values,
but I can read something that matches the spec.

> +
> +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;
> +
> +	/* Storing a value with LPIs already enabled is undefined */
> +	if (vgic_cpu->lpis_enabled)
> +		return;
> +
> +	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
> +	dist->propbaser &= ~PROPBASER_RES0_MASK;
> +	vgic_sanitise_redist_baser(&dist->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;
> +
> +	/* Storing a value with LPIs already enabled is undefined */
> +	if (vgic_cpu->lpis_enabled)
> +		return;
> +
> +	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
> +					       addr & 4, len, val);
> +	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
> +	vgic_sanitise_redist_baser(&vgic_cpu->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
> @@ -226,10 +334,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,
> 

Thanks,

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

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

* Re: [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
  2016-06-22 14:07     ` Marc Zyngier
@ 2016-06-22 14:39       ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22 14:39 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

Hi Marc,

On 22/06/16 15:07, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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/vgic/vgic.h            |  13 +++++
>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>>  3 files changed, 124 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>> index e488a369..dc7f2fd 100644
>> --- a/include/kvm/vgic/vgic.h
>> +++ b/include/kvm/vgic/vgic.h
>> @@ -146,6 +146,14 @@ struct vgic_dist {
>>  	struct vgic_irq		*spis;
>>  
>>  	struct vgic_io_device	dist_iodev;
>> +
>> +	/*
>> +	 * Contains the address 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;
>> +
>> +	/* Points to the LPI pending tables for the redistributor */
>> +	u64 pendbaser;
>> +
>> +	bool lpis_enabled;
>>  };
>>  
>>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>> index bfbd707..64e8c70 100644
>> --- a/include/linux/irqchip/arm-gic-v3.h
>> +++ b/include/linux/irqchip/arm-gic-v3.h
>> @@ -124,6 +124,7 @@
>>  #define GICR_PROPBASER_WaWb		(5U << 7)
>>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
>> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>>  
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>> index c38302d..8cd7190 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)
>>  {
>> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>>  	return 0;
>>  }
>>  
>> +/* We don't have any constraints about the shareability attributes. */
>> +static void vgic_sanitise_shareability(u64 *reg)
> 
> Which register is that for?

I was under the assumption that any shareability constraints would apply
to _all_ registers that use that mechanism:
PENDBASER, PROPBASER, BASER(device), BASER(collection), CBASER.
That's why this common function.

>> +{
>> +}
> 
> We may not have constraints, but we don't want to let the luser go wild
> either... ;-) The notion of outer sharable is pretty useless on ARMv8,
> and I'd rather not pretend it is supported in any way. Can you please
> make this support all values but OS?

Sure, actually I was hoping for your guidance here, since I couldn't
really wrap my mind around shareability in this virtualisation context.

>> +
>> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
>> +#define GIC_CACHE_PROP_MASK \
>> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
>> +
>> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
> 
> I assume this is for GICR_PROPBASER?

Again this is for all registers as listed above. Do we need separate
constraints for different registers?

>> +{
>> +	switch (*reg >> reg_shift) {
> 
> What about the upper bits?

Pah ;-)
Will fix it ...

>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>> +		break;
> 
> Why would you force things to be cacheable? Specially outer cacheable?

To be honest, I couldn't really make out what "outer cacheability"
actually means in the context of a guest having user space memory mapped.

> 
> Frankly, I'd rather stick to either 000 (same as inner cacheability) and
> 001 (outer non-cacheable).

Sure.

> 
>> +	default:
>> +		/* We are fine with the other attributes. */
>> +		break;
>> +	}
>> +}
>> +
>> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
>> +{
>> +	switch (*reg >> reg_shift) {
>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>> +		break;
>> +	default:
>> +		/* We are fine with the other attributes. */
>> +		break;
>> +	}
> 
> I fail to see the difference with the previous function, 

The meaning of 000 is different: for inner cacheability it means Device,
for outer it means "same as inner". So we need two functions here to
differentiate, don't we? Unless our constraints happen to be the same
for the two (by coincidence)?
Also I think you just requested different constraints for outer and
inner cacheability? Or did I miss something?

> and it has the
> same bugs. As for the cacheability, we definitely want to enforce it, so
> you should make both Device-nGnRnE and Normal-nC unsupported.

Isn't that what the function does? If the value is 000 or 001, it
changes it to WaWb, otherwise it keeps the value. At least that was what
I had in mind. WaWb is what the Linux ITS driver prefers, so this serves
as some sensible value here (I hope). It's a bit weird that these bits
are not a bitfield, so the ITS cannot report back a set of supported
attributes easily at once.

>> +}
>> +
>> +static void vgic_sanitise_redist_baser(u64 *reg)
> 
> Which base register? Please name them entierely (gicr_propbaser) so that
> we know what you are messing with.

This is for BASER (both for collections and devices). It's a bit
unfortunate that this one just consists of the stem which the other
registers also share, so it's a bit hard to differentiate without
inventing a new name. I can add a comment, though.

>> +{
>> +	vgic_sanitise_shareability(reg);
>> +	vgic_sanitise_inner_cacheability(reg,
>> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
>> +	vgic_sanitise_outer_cacheability(reg, 56);
> 
> Care to define this bit field?

Sure.

> 
>> +}
>> +
>> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
>> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f
> 
> Please build those using GENMASK_ULL. I can't process long hex values,
> but I can read something that matches the spec.

OK.

Thanks for having a look!

Andre.

>> +
>> +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;
>> +
>> +	/* Storing a value with LPIs already enabled is undefined */
>> +	if (vgic_cpu->lpis_enabled)
>> +		return;
>> +
>> +	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
>> +	dist->propbaser &= ~PROPBASER_RES0_MASK;
>> +	vgic_sanitise_redist_baser(&dist->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;
>> +
>> +	/* Storing a value with LPIs already enabled is undefined */
>> +	if (vgic_cpu->lpis_enabled)
>> +		return;
>> +
>> +	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
>> +					       addr & 4, len, val);
>> +	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
>> +	vgic_sanitise_redist_baser(&vgic_cpu->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
>> @@ -226,10 +334,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,
>>
> 
> Thanks,
> 
> 	M.
> 

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

* [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
@ 2016-06-22 14:39       ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22 14:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On 22/06/16 15:07, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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/vgic/vgic.h            |  13 +++++
>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>>  3 files changed, 124 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>> index e488a369..dc7f2fd 100644
>> --- a/include/kvm/vgic/vgic.h
>> +++ b/include/kvm/vgic/vgic.h
>> @@ -146,6 +146,14 @@ struct vgic_dist {
>>  	struct vgic_irq		*spis;
>>  
>>  	struct vgic_io_device	dist_iodev;
>> +
>> +	/*
>> +	 * Contains the address 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;
>> +
>> +	/* Points to the LPI pending tables for the redistributor */
>> +	u64 pendbaser;
>> +
>> +	bool lpis_enabled;
>>  };
>>  
>>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>> index bfbd707..64e8c70 100644
>> --- a/include/linux/irqchip/arm-gic-v3.h
>> +++ b/include/linux/irqchip/arm-gic-v3.h
>> @@ -124,6 +124,7 @@
>>  #define GICR_PROPBASER_WaWb		(5U << 7)
>>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
>> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>>  
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>> index c38302d..8cd7190 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)
>>  {
>> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>>  	return 0;
>>  }
>>  
>> +/* We don't have any constraints about the shareability attributes. */
>> +static void vgic_sanitise_shareability(u64 *reg)
> 
> Which register is that for?

I was under the assumption that any shareability constraints would apply
to _all_ registers that use that mechanism:
PENDBASER, PROPBASER, BASER(device), BASER(collection), CBASER.
That's why this common function.

>> +{
>> +}
> 
> We may not have constraints, but we don't want to let the luser go wild
> either... ;-) The notion of outer sharable is pretty useless on ARMv8,
> and I'd rather not pretend it is supported in any way. Can you please
> make this support all values but OS?

Sure, actually I was hoping for your guidance here, since I couldn't
really wrap my mind around shareability in this virtualisation context.

>> +
>> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
>> +#define GIC_CACHE_PROP_MASK \
>> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
>> +
>> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
> 
> I assume this is for GICR_PROPBASER?

Again this is for all registers as listed above. Do we need separate
constraints for different registers?

>> +{
>> +	switch (*reg >> reg_shift) {
> 
> What about the upper bits?

Pah ;-)
Will fix it ...

>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>> +		break;
> 
> Why would you force things to be cacheable? Specially outer cacheable?

To be honest, I couldn't really make out what "outer cacheability"
actually means in the context of a guest having user space memory mapped.

> 
> Frankly, I'd rather stick to either 000 (same as inner cacheability) and
> 001 (outer non-cacheable).

Sure.

> 
>> +	default:
>> +		/* We are fine with the other attributes. */
>> +		break;
>> +	}
>> +}
>> +
>> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
>> +{
>> +	switch (*reg >> reg_shift) {
>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>> +		break;
>> +	default:
>> +		/* We are fine with the other attributes. */
>> +		break;
>> +	}
> 
> I fail to see the difference with the previous function, 

The meaning of 000 is different: for inner cacheability it means Device,
for outer it means "same as inner". So we need two functions here to
differentiate, don't we? Unless our constraints happen to be the same
for the two (by coincidence)?
Also I think you just requested different constraints for outer and
inner cacheability? Or did I miss something?

> and it has the
> same bugs. As for the cacheability, we definitely want to enforce it, so
> you should make both Device-nGnRnE and Normal-nC unsupported.

Isn't that what the function does? If the value is 000 or 001, it
changes it to WaWb, otherwise it keeps the value. At least that was what
I had in mind. WaWb is what the Linux ITS driver prefers, so this serves
as some sensible value here (I hope). It's a bit weird that these bits
are not a bitfield, so the ITS cannot report back a set of supported
attributes easily at once.

>> +}
>> +
>> +static void vgic_sanitise_redist_baser(u64 *reg)
> 
> Which base register? Please name them entierely (gicr_propbaser) so that
> we know what you are messing with.

This is for BASER (both for collections and devices). It's a bit
unfortunate that this one just consists of the stem which the other
registers also share, so it's a bit hard to differentiate without
inventing a new name. I can add a comment, though.

>> +{
>> +	vgic_sanitise_shareability(reg);
>> +	vgic_sanitise_inner_cacheability(reg,
>> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
>> +	vgic_sanitise_outer_cacheability(reg, 56);
> 
> Care to define this bit field?

Sure.

> 
>> +}
>> +
>> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
>> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f
> 
> Please build those using GENMASK_ULL. I can't process long hex values,
> but I can read something that matches the spec.

OK.

Thanks for having a look!

Andre.

>> +
>> +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;
>> +
>> +	/* Storing a value with LPIs already enabled is undefined */
>> +	if (vgic_cpu->lpis_enabled)
>> +		return;
>> +
>> +	dist->propbaser = update_64bit_reg(dist->propbaser, addr & 4, len, val);
>> +	dist->propbaser &= ~PROPBASER_RES0_MASK;
>> +	vgic_sanitise_redist_baser(&dist->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;
>> +
>> +	/* Storing a value with LPIs already enabled is undefined */
>> +	if (vgic_cpu->lpis_enabled)
>> +		return;
>> +
>> +	vgic_cpu->pendbaser = update_64bit_reg(vgic_cpu->pendbaser,
>> +					       addr & 4, len, val);
>> +	vgic_cpu->pendbaser &= ~PENDBASER_RES0_MASK;
>> +	vgic_sanitise_redist_baser(&vgic_cpu->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
>> @@ -226,10 +334,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,
>>
> 
> Thanks,
> 
> 	M.
> 

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

* Re: [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22 14:48     ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:48 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: kvmarm, kvm, linux-arm-kernel

On 17/06/16 13:08, Andre Przywara wrote:
> 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/vgic/vgic.h            |  13 ++++-
>  include/linux/irqchip/arm-gic-v3.h |   1 +
>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>  8 files changed, 187 insertions(+), 9 deletions(-)
>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
> 
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index a7a958c..2755d17 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -30,6 +30,7 @@ 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
>  else
>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index dc7f2fd..a66ba9a 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -111,12 +111,23 @@ struct vgic_register_region;
>  
>  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;
>  	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;
> +};

You may want to move this definition before struct vgic_io_device, some
compilers are more picky than some others.

> +
>  struct vgic_dist {
>  	bool			in_kernel;
>  	bool			ready;
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index 64e8c70..92d5da8 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -176,6 +176,7 @@
>  #define GITS_CWRITER			0x0088
>  #define GITS_CREADR			0x0090
>  #define GITS_BASER			0x0100
> +#define GITS_IDREGS_BASE		0xffd0

Please make all the changes to the core GIC code in a separate patch,
which I can pick independently.

>  #define GITS_PIDR2			GICR_PIDR2
>  
>  #define GITS_TRANSLATER			0x10040
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> new file mode 100644
> index 0000000..c02d9df
> --- /dev/null
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -0,0 +1,107 @@
> +/*
> + * 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,					\
> +	.read = NULL,						\
> +	.write = NULL,						\
> +	.its_read = rd,						\
> +	.its_write = wr,					\

Meh. That's not very nice... Why can't they be a union?

> +}
> +
> +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 */
> +}
> +
> +struct vgic_register_region its_registers[] = {

Shouldn't this be static?

> +	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 vits_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->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;
> +}
> +
> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
> +{
> +
> +	its->enabled = false;
> +}
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 8cd7190..4eee0d7 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)
>  {
> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>  	vgic_put_irq(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)
>  {
> @@ -325,7 +361,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,
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
> index 4050c1c..c98a4fd 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
> @@ -461,8 +461,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);
> @@ -471,8 +470,15 @@ 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);
> +	if (region->read) {
> +		struct kvm_vcpu *r_vcpu;
> +
> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
> +		data = region->read(r_vcpu, addr, len);
> +	} else if (region->its_read) {
> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
> +	}

I'd rather have a flag somewhere that allows us to identify the type of
device we're dealing with. You could have something like:

enum iodev_type {
	IODEV_VGIC_DISTRIBUTOR,
	IODEV_VGIC_REDISTRIBUTOR,
	IODEV_VGIC_ITS,
};

assign the type to each region we have, and then have something like:

	switch (region->iodev_type) {
	case IODEV_VGIC_DISTRIBUTOR:
		data = region->read(vcpu, addr, len);
		break;
	case IODEV_VGIC_REDISTRIBUTOR:
		data = region->read(iodev->redist_vcpu, addr, len);
		break;
	case IODEV_VGIC_ITS:
		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
		break;
	}

It would make it extensible *and* readable.

> +
>  	vgic_data_host_to_mmio_bus(val, len, data);
>  	return 0;
>  }
> @@ -482,7 +488,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,
> @@ -493,8 +498,14 @@ 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);
> +	if (region->write) {
> +		struct kvm_vcpu *r_vcpu;
> +
> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
> +		region->write(r_vcpu, addr, len, data);
> +	} else if (region->its_write) {
> +		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
> +	}

Same here.

>  	return 0;
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
> index 8509014..6dfc2f0 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.h
> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
> @@ -25,6 +25,10 @@ struct vgic_register_region {
>  			      unsigned int len);
>  	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
>  		      unsigned long val);
> +	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
> +				  gpa_t addr, unsigned int len);
> +	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 fa2d225..b09590d 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -76,6 +76,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)
>  {
> @@ -127,6 +128,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);
> 

Thanks,

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

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

* [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
@ 2016-06-22 14:48     ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:48 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/06/16 13:08, Andre Przywara wrote:
> 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/vgic/vgic.h            |  13 ++++-
>  include/linux/irqchip/arm-gic-v3.h |   1 +
>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>  8 files changed, 187 insertions(+), 9 deletions(-)
>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
> 
> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
> index a7a958c..2755d17 100644
> --- a/arch/arm64/kvm/Makefile
> +++ b/arch/arm64/kvm/Makefile
> @@ -30,6 +30,7 @@ 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
>  else
>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index dc7f2fd..a66ba9a 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -111,12 +111,23 @@ struct vgic_register_region;
>  
>  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;
>  	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;
> +};

You may want to move this definition before struct vgic_io_device, some
compilers are more picky than some others.

> +
>  struct vgic_dist {
>  	bool			in_kernel;
>  	bool			ready;
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index 64e8c70..92d5da8 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -176,6 +176,7 @@
>  #define GITS_CWRITER			0x0088
>  #define GITS_CREADR			0x0090
>  #define GITS_BASER			0x0100
> +#define GITS_IDREGS_BASE		0xffd0

Please make all the changes to the core GIC code in a separate patch,
which I can pick independently.

>  #define GITS_PIDR2			GICR_PIDR2
>  
>  #define GITS_TRANSLATER			0x10040
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> new file mode 100644
> index 0000000..c02d9df
> --- /dev/null
> +++ b/virt/kvm/arm/vgic/vgic-its.c
> @@ -0,0 +1,107 @@
> +/*
> + * 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,					\
> +	.read = NULL,						\
> +	.write = NULL,						\
> +	.its_read = rd,						\
> +	.its_write = wr,					\

Meh. That's not very nice... Why can't they be a union?

> +}
> +
> +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 */
> +}
> +
> +struct vgic_register_region its_registers[] = {

Shouldn't this be static?

> +	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 vits_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->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;
> +}
> +
> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
> +{
> +
> +	its->enabled = false;
> +}
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 8cd7190..4eee0d7 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)
>  {
> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>  	vgic_put_irq(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)
>  {
> @@ -325,7 +361,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,
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
> index 4050c1c..c98a4fd 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.c
> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
> @@ -461,8 +461,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);
> @@ -471,8 +470,15 @@ 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);
> +	if (region->read) {
> +		struct kvm_vcpu *r_vcpu;
> +
> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
> +		data = region->read(r_vcpu, addr, len);
> +	} else if (region->its_read) {
> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
> +	}

I'd rather have a flag somewhere that allows us to identify the type of
device we're dealing with. You could have something like:

enum iodev_type {
	IODEV_VGIC_DISTRIBUTOR,
	IODEV_VGIC_REDISTRIBUTOR,
	IODEV_VGIC_ITS,
};

assign the type to each region we have, and then have something like:

	switch (region->iodev_type) {
	case IODEV_VGIC_DISTRIBUTOR:
		data = region->read(vcpu, addr, len);
		break;
	case IODEV_VGIC_REDISTRIBUTOR:
		data = region->read(iodev->redist_vcpu, addr, len);
		break;
	case IODEV_VGIC_ITS:
		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
		break;
	}

It would make it extensible *and* readable.

> +
>  	vgic_data_host_to_mmio_bus(val, len, data);
>  	return 0;
>  }
> @@ -482,7 +488,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,
> @@ -493,8 +498,14 @@ 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);
> +	if (region->write) {
> +		struct kvm_vcpu *r_vcpu;
> +
> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
> +		region->write(r_vcpu, addr, len, data);
> +	} else if (region->its_write) {
> +		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
> +	}

Same here.

>  	return 0;
>  }
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
> index 8509014..6dfc2f0 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.h
> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
> @@ -25,6 +25,10 @@ struct vgic_register_region {
>  			      unsigned int len);
>  	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
>  		      unsigned long val);
> +	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
> +				  gpa_t addr, unsigned int len);
> +	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 fa2d225..b09590d 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -76,6 +76,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)
>  {
> @@ -127,6 +128,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);
> 

Thanks,

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

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

* Re: [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
  2016-06-22 14:39       ` Andre Przywara
@ 2016-06-22 14:59         ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:59 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: linux-arm-kernel, kvmarm, kvm

On 22/06/16 15:39, Andre Przywara wrote:
> Hi Marc,
> 
> On 22/06/16 15:07, Marc Zyngier wrote:
>> On 17/06/16 13:08, Andre Przywara wrote:
>>> 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/vgic/vgic.h            |  13 +++++
>>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>>>  3 files changed, 124 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>>> index e488a369..dc7f2fd 100644
>>> --- a/include/kvm/vgic/vgic.h
>>> +++ b/include/kvm/vgic/vgic.h
>>> @@ -146,6 +146,14 @@ struct vgic_dist {
>>>  	struct vgic_irq		*spis;
>>>  
>>>  	struct vgic_io_device	dist_iodev;
>>> +
>>> +	/*
>>> +	 * Contains the address 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;
>>> +
>>> +	/* Points to the LPI pending tables for the redistributor */
>>> +	u64 pendbaser;
>>> +
>>> +	bool lpis_enabled;
>>>  };
>>>  
>>>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
>>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>>> index bfbd707..64e8c70 100644
>>> --- a/include/linux/irqchip/arm-gic-v3.h
>>> +++ b/include/linux/irqchip/arm-gic-v3.h
>>> @@ -124,6 +124,7 @@
>>>  #define GICR_PROPBASER_WaWb		(5U << 7)
>>>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>>>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
>>> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>>>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>>>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>>>  
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>>> index c38302d..8cd7190 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)
>>>  {
>>> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>>>  	return 0;
>>>  }
>>>  
>>> +/* We don't have any constraints about the shareability attributes. */
>>> +static void vgic_sanitise_shareability(u64 *reg)
>>
>> Which register is that for?
> 
> I was under the assumption that any shareability constraints would apply
> to _all_ registers that use that mechanism:
> PENDBASER, PROPBASER, BASER(device), BASER(collection), CBASER.
> That's why this common function.
> 
>>> +{
>>> +}
>>
>> We may not have constraints, but we don't want to let the luser go wild
>> either... ;-) The notion of outer sharable is pretty useless on ARMv8,
>> and I'd rather not pretend it is supported in any way. Can you please
>> make this support all values but OS?
> 
> Sure, actually I was hoping for your guidance here, since I couldn't
> really wrap my mind around shareability in this virtualisation context.
> 
>>> +
>>> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
>>> +#define GIC_CACHE_PROP_MASK \
>>> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
>>> +
>>> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
>>
>> I assume this is for GICR_PROPBASER?
> 
> Again this is for all registers as listed above. Do we need separate
> constraints for different registers?

I'd rather have something that is per register (after all, there is only
a handful). It will make the code readable (and it is so trivial that
the compiler will inline it anyway).

> 
>>> +{
>>> +	switch (*reg >> reg_shift) {
>>
>> What about the upper bits?
> 
> Pah ;-)
> Will fix it ...
> 
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>>> +		break;
>>
>> Why would you force things to be cacheable? Specially outer cacheable?
> 
> To be honest, I couldn't really make out what "outer cacheability"
> actually means in the context of a guest having user space memory mapped.
> 
>>
>> Frankly, I'd rather stick to either 000 (same as inner cacheability) and
>> 001 (outer non-cacheable).
> 
> Sure.
> 
>>
>>> +	default:
>>> +		/* We are fine with the other attributes. */
>>> +		break;
>>> +	}
>>> +}
>>> +
>>> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
>>> +{
>>> +	switch (*reg >> reg_shift) {
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>>> +		break;
>>> +	default:
>>> +		/* We are fine with the other attributes. */
>>> +		break;
>>> +	}
>>
>> I fail to see the difference with the previous function, 
> 
> The meaning of 000 is different: for inner cacheability it means Device,
> for outer it means "same as inner". So we need two functions here to
> differentiate, don't we? Unless our constraints happen to be the same
> for the two (by coincidence)?
> Also I think you just requested different constraints for outer and
> inner cacheability? Or did I miss something?

Yeah, I missed the first line. Your GIC_CACHE_PROP_ATTR() macros are
really getting in the way here. I'd prefer to see each register accessor
doing its own "sanitisation" in situ, without some fake sharing of code.

>> and it has the
>> same bugs. As for the cacheability, we definitely want to enforce it, so
>> you should make both Device-nGnRnE and Normal-nC unsupported.
> 
> Isn't that what the function does? If the value is 000 or 001, it
> changes it to WaWb, otherwise it keeps the value. At least that was what
> I had in mind. WaWb is what the Linux ITS driver prefers, so this serves

Careful, that's about to change. And don't mind reporting *any* value,
as long as it is cacheable.

> as some sensible value here (I hope). It's a bit weird that these bits
> are not a bitfield, so the ITS cannot report back a set of supported
> attributes easily at once.
> 
>>> +}
>>> +
>>> +static void vgic_sanitise_redist_baser(u64 *reg)
>>
>> Which base register? Please name them entierely (gicr_propbaser) so that
>> we know what you are messing with.
> 
> This is for BASER (both for collections and devices). It's a bit
> unfortunate that this one just consists of the stem which the other
> registers also share, so it's a bit hard to differentiate without
> inventing a new name. I can add a comment, though.

Well, you say "redist_baser". To me, that's a redistributor, not an ITS
register. just drop these functions, and do it in each accessor.
Everything will become much more readable.

> 
>>> +{
>>> +	vgic_sanitise_shareability(reg);
>>> +	vgic_sanitise_inner_cacheability(reg,
>>> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
>>> +	vgic_sanitise_outer_cacheability(reg, 56);
>>
>> Care to define this bit field?
> 
> Sure.
> 
>>
>>> +}
>>> +
>>> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
>>> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f
>>
>> Please build those using GENMASK_ULL. I can't process long hex values,
>> but I can read something that matches the spec.
> 
> OK.
> 
> Thanks for having a look!

Thanks,

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

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

* [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers
@ 2016-06-22 14:59         ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 14:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/06/16 15:39, Andre Przywara wrote:
> Hi Marc,
> 
> On 22/06/16 15:07, Marc Zyngier wrote:
>> On 17/06/16 13:08, Andre Przywara wrote:
>>> 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/vgic/vgic.h            |  13 +++++
>>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   | 112 ++++++++++++++++++++++++++++++++++++-
>>>  3 files changed, 124 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>>> index e488a369..dc7f2fd 100644
>>> --- a/include/kvm/vgic/vgic.h
>>> +++ b/include/kvm/vgic/vgic.h
>>> @@ -146,6 +146,14 @@ struct vgic_dist {
>>>  	struct vgic_irq		*spis;
>>>  
>>>  	struct vgic_io_device	dist_iodev;
>>> +
>>> +	/*
>>> +	 * Contains the address 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;
>>> +
>>> +	/* Points to the LPI pending tables for the redistributor */
>>> +	u64 pendbaser;
>>> +
>>> +	bool lpis_enabled;
>>>  };
>>>  
>>>  int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
>>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>>> index bfbd707..64e8c70 100644
>>> --- a/include/linux/irqchip/arm-gic-v3.h
>>> +++ b/include/linux/irqchip/arm-gic-v3.h
>>> @@ -124,6 +124,7 @@
>>>  #define GICR_PROPBASER_WaWb		(5U << 7)
>>>  #define GICR_PROPBASER_RaWaWt		(6U << 7)
>>>  #define GICR_PROPBASER_RaWaWb		(7U << 7)
>>> +#define GICR_PROPBASER_CACHEABILITY_SHIFT (7)
>>>  #define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
>>>  #define GICR_PROPBASER_IDBITS_MASK	(0x1f)
>>>  
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>>> index c38302d..8cd7190 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)
>>>  {
>>> @@ -146,6 +159,101 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
>>>  	return 0;
>>>  }
>>>  
>>> +/* We don't have any constraints about the shareability attributes. */
>>> +static void vgic_sanitise_shareability(u64 *reg)
>>
>> Which register is that for?
> 
> I was under the assumption that any shareability constraints would apply
> to _all_ registers that use that mechanism:
> PENDBASER, PROPBASER, BASER(device), BASER(collection), CBASER.
> That's why this common function.
> 
>>> +{
>>> +}
>>
>> We may not have constraints, but we don't want to let the luser go wild
>> either... ;-) The notion of outer sharable is pretty useless on ARMv8,
>> and I'd rather not pretend it is supported in any way. Can you please
>> make this support all values but OS?
> 
> Sure, actually I was hoping for your guidance here, since I couldn't
> really wrap my mind around shareability in this virtualisation context.
> 
>>> +
>>> +#define GIC_CACHE_PROP_ATTR(x) ((x) >> GICR_PROPBASER_CACHEABILITY_SHIFT)
>>> +#define GIC_CACHE_PROP_MASK \
>>> +	((u64)(GIC_CACHE_PROP_ATTR(GICR_PROPBASER_CACHEABILITY_MASK)))
>>> +
>>> +static void vgic_sanitise_outer_cacheability(u64 *reg, int reg_shift)
>>
>> I assume this is for GICR_PROPBASER?
> 
> Again this is for all registers as listed above. Do we need separate
> constraints for different registers?

I'd rather have something that is per register (after all, there is only
a handful). It will make the code readable (and it is so trivial that
the compiler will inline it anyway).

> 
>>> +{
>>> +	switch (*reg >> reg_shift) {
>>
>> What about the upper bits?
> 
> Pah ;-)
> Will fix it ...
> 
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>>> +		break;
>>
>> Why would you force things to be cacheable? Specially outer cacheable?
> 
> To be honest, I couldn't really make out what "outer cacheability"
> actually means in the context of a guest having user space memory mapped.
> 
>>
>> Frankly, I'd rather stick to either 000 (same as inner cacheability) and
>> 001 (outer non-cacheable).
> 
> Sure.
> 
>>
>>> +	default:
>>> +		/* We are fine with the other attributes. */
>>> +		break;
>>> +	}
>>> +}
>>> +
>>> +static void vgic_sanitise_inner_cacheability(u64 *reg, int reg_shift)
>>> +{
>>> +	switch (*reg >> reg_shift) {
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nCnB):
>>> +	case GIC_CACHE_PROP_ATTR(GICR_PROPBASER_nC):
>>> +		*reg &= ~(GIC_CACHE_PROP_MASK << reg_shift);
>>> +		*reg |= GIC_CACHE_PROP_ATTR(GICR_PROPBASER_WaWb) << reg_shift;
>>> +		break;
>>> +	default:
>>> +		/* We are fine with the other attributes. */
>>> +		break;
>>> +	}
>>
>> I fail to see the difference with the previous function, 
> 
> The meaning of 000 is different: for inner cacheability it means Device,
> for outer it means "same as inner". So we need two functions here to
> differentiate, don't we? Unless our constraints happen to be the same
> for the two (by coincidence)?
> Also I think you just requested different constraints for outer and
> inner cacheability? Or did I miss something?

Yeah, I missed the first line. Your GIC_CACHE_PROP_ATTR() macros are
really getting in the way here. I'd prefer to see each register accessor
doing its own "sanitisation" in situ, without some fake sharing of code.

>> and it has the
>> same bugs. As for the cacheability, we definitely want to enforce it, so
>> you should make both Device-nGnRnE and Normal-nC unsupported.
> 
> Isn't that what the function does? If the value is 000 or 001, it
> changes it to WaWb, otherwise it keeps the value. At least that was what
> I had in mind. WaWb is what the Linux ITS driver prefers, so this serves

Careful, that's about to change. And don't mind reporting *any* value,
as long as it is cacheable.

> as some sensible value here (I hope). It's a bit weird that these bits
> are not a bitfield, so the ITS cannot report back a set of supported
> attributes easily at once.
> 
>>> +}
>>> +
>>> +static void vgic_sanitise_redist_baser(u64 *reg)
>>
>> Which base register? Please name them entierely (gicr_propbaser) so that
>> we know what you are messing with.
> 
> This is for BASER (both for collections and devices). It's a bit
> unfortunate that this one just consists of the stem which the other
> registers also share, so it's a bit hard to differentiate without
> inventing a new name. I can add a comment, though.

Well, you say "redist_baser". To me, that's a redistributor, not an ITS
register. just drop these functions, and do it in each accessor.
Everything will become much more readable.

> 
>>> +{
>>> +	vgic_sanitise_shareability(reg);
>>> +	vgic_sanitise_inner_cacheability(reg,
>>> +					 GICR_PROPBASER_CACHEABILITY_SHIFT);
>>> +	vgic_sanitise_outer_cacheability(reg, 56);
>>
>> Care to define this bit field?
> 
> Sure.
> 
>>
>>> +}
>>> +
>>> +#define PROPBASER_RES0_MASK 0xf8f0000000000060
>>> +#define PENDBASER_RES0_MASK 0xb8f000000000f07f
>>
>> Please build those using GENMASK_ULL. I can't process long hex values,
>> but I can read something that matches the spec.
> 
> OK.
> 
> Thanks for having a look!

Thanks,

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

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

* Re: [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
  2016-06-22 14:48     ` Marc Zyngier
@ 2016-06-22 15:03       ` Andre Przywara
  -1 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22 15:03 UTC (permalink / raw)
  To: Marc Zyngier, Christoffer Dall, Eric Auger; +Cc: linux-arm-kernel, kvmarm, kvm

Hi,

On 22/06/16 15:48, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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/vgic/vgic.h            |  13 ++++-
>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>>  8 files changed, 187 insertions(+), 9 deletions(-)
>>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
>>
>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>> index a7a958c..2755d17 100644
>> --- a/arch/arm64/kvm/Makefile
>> +++ b/arch/arm64/kvm/Makefile
>> @@ -30,6 +30,7 @@ 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
>>  else
>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>> index dc7f2fd..a66ba9a 100644
>> --- a/include/kvm/vgic/vgic.h
>> +++ b/include/kvm/vgic/vgic.h
>> @@ -111,12 +111,23 @@ struct vgic_register_region;
>>  
>>  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;
>>  	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;
>> +};
> 
> You may want to move this definition before struct vgic_io_device, some
> compilers are more picky than some others.

Mmh, I just see that this is actually circular here.
So shall I add an opaque declaration for struct vgic_its above?

> 
>> +
>>  struct vgic_dist {
>>  	bool			in_kernel;
>>  	bool			ready;
>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>> index 64e8c70..92d5da8 100644
>> --- a/include/linux/irqchip/arm-gic-v3.h
>> +++ b/include/linux/irqchip/arm-gic-v3.h
>> @@ -176,6 +176,7 @@
>>  #define GITS_CWRITER			0x0088
>>  #define GITS_CREADR			0x0090
>>  #define GITS_BASER			0x0100
>> +#define GITS_IDREGS_BASE		0xffd0
> 
> Please make all the changes to the core GIC code in a separate patch,
> which I can pick independently.

Sure.

> 
>>  #define GITS_PIDR2			GICR_PIDR2
>>  
>>  #define GITS_TRANSLATER			0x10040
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> new file mode 100644
>> index 0000000..c02d9df
>> --- /dev/null
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -0,0 +1,107 @@
>> +/*
>> + * 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,					\
>> +	.read = NULL,						\
>> +	.write = NULL,						\
>> +	.its_read = rd,						\
>> +	.its_write = wr,					\
> 
> Meh. That's not very nice... Why can't they be a union?

We can, but we would need another differentiator then (as you suggested
below).

>> +}
>> +
>> +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 */
>> +}
>> +
>> +struct vgic_register_region its_registers[] = {
> 
> Shouldn't this be static?

Indeed, looks like a rebase artefact (I had the registration in another
file during some rework attempts).

> 
>> +	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 vits_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->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;
>> +}
>> +
>> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>> +{
>> +
>> +	its->enabled = false;
>> +}
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>> index 8cd7190..4eee0d7 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)
>>  {
>> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>>  	vgic_put_irq(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)
>>  {
>> @@ -325,7 +361,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,
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
>> index 4050c1c..c98a4fd 100644
>> --- a/virt/kvm/arm/vgic/vgic-mmio.c
>> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
>> @@ -461,8 +461,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);
>> @@ -471,8 +470,15 @@ 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);
>> +	if (region->read) {
>> +		struct kvm_vcpu *r_vcpu;
>> +
>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>> +		data = region->read(r_vcpu, addr, len);
>> +	} else if (region->its_read) {
>> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>> +	}
> 
> I'd rather have a flag somewhere that allows us to identify the type of
> device we're dealing with. You could have something like:
> 
> enum iodev_type {
> 	IODEV_VGIC_DISTRIBUTOR,
> 	IODEV_VGIC_REDISTRIBUTOR,
> 	IODEV_VGIC_ITS,
> };
> 
> assign the type to each region we have, and then have something like:
> 
> 	switch (region->iodev_type) {
> 	case IODEV_VGIC_DISTRIBUTOR:
> 		data = region->read(vcpu, addr, len);
> 		break;
> 	case IODEV_VGIC_REDISTRIBUTOR:
> 		data = region->read(iodev->redist_vcpu, addr, len);
> 		break;
> 	case IODEV_VGIC_ITS:
> 		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
> 		break;
> 	}
> 
> It would make it extensible *and* readable.

Agreed, will fix it.

Cheers,
Andre.

>> +
>>  	vgic_data_host_to_mmio_bus(val, len, data);
>>  	return 0;
>>  }
>> @@ -482,7 +488,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,
>> @@ -493,8 +498,14 @@ 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);
>> +	if (region->write) {
>> +		struct kvm_vcpu *r_vcpu;
>> +
>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>> +		region->write(r_vcpu, addr, len, data);
>> +	} else if (region->its_write) {
>> +		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
>> +	}
> 
> Same here.
> 
>>  	return 0;
>>  }
>>  
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
>> index 8509014..6dfc2f0 100644
>> --- a/virt/kvm/arm/vgic/vgic-mmio.h
>> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
>> @@ -25,6 +25,10 @@ struct vgic_register_region {
>>  			      unsigned int len);
>>  	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
>>  		      unsigned long val);
>> +	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
>> +				  gpa_t addr, unsigned int len);
>> +	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 fa2d225..b09590d 100644
>> --- a/virt/kvm/arm/vgic/vgic.h
>> +++ b/virt/kvm/arm/vgic/vgic.h
>> @@ -76,6 +76,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)
>>  {
>> @@ -127,6 +128,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);
>>
> 
> Thanks,
> 
> 	M.
> 

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

* [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
@ 2016-06-22 15:03       ` Andre Przywara
  0 siblings, 0 replies; 56+ messages in thread
From: Andre Przywara @ 2016-06-22 15:03 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 22/06/16 15:48, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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/vgic/vgic.h            |  13 ++++-
>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>>  8 files changed, 187 insertions(+), 9 deletions(-)
>>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
>>
>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>> index a7a958c..2755d17 100644
>> --- a/arch/arm64/kvm/Makefile
>> +++ b/arch/arm64/kvm/Makefile
>> @@ -30,6 +30,7 @@ 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
>>  else
>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>> index dc7f2fd..a66ba9a 100644
>> --- a/include/kvm/vgic/vgic.h
>> +++ b/include/kvm/vgic/vgic.h
>> @@ -111,12 +111,23 @@ struct vgic_register_region;
>>  
>>  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;
>>  	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;
>> +};
> 
> You may want to move this definition before struct vgic_io_device, some
> compilers are more picky than some others.

Mmh, I just see that this is actually circular here.
So shall I add an opaque declaration for struct vgic_its above?

> 
>> +
>>  struct vgic_dist {
>>  	bool			in_kernel;
>>  	bool			ready;
>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>> index 64e8c70..92d5da8 100644
>> --- a/include/linux/irqchip/arm-gic-v3.h
>> +++ b/include/linux/irqchip/arm-gic-v3.h
>> @@ -176,6 +176,7 @@
>>  #define GITS_CWRITER			0x0088
>>  #define GITS_CREADR			0x0090
>>  #define GITS_BASER			0x0100
>> +#define GITS_IDREGS_BASE		0xffd0
> 
> Please make all the changes to the core GIC code in a separate patch,
> which I can pick independently.

Sure.

> 
>>  #define GITS_PIDR2			GICR_PIDR2
>>  
>>  #define GITS_TRANSLATER			0x10040
>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>> new file mode 100644
>> index 0000000..c02d9df
>> --- /dev/null
>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>> @@ -0,0 +1,107 @@
>> +/*
>> + * 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,					\
>> +	.read = NULL,						\
>> +	.write = NULL,						\
>> +	.its_read = rd,						\
>> +	.its_write = wr,					\
> 
> Meh. That's not very nice... Why can't they be a union?

We can, but we would need another differentiator then (as you suggested
below).

>> +}
>> +
>> +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 */
>> +}
>> +
>> +struct vgic_register_region its_registers[] = {
> 
> Shouldn't this be static?

Indeed, looks like a rebase artefact (I had the registration in another
file during some rework attempts).

> 
>> +	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 vits_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->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;
>> +}
>> +
>> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>> +{
>> +
>> +	its->enabled = false;
>> +}
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>> index 8cd7190..4eee0d7 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)
>>  {
>> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>>  	vgic_put_irq(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)
>>  {
>> @@ -325,7 +361,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,
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
>> index 4050c1c..c98a4fd 100644
>> --- a/virt/kvm/arm/vgic/vgic-mmio.c
>> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
>> @@ -461,8 +461,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);
>> @@ -471,8 +470,15 @@ 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);
>> +	if (region->read) {
>> +		struct kvm_vcpu *r_vcpu;
>> +
>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>> +		data = region->read(r_vcpu, addr, len);
>> +	} else if (region->its_read) {
>> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>> +	}
> 
> I'd rather have a flag somewhere that allows us to identify the type of
> device we're dealing with. You could have something like:
> 
> enum iodev_type {
> 	IODEV_VGIC_DISTRIBUTOR,
> 	IODEV_VGIC_REDISTRIBUTOR,
> 	IODEV_VGIC_ITS,
> };
> 
> assign the type to each region we have, and then have something like:
> 
> 	switch (region->iodev_type) {
> 	case IODEV_VGIC_DISTRIBUTOR:
> 		data = region->read(vcpu, addr, len);
> 		break;
> 	case IODEV_VGIC_REDISTRIBUTOR:
> 		data = region->read(iodev->redist_vcpu, addr, len);
> 		break;
> 	case IODEV_VGIC_ITS:
> 		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
> 		break;
> 	}
> 
> It would make it extensible *and* readable.

Agreed, will fix it.

Cheers,
Andre.

>> +
>>  	vgic_data_host_to_mmio_bus(val, len, data);
>>  	return 0;
>>  }
>> @@ -482,7 +488,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,
>> @@ -493,8 +498,14 @@ 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);
>> +	if (region->write) {
>> +		struct kvm_vcpu *r_vcpu;
>> +
>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>> +		region->write(r_vcpu, addr, len, data);
>> +	} else if (region->its_write) {
>> +		region->its_write(vcpu->kvm, iodev->its, addr, len, data);
>> +	}
> 
> Same here.
> 
>>  	return 0;
>>  }
>>  
>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
>> index 8509014..6dfc2f0 100644
>> --- a/virt/kvm/arm/vgic/vgic-mmio.h
>> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
>> @@ -25,6 +25,10 @@ struct vgic_register_region {
>>  			      unsigned int len);
>>  	void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
>>  		      unsigned long val);
>> +	unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
>> +				  gpa_t addr, unsigned int len);
>> +	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 fa2d225..b09590d 100644
>> --- a/virt/kvm/arm/vgic/vgic.h
>> +++ b/virt/kvm/arm/vgic/vgic.h
>> @@ -76,6 +76,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)
>>  {
>> @@ -127,6 +128,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);
>>
> 
> Thanks,
> 
> 	M.
> 

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

* Re: [PATCH v6 08/15] KVM: arm64: introduce new KVM ITS device
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22 15:23     ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 15:23 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: linux-arm-kernel, kvmarm, kvm

On 17/06/16 13:08, 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 malloc 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 MMIO accesses.
> 
> 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/vgic/vgic.h                        |   1 +
>  include/uapi/linux/kvm.h                       |   2 +
>  virt/kvm/arm/vgic/vgic-its.c                   | 131 ++++++++++++++++++++++++-
>  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, 168 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 a268c85..f4a953e 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..f8c257b 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		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/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index a66ba9a..979d8c3 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -158,6 +158,7 @@ struct vgic_dist {
>  
>  	struct vgic_io_device	dist_iodev;
>  
> +	bool			has_its;
>  	/*
>  	 * Contains the address 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 c02d9df..4ae3c82 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>
>  
> @@ -81,7 +82,7 @@ struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> -int vits_register(struct kvm *kvm, struct vgic_its *its)
> +static int vits_register(struct kvm *kvm, struct vgic_its *its)
>  {
>  	struct vgic_io_device *iodev = &its->iodev;
>  	int ret;
> @@ -100,8 +101,134 @@ int vits_register(struct kvm *kvm, struct vgic_its *its)
>  	return ret;
>  }
>  
> -void vits_destroy(struct kvm *kvm, struct vgic_its *its)
> +static void vits_destroy(struct kvm *kvm, struct vgic_its *its)

Maybe move these hunks to the previous patch?

>  {
>  
>  	its->enabled = false;
>  }
> +
> +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->enabled = false;
> +
> +	dev->private = its;
> +
> +	return 0;
> +}
> +
> +static void vgic_its_destroy(struct kvm_device *dev)
> +{
> +	struct vgic_its *its = dev->private;
> +
> +	vits_destroy(dev->kvm, its);

If that's the only use of vits_destroy (and given that it is not really
doing much), can we inline it here (or alternatively get rid of it
altogether)?

> +
> +	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 (copy_from_user(&addr, uaddr, sizeof(addr)))
> +			return -EFAULT;
> +
> +		ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base,
> +					addr, SZ_64K);
> +		if (ret)
> +			return ret;
> +
> +		its->vgic_its_base = addr;
> +
> +		return 0;
> +	}
> +	case KVM_DEV_ARM_VGIC_GRP_CTRL:
> +		switch (attr->attr) {
> +		case KVM_DEV_ARM_VGIC_CTRL_INIT:
> +			return vits_register(dev->kvm, its);
> +		}
> +		break;
> +	}
> +	return -ENXIO;
> +}
> +
> +static int vgic_its_get_attr(struct kvm_device *dev,
> +			     struct kvm_device_attr *attr)
> +{
> +	switch (attr->group) {
> +	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
> +		struct vgic_its *its = dev->private;
> +		u64 addr = its->vgic_its_base;
> +		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
> +		unsigned long type = (unsigned long)attr->attr;
> +
> +		if (type != KVM_VGIC_ITS_ADDR_TYPE)
> +			return -ENODEV;
> +
> +		if (copy_to_user(uaddr, &addr, sizeof(addr)))
> +			return -EFAULT;
> +		break;
> +	default:
> +		return -ENXIO;
> +	}
> +	}
> +
> +	return 0;
> +}
> +
> +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 4eee0d7..3a72308 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 b09590d..24150f7 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -46,6 +46,9 @@ static inline void vgic_put_irq(struct vgic_irq *irq)
>  bool vgic_queue_irq_put(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);
> @@ -77,6 +80,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)
>  {
> @@ -134,6 +138,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] 56+ messages in thread

* [PATCH v6 08/15] KVM: arm64: introduce new KVM ITS device
@ 2016-06-22 15:23     ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 15:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/06/16 13:08, 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 malloc 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 MMIO accesses.
> 
> 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/vgic/vgic.h                        |   1 +
>  include/uapi/linux/kvm.h                       |   2 +
>  virt/kvm/arm/vgic/vgic-its.c                   | 131 ++++++++++++++++++++++++-
>  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, 168 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 a268c85..f4a953e 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..f8c257b 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		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/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index a66ba9a..979d8c3 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/vgic.h
> @@ -158,6 +158,7 @@ struct vgic_dist {
>  
>  	struct vgic_io_device	dist_iodev;
>  
> +	bool			has_its;
>  	/*
>  	 * Contains the address 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 c02d9df..4ae3c82 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>
>  
> @@ -81,7 +82,7 @@ struct vgic_register_region its_registers[] = {
>  		VGIC_ACCESS_32bit),
>  };
>  
> -int vits_register(struct kvm *kvm, struct vgic_its *its)
> +static int vits_register(struct kvm *kvm, struct vgic_its *its)
>  {
>  	struct vgic_io_device *iodev = &its->iodev;
>  	int ret;
> @@ -100,8 +101,134 @@ int vits_register(struct kvm *kvm, struct vgic_its *its)
>  	return ret;
>  }
>  
> -void vits_destroy(struct kvm *kvm, struct vgic_its *its)
> +static void vits_destroy(struct kvm *kvm, struct vgic_its *its)

Maybe move these hunks to the previous patch?

>  {
>  
>  	its->enabled = false;
>  }
> +
> +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->enabled = false;
> +
> +	dev->private = its;
> +
> +	return 0;
> +}
> +
> +static void vgic_its_destroy(struct kvm_device *dev)
> +{
> +	struct vgic_its *its = dev->private;
> +
> +	vits_destroy(dev->kvm, its);

If that's the only use of vits_destroy (and given that it is not really
doing much), can we inline it here (or alternatively get rid of it
altogether)?

> +
> +	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 (copy_from_user(&addr, uaddr, sizeof(addr)))
> +			return -EFAULT;
> +
> +		ret = vgic_check_ioaddr(dev->kvm, &its->vgic_its_base,
> +					addr, SZ_64K);
> +		if (ret)
> +			return ret;
> +
> +		its->vgic_its_base = addr;
> +
> +		return 0;
> +	}
> +	case KVM_DEV_ARM_VGIC_GRP_CTRL:
> +		switch (attr->attr) {
> +		case KVM_DEV_ARM_VGIC_CTRL_INIT:
> +			return vits_register(dev->kvm, its);
> +		}
> +		break;
> +	}
> +	return -ENXIO;
> +}
> +
> +static int vgic_its_get_attr(struct kvm_device *dev,
> +			     struct kvm_device_attr *attr)
> +{
> +	switch (attr->group) {
> +	case KVM_DEV_ARM_VGIC_GRP_ADDR: {
> +		struct vgic_its *its = dev->private;
> +		u64 addr = its->vgic_its_base;
> +		u64 __user *uaddr = (u64 __user *)(long)attr->addr;
> +		unsigned long type = (unsigned long)attr->attr;
> +
> +		if (type != KVM_VGIC_ITS_ADDR_TYPE)
> +			return -ENODEV;
> +
> +		if (copy_to_user(uaddr, &addr, sizeof(addr)))
> +			return -EFAULT;
> +		break;
> +	default:
> +		return -ENXIO;
> +	}
> +	}
> +
> +	return 0;
> +}
> +
> +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 4eee0d7..3a72308 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 b09590d..24150f7 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -46,6 +46,9 @@ static inline void vgic_put_irq(struct vgic_irq *irq)
>  bool vgic_queue_irq_put(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);
> @@ -77,6 +80,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)
>  {
> @@ -134,6 +138,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] 56+ messages in thread

* Re: [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
  2016-06-22 15:03       ` Andre Przywara
@ 2016-06-22 15:24         ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 15:24 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: linux-arm-kernel, kvmarm, kvm

On 22/06/16 16:03, Andre Przywara wrote:
> Hi,
> 
> On 22/06/16 15:48, Marc Zyngier wrote:
>> On 17/06/16 13:08, Andre Przywara wrote:
>>> 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/vgic/vgic.h            |  13 ++++-
>>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>>>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>>>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>>>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>>>  8 files changed, 187 insertions(+), 9 deletions(-)
>>>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
>>>
>>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>>> index a7a958c..2755d17 100644
>>> --- a/arch/arm64/kvm/Makefile
>>> +++ b/arch/arm64/kvm/Makefile
>>> @@ -30,6 +30,7 @@ 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
>>>  else
>>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
>>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>>> index dc7f2fd..a66ba9a 100644
>>> --- a/include/kvm/vgic/vgic.h
>>> +++ b/include/kvm/vgic/vgic.h
>>> @@ -111,12 +111,23 @@ struct vgic_register_region;
>>>  
>>>  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;
>>>  	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;
>>> +};
>>
>> You may want to move this definition before struct vgic_io_device, some
>> compilers are more picky than some others.
> 
> Mmh, I just see that this is actually circular here.

Ah, indeed.

> So shall I add an opaque declaration for struct vgic_its above?

Yes please.

>>
>>> +
>>>  struct vgic_dist {
>>>  	bool			in_kernel;
>>>  	bool			ready;
>>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>>> index 64e8c70..92d5da8 100644
>>> --- a/include/linux/irqchip/arm-gic-v3.h
>>> +++ b/include/linux/irqchip/arm-gic-v3.h
>>> @@ -176,6 +176,7 @@
>>>  #define GITS_CWRITER			0x0088
>>>  #define GITS_CREADR			0x0090
>>>  #define GITS_BASER			0x0100
>>> +#define GITS_IDREGS_BASE		0xffd0
>>
>> Please make all the changes to the core GIC code in a separate patch,
>> which I can pick independently.
> 
> Sure.
> 
>>
>>>  #define GITS_PIDR2			GICR_PIDR2
>>>  
>>>  #define GITS_TRANSLATER			0x10040
>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>> new file mode 100644
>>> index 0000000..c02d9df
>>> --- /dev/null
>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>>> @@ -0,0 +1,107 @@
>>> +/*
>>> + * 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,					\
>>> +	.read = NULL,						\
>>> +	.write = NULL,						\
>>> +	.its_read = rd,						\
>>> +	.its_write = wr,					\
>>
>> Meh. That's not very nice... Why can't they be a union?
> 
> We can, but we would need another differentiator then (as you suggested
> below).
> 
>>> +}
>>> +
>>> +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 */
>>> +}
>>> +
>>> +struct vgic_register_region its_registers[] = {
>>
>> Shouldn't this be static?
> 
> Indeed, looks like a rebase artefact (I had the registration in another
> file during some rework attempts).
> 
>>
>>> +	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 vits_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->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;
>>> +}
>>> +
>>> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>>> +{
>>> +
>>> +	its->enabled = false;
>>> +}
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>>> index 8cd7190..4eee0d7 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)
>>>  {
>>> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>>>  	vgic_put_irq(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)
>>>  {
>>> @@ -325,7 +361,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,
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
>>> index 4050c1c..c98a4fd 100644
>>> --- a/virt/kvm/arm/vgic/vgic-mmio.c
>>> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
>>> @@ -461,8 +461,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);
>>> @@ -471,8 +470,15 @@ 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);
>>> +	if (region->read) {
>>> +		struct kvm_vcpu *r_vcpu;
>>> +
>>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>>> +		data = region->read(r_vcpu, addr, len);
>>> +	} else if (region->its_read) {
>>> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>>> +	}
>>
>> I'd rather have a flag somewhere that allows us to identify the type of
>> device we're dealing with. You could have something like:
>>
>> enum iodev_type {
>> 	IODEV_VGIC_DISTRIBUTOR,
>> 	IODEV_VGIC_REDISTRIBUTOR,
>> 	IODEV_VGIC_ITS,
>> };
>>
>> assign the type to each region we have, and then have something like:
>>
>> 	switch (region->iodev_type) {
>> 	case IODEV_VGIC_DISTRIBUTOR:
>> 		data = region->read(vcpu, addr, len);
>> 		break;
>> 	case IODEV_VGIC_REDISTRIBUTOR:
>> 		data = region->read(iodev->redist_vcpu, addr, len);
>> 		break;
>> 	case IODEV_VGIC_ITS:
>> 		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>> 		break;
>> 	}
>>
>> It would make it extensible *and* readable.
> 
> Agreed, will fix it.

Cool. Thanks,

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

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

* [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework
@ 2016-06-22 15:24         ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 15:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/06/16 16:03, Andre Przywara wrote:
> Hi,
> 
> On 22/06/16 15:48, Marc Zyngier wrote:
>> On 17/06/16 13:08, Andre Przywara wrote:
>>> 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/vgic/vgic.h            |  13 ++++-
>>>  include/linux/irqchip/arm-gic-v3.h |   1 +
>>>  virt/kvm/arm/vgic/vgic-its.c       | 107 +++++++++++++++++++++++++++++++++++++
>>>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  38 ++++++++++++-
>>>  virt/kvm/arm/vgic/vgic-mmio.c      |  25 ++++++---
>>>  virt/kvm/arm/vgic/vgic-mmio.h      |   4 ++
>>>  virt/kvm/arm/vgic/vgic.h           |   7 +++
>>>  8 files changed, 187 insertions(+), 9 deletions(-)
>>>  create mode 100644 virt/kvm/arm/vgic/vgic-its.c
>>>
>>> diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
>>> index a7a958c..2755d17 100644
>>> --- a/arch/arm64/kvm/Makefile
>>> +++ b/arch/arm64/kvm/Makefile
>>> @@ -30,6 +30,7 @@ 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
>>>  else
>>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
>>>  kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
>>> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
>>> index dc7f2fd..a66ba9a 100644
>>> --- a/include/kvm/vgic/vgic.h
>>> +++ b/include/kvm/vgic/vgic.h
>>> @@ -111,12 +111,23 @@ struct vgic_register_region;
>>>  
>>>  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;
>>>  	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;
>>> +};
>>
>> You may want to move this definition before struct vgic_io_device, some
>> compilers are more picky than some others.
> 
> Mmh, I just see that this is actually circular here.

Ah, indeed.

> So shall I add an opaque declaration for struct vgic_its above?

Yes please.

>>
>>> +
>>>  struct vgic_dist {
>>>  	bool			in_kernel;
>>>  	bool			ready;
>>> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
>>> index 64e8c70..92d5da8 100644
>>> --- a/include/linux/irqchip/arm-gic-v3.h
>>> +++ b/include/linux/irqchip/arm-gic-v3.h
>>> @@ -176,6 +176,7 @@
>>>  #define GITS_CWRITER			0x0088
>>>  #define GITS_CREADR			0x0090
>>>  #define GITS_BASER			0x0100
>>> +#define GITS_IDREGS_BASE		0xffd0
>>
>> Please make all the changes to the core GIC code in a separate patch,
>> which I can pick independently.
> 
> Sure.
> 
>>
>>>  #define GITS_PIDR2			GICR_PIDR2
>>>  
>>>  #define GITS_TRANSLATER			0x10040
>>> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
>>> new file mode 100644
>>> index 0000000..c02d9df
>>> --- /dev/null
>>> +++ b/virt/kvm/arm/vgic/vgic-its.c
>>> @@ -0,0 +1,107 @@
>>> +/*
>>> + * 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,					\
>>> +	.read = NULL,						\
>>> +	.write = NULL,						\
>>> +	.its_read = rd,						\
>>> +	.its_write = wr,					\
>>
>> Meh. That's not very nice... Why can't they be a union?
> 
> We can, but we would need another differentiator then (as you suggested
> below).
> 
>>> +}
>>> +
>>> +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 */
>>> +}
>>> +
>>> +struct vgic_register_region its_registers[] = {
>>
>> Shouldn't this be static?
> 
> Indeed, looks like a rebase artefact (I had the registration in another
> file during some rework attempts).
> 
>>
>>> +	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 vits_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->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;
>>> +}
>>> +
>>> +void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>>> +{
>>> +
>>> +	its->enabled = false;
>>> +}
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
>>> index 8cd7190..4eee0d7 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)
>>>  {
>>> @@ -126,6 +136,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
>>>  	vgic_put_irq(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)
>>>  {
>>> @@ -325,7 +361,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,
>>> diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
>>> index 4050c1c..c98a4fd 100644
>>> --- a/virt/kvm/arm/vgic/vgic-mmio.c
>>> +++ b/virt/kvm/arm/vgic/vgic-mmio.c
>>> @@ -461,8 +461,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);
>>> @@ -471,8 +470,15 @@ 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);
>>> +	if (region->read) {
>>> +		struct kvm_vcpu *r_vcpu;
>>> +
>>> +		r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
>>> +		data = region->read(r_vcpu, addr, len);
>>> +	} else if (region->its_read) {
>>> +		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>>> +	}
>>
>> I'd rather have a flag somewhere that allows us to identify the type of
>> device we're dealing with. You could have something like:
>>
>> enum iodev_type {
>> 	IODEV_VGIC_DISTRIBUTOR,
>> 	IODEV_VGIC_REDISTRIBUTOR,
>> 	IODEV_VGIC_ITS,
>> };
>>
>> assign the type to each region we have, and then have something like:
>>
>> 	switch (region->iodev_type) {
>> 	case IODEV_VGIC_DISTRIBUTOR:
>> 		data = region->read(vcpu, addr, len);
>> 		break;
>> 	case IODEV_VGIC_REDISTRIBUTOR:
>> 		data = region->read(iodev->redist_vcpu, addr, len);
>> 		break;
>> 	case IODEV_VGIC_ITS:
>> 		data = region->its_read(vcpu->kvm, iodev->its, addr, len);
>> 		break;
>> 	}
>>
>> It would make it extensible *and* readable.
> 
> Agreed, will fix it.

Cool. Thanks,

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

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

* Re: [PATCH v6 09/15] KVM: arm64: implement basic ITS register handlers
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22 16:19     ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 16:19 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: linux-arm-kernel, kvmarm, kvm

On 17/06/16 13:08, 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/vgic/vgic.h            |  15 ++
>  include/linux/irqchip/arm-gic-v3.h |  11 ++
>  virt/kvm/arm/vgic/vgic-its.c       | 343 +++++++++++++++++++++++++++++++++++--
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  15 +-
>  virt/kvm/arm/vgic/vgic-mmio.h      |  10 ++
>  5 files changed, 380 insertions(+), 14 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index 979d8c3..891775e 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/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
> @@ -126,6 +127,20 @@ struct vgic_its {
>  
>  	bool			enabled;
>  	struct vgic_io_device	iodev;
> +
> +	u64			baser_device_table;
> +	u64			baser_coll_table;

Maybe add a comment saying that these correspond to GITS_BASER{0,1}?

> +
> +	/* 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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index 92d5da8..fe5a7fe 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -177,16 +177,26 @@
>  #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_TYPER_HWCOLLCNT_SHIFT	24
>  
>  #define GITS_CBASER_VALID		(1UL << 63)
>  #define GITS_CBASER_nCnB		(0UL << 59)
> @@ -214,6 +224,7 @@
>  #define GITS_BASER_WaWb			(5UL << 59)
>  #define GITS_BASER_RaWaWt		(6UL << 59)
>  #define GITS_BASER_RaWaWb		(7UL << 59)
> +#define GITS_BASER_CACHEABILITY_SHIFT	(59)

If you start adding this, please express all the GITS_BASER_*
cacheability attributes in terms of the shift you've defined.

>  #define GITS_BASER_CACHEABILITY_MASK	(7UL << 59)
>  #define GITS_BASER_TYPE_SHIFT		(56)
>  #define GITS_BASER_TYPE(r)		(((r) >> GITS_BASER_TYPE_SHIFT) & 7)
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 4ae3c82..d7f8834 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,289 @@
>  #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)-1)

Nit: I always shiver when I see -1 cast to an unsigned value. How about
((u32)~0) instead?

> +
> +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))
> +#define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))

Warning, this is a slippery one. From the spec:

<quote>
When Page_Size is 64KB:
- Bits[47:16] of the register provide bits[47:16] of the base physical
address of the table.
- Bits[15:12] of the register provide bits[51:48] of the base physical
address of the table.
- Bits[15:0] of the base physical address are 0.

In implementations that support fewer than 52 bits of physical address,
any unimplemented upper bits may be RAZ/WI.
</quote>

Can you please define what you are planning to support here?
CBASER_ADDRESS tends to indicate that you're in for 52bit PAs, but
BASER_ADDRESS suggests otherwise.

> +
> +#define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
> +
> +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.
> +	 * As we hold all LPI mapping related data structures in the kernel
> +	 * (mimicing what the spec describes as "held in hardware"), we can
> +	 * claim to support a high number of "hardware" mapped collections
> +	 * (since we use linked lists to store them).
> +	 * However to avoid memory waste, we keep the number of IDBits and
> +	 * DevBits low - as least for the time being.
> +	 */
> +	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;

I though I had convinced you to get rid of the HW collections??? What is
happening here?

> +	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;
> +}
> +
> +static void its_free_itte(struct its_itte *itte)
> +{
> +	list_del(&itte->itte_list);
> +	kfree(itte);

Please document the locking requirements.

> +}
> +
> +static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
> +			       u64 *its_cmd)
> +{
> +	return -ENODEV;
> +}
> +
> +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);
> +	vgic_sanitise_its_baser(&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)
> +
> +/*
> + * 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;
> +	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, 32);

How about having a #define for this 32? Or even better, make struct
its_cmd_block available and use it all over the map?

> +		if (ret) {
> +			/*
> +			 * Gah, we are screwed. Either the guest programmed
> +			 * bogus values in CBASER or something else went
> +			 * wrong from which we cannot easily recover.
> +			 * Reset CWRITER to the command that we have finished
> +			 * processing and return.
> +			 */
> +			its->cwriter = its->creadr;
> +			break;

Have you looked at 6.3.2 (Command errors) recently? It now describes the
acceptable behaviours (yeah, it took a long time...), and the behaviour
you have here doesn't seem to be acceptable (it is a mix between
"ignore" and "stall"). I suggest that you implement ignore (dead easy),
and we can upgrade it to stall later.

> +		}
> +		vits_handle_command(kvm, its, cmd_buf);
> +
> +		its->creadr += 32;
> +		if (its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser))
> +			its->creadr = 0;
> +	}
> +
> +	mutex_unlock(&its->cmd_lock);

Overall, this function looks much better than the previous versions.

> +}
> +
> +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	\
> +	((0x1fLL << GITS_BASER_ENTRY_SIZE_SHIFT) | \
> +	 (0x07LL << GITS_BASER_TYPE_SHIFT))

GENMASK_ULL?

> +static void vgic_mmio_write_its_baser(struct kvm *kvm,
> +				      struct vgic_its *its,
> +				      gpa_t addr, unsigned int len,
> +				      unsigned long val)
> +{
> +	u64 reg, *regptr;
> +	u64 entry_size, device_type;
> +
> +	/* 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;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	reg = update_64bit_reg(*regptr, addr & 7, len, val);
> +	reg &= ~GITS_BASER_RO_MASK;
> +	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +	reg |= device_type << GITS_BASER_TYPE_SHIFT;
> +	vgic_sanitise_its_baser(&reg);
> +
> +	*regptr = reg;
> +}
> +
>  #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
>  {								\
>  	.reg_offset = off,					\
> @@ -43,8 +327,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;
>  }
> @@ -57,28 +341,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
>  
>  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),
>  };
>  
> @@ -103,10 +387,40 @@ static int vits_register(struct kvm *kvm, struct vgic_its *its)
>  
>  static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>  {
> +	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(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);
>  
>  	its->enabled = false;
>  }
>  
> +#define INITIAL_BASER_VALUE	(GITS_BASER_WaWb | GITS_BASER_PAGE_SIZE_64K)

How about the entry size? Shareability? The type? An ITS driver won't
even use this because the type is 0 (just boot a guest and tell me what
tables are allocated...). Grmbl...

> +
>  static int vgic_its_create(struct kvm_device *dev, u32 type)
>  {
>  	struct vgic_its *its;
> @@ -118,11 +432,20 @@ 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->enabled = false;
>  
> +	its->baser_device_table = INITIAL_BASER_VALUE;

I also asked for the device table to advertize indirect support. Can't
see it anywhere, unfortunately.

> +	its->baser_coll_table = INITIAL_BASER_VALUE;
> +
>  	dev->private = its;
>  
>  	return 0;
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 3a72308..da74d67 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;
> @@ -239,6 +239,13 @@ static void vgic_sanitise_redist_baser(u64 *reg)
>  	vgic_sanitise_outer_cacheability(reg, 56);
>  }
>  
> +void vgic_sanitise_its_baser(u64 *reg)
> +{
> +	vgic_sanitise_shareability(reg);
> +	vgic_sanitise_inner_cacheability(reg, GITS_BASER_CACHEABILITY_SHIFT);
> +	vgic_sanitise_outer_cacheability(reg, 53);
> +}

As previous patches, drop these and do in in the accessor.

> +
>  #define PROPBASER_RES0_MASK 0xf8f0000000000060
>  #define PENDBASER_RES0_MASK 0xb8f000000000f07f
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
> index 6dfc2f0..f6fd662 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.h
> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
> @@ -91,6 +91,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);
>  
> @@ -151,4 +157,8 @@ 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
> +void vgic_sanitise_its_baser(u64 *reg);
> +#endif
> +
>  #endif
> 

Thanks,

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

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

* [PATCH v6 09/15] KVM: arm64: implement basic ITS register handlers
@ 2016-06-22 16:19     ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 16:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/06/16 13:08, 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/vgic/vgic.h            |  15 ++
>  include/linux/irqchip/arm-gic-v3.h |  11 ++
>  virt/kvm/arm/vgic/vgic-its.c       | 343 +++++++++++++++++++++++++++++++++++--
>  virt/kvm/arm/vgic/vgic-mmio-v3.c   |  15 +-
>  virt/kvm/arm/vgic/vgic-mmio.h      |  10 ++
>  5 files changed, 380 insertions(+), 14 deletions(-)
> 
> diff --git a/include/kvm/vgic/vgic.h b/include/kvm/vgic/vgic.h
> index 979d8c3..891775e 100644
> --- a/include/kvm/vgic/vgic.h
> +++ b/include/kvm/vgic/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
> @@ -126,6 +127,20 @@ struct vgic_its {
>  
>  	bool			enabled;
>  	struct vgic_io_device	iodev;
> +
> +	u64			baser_device_table;
> +	u64			baser_coll_table;

Maybe add a comment saying that these correspond to GITS_BASER{0,1}?

> +
> +	/* 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/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index 92d5da8..fe5a7fe 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -177,16 +177,26 @@
>  #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_TYPER_HWCOLLCNT_SHIFT	24
>  
>  #define GITS_CBASER_VALID		(1UL << 63)
>  #define GITS_CBASER_nCnB		(0UL << 59)
> @@ -214,6 +224,7 @@
>  #define GITS_BASER_WaWb			(5UL << 59)
>  #define GITS_BASER_RaWaWt		(6UL << 59)
>  #define GITS_BASER_RaWaWb		(7UL << 59)
> +#define GITS_BASER_CACHEABILITY_SHIFT	(59)

If you start adding this, please express all the GITS_BASER_*
cacheability attributes in terms of the shift you've defined.

>  #define GITS_BASER_CACHEABILITY_MASK	(7UL << 59)
>  #define GITS_BASER_TYPE_SHIFT		(56)
>  #define GITS_BASER_TYPE(r)		(((r) >> GITS_BASER_TYPE_SHIFT) & 7)
> diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
> index 4ae3c82..d7f8834 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,289 @@
>  #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)-1)

Nit: I always shiver when I see -1 cast to an unsigned value. How about
((u32)~0) instead?

> +
> +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))
> +#define BASER_ADDRESS(x)	((x) & GENMASK_ULL(47, 12))

Warning, this is a slippery one. From the spec:

<quote>
When Page_Size is 64KB:
- Bits[47:16] of the register provide bits[47:16] of the base physical
address of the table.
- Bits[15:12] of the register provide bits[51:48] of the base physical
address of the table.
- Bits[15:0] of the base physical address are 0.

In implementations that support fewer than 52 bits of physical address,
any unimplemented upper bits may be RAZ/WI.
</quote>

Can you please define what you are planning to support here?
CBASER_ADDRESS tends to indicate that you're in for 52bit PAs, but
BASER_ADDRESS suggests otherwise.

> +
> +#define ITS_FRAME(addr) ((addr) & ~(SZ_64K - 1))
> +
> +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.
> +	 * As we hold all LPI mapping related data structures in the kernel
> +	 * (mimicing what the spec describes as "held in hardware"), we can
> +	 * claim to support a high number of "hardware" mapped collections
> +	 * (since we use linked lists to store them).
> +	 * However to avoid memory waste, we keep the number of IDBits and
> +	 * DevBits low - as least for the time being.
> +	 */
> +	reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT;

I though I had convinced you to get rid of the HW collections??? What is
happening here?

> +	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;
> +}
> +
> +static void its_free_itte(struct its_itte *itte)
> +{
> +	list_del(&itte->itte_list);
> +	kfree(itte);

Please document the locking requirements.

> +}
> +
> +static int vits_handle_command(struct kvm *kvm, struct vgic_its *its,
> +			       u64 *its_cmd)
> +{
> +	return -ENODEV;
> +}
> +
> +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);
> +	vgic_sanitise_its_baser(&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)
> +
> +/*
> + * 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;
> +	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, 32);

How about having a #define for this 32? Or even better, make struct
its_cmd_block available and use it all over the map?

> +		if (ret) {
> +			/*
> +			 * Gah, we are screwed. Either the guest programmed
> +			 * bogus values in CBASER or something else went
> +			 * wrong from which we cannot easily recover.
> +			 * Reset CWRITER to the command that we have finished
> +			 * processing and return.
> +			 */
> +			its->cwriter = its->creadr;
> +			break;

Have you looked at 6.3.2 (Command errors) recently? It now describes the
acceptable behaviours (yeah, it took a long time...), and the behaviour
you have here doesn't seem to be acceptable (it is a mix between
"ignore" and "stall"). I suggest that you implement ignore (dead easy),
and we can upgrade it to stall later.

> +		}
> +		vits_handle_command(kvm, its, cmd_buf);
> +
> +		its->creadr += 32;
> +		if (its->creadr == ITS_CMD_BUFFER_SIZE(its->cbaser))
> +			its->creadr = 0;
> +	}
> +
> +	mutex_unlock(&its->cmd_lock);

Overall, this function looks much better than the previous versions.

> +}
> +
> +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	\
> +	((0x1fLL << GITS_BASER_ENTRY_SIZE_SHIFT) | \
> +	 (0x07LL << GITS_BASER_TYPE_SHIFT))

GENMASK_ULL?

> +static void vgic_mmio_write_its_baser(struct kvm *kvm,
> +				      struct vgic_its *its,
> +				      gpa_t addr, unsigned int len,
> +				      unsigned long val)
> +{
> +	u64 reg, *regptr;
> +	u64 entry_size, device_type;
> +
> +	/* 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;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	reg = update_64bit_reg(*regptr, addr & 7, len, val);
> +	reg &= ~GITS_BASER_RO_MASK;
> +	reg |= (entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT;
> +	reg |= device_type << GITS_BASER_TYPE_SHIFT;
> +	vgic_sanitise_its_baser(&reg);
> +
> +	*regptr = reg;
> +}
> +
>  #define REGISTER_ITS_DESC(off, rd, wr, length, acc)		\
>  {								\
>  	.reg_offset = off,					\
> @@ -43,8 +327,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;
>  }
> @@ -57,28 +341,28 @@ static void its_mmio_write_wi(struct kvm *kvm, struct vgic_its *its,
>  
>  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),
>  };
>  
> @@ -103,10 +387,40 @@ static int vits_register(struct kvm *kvm, struct vgic_its *its)
>  
>  static void vits_destroy(struct kvm *kvm, struct vgic_its *its)
>  {
> +	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(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);
>  
>  	its->enabled = false;
>  }
>  
> +#define INITIAL_BASER_VALUE	(GITS_BASER_WaWb | GITS_BASER_PAGE_SIZE_64K)

How about the entry size? Shareability? The type? An ITS driver won't
even use this because the type is 0 (just boot a guest and tell me what
tables are allocated...). Grmbl...

> +
>  static int vgic_its_create(struct kvm_device *dev, u32 type)
>  {
>  	struct vgic_its *its;
> @@ -118,11 +432,20 @@ 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->enabled = false;
>  
> +	its->baser_device_table = INITIAL_BASER_VALUE;

I also asked for the device table to advertize indirect support. Can't
see it anywhere, unfortunately.

> +	its->baser_coll_table = INITIAL_BASER_VALUE;
> +
>  	dev->private = its;
>  
>  	return 0;
> diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
> index 3a72308..da74d67 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;
> @@ -239,6 +239,13 @@ static void vgic_sanitise_redist_baser(u64 *reg)
>  	vgic_sanitise_outer_cacheability(reg, 56);
>  }
>  
> +void vgic_sanitise_its_baser(u64 *reg)
> +{
> +	vgic_sanitise_shareability(reg);
> +	vgic_sanitise_inner_cacheability(reg, GITS_BASER_CACHEABILITY_SHIFT);
> +	vgic_sanitise_outer_cacheability(reg, 53);
> +}

As previous patches, drop these and do in in the accessor.

> +
>  #define PROPBASER_RES0_MASK 0xf8f0000000000060
>  #define PENDBASER_RES0_MASK 0xb8f000000000f07f
>  
> diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h
> index 6dfc2f0..f6fd662 100644
> --- a/virt/kvm/arm/vgic/vgic-mmio.h
> +++ b/virt/kvm/arm/vgic/vgic-mmio.h
> @@ -91,6 +91,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);
>  
> @@ -151,4 +157,8 @@ 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
> +void vgic_sanitise_its_baser(u64 *reg);
> +#endif
> +
>  #endif
> 

Thanks,

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

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

* Re: [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
  2016-06-17 12:08   ` Andre Przywara
@ 2016-06-22 16:26     ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 16:26 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: kvmarm, kvm, linux-arm-kernel

On 17/06/16 13:08, Andre Przywara wrote:
> 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
> iterates the list using RCU list primitives, so it's safe to be called
> from an non-preemptible context like the KVM exit/entry path.
> 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_its_get_lpi() from vgic_get_irq(), so
> the VGIC code gets transparently access to LPIs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

I'm skipping the review of this particular patch until you've switched
to the kref API.

Thanks,

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

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

* [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
@ 2016-06-22 16:26     ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 16:26 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/06/16 13:08, Andre Przywara wrote:
> 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
> iterates the list using RCU list primitives, so it's safe to be called
> from an non-preemptible context like the KVM exit/entry path.
> 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_its_get_lpi() from vgic_get_irq(), so
> the VGIC code gets transparently access to LPIs.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>

I'm skipping the review of this particular patch until you've switched
to the kref API.

Thanks,

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

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

* Re: [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
  2016-06-22 16:26     ` Marc Zyngier
@ 2016-06-22 17:02       ` Marc Zyngier
  -1 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 17:02 UTC (permalink / raw)
  To: Andre Przywara, Christoffer Dall, Eric Auger
  Cc: kvmarm, linux-arm-kernel, kvm

On 22/06/16 17:26, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
>> iterates the list using RCU list primitives, so it's safe to be called
>> from an non-preemptible context like the KVM exit/entry path.
>> 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_its_get_lpi() from vgic_get_irq(), so
>> the VGIC code gets transparently access to LPIs.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> 
> I'm skipping the review of this particular patch until you've switched
> to the kref API.

As an added comment, and having gone through this and the following
patches, I cannot see what having an RCU list brings us if we're also
using refcounting. Taking a spinlock on the read side feels very dodgy
(what guarantees that we can make forward progress without messing with
the grace period?), and I feel that we need to keep things relatively
simple. Not simplistic, but slightly simpler.

Thanks,

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

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

* [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation
@ 2016-06-22 17:02       ` Marc Zyngier
  0 siblings, 0 replies; 56+ messages in thread
From: Marc Zyngier @ 2016-06-22 17:02 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/06/16 17:26, Marc Zyngier wrote:
> On 17/06/16 13:08, Andre Przywara wrote:
>> 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 an RCU protected list to hold all mapped LPIs. vgic_its_get_lpi()
>> iterates the list using RCU list primitives, so it's safe to be called
>> from an non-preemptible context like the KVM exit/entry path.
>> 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_its_get_lpi() from vgic_get_irq(), so
>> the VGIC code gets transparently access to LPIs.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> 
> I'm skipping the review of this particular patch until you've switched
> to the kref API.

As an added comment, and having gone through this and the following
patches, I cannot see what having an RCU list brings us if we're also
using refcounting. Taking a spinlock on the read side feels very dodgy
(what guarantees that we can make forward progress without messing with
the grace period?), and I feel that we need to keep things relatively
simple. Not simplistic, but slightly simpler.

Thanks,

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

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

end of thread, other threads:[~2016-06-22 17:02 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-17 12:08 [PATCH v6 00/15] KVM: arm64: GICv3 ITS emulation Andre Przywara
2016-06-17 12:08 ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 01/15] KVM: arm/arm64: move redistributor kvm_io_devices Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 02/15] KVM: arm/arm64: check return value for kvm_register_vgic_device Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 03/15] KVM: extend struct kvm_msi to hold a 32-bit device ID Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 04/15] KVM: arm/arm64: extend arch CAP checks to allow per-VM capabilities Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 05/15] KVM: arm/arm64: VGIC: add refcounting for IRQs Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22  8:18   ` Andre Przywara
2016-06-22  8:18     ` Andre Przywara
2016-06-22  8:26     ` Marc Zyngier
2016-06-22  8:26       ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 06/15] KVM: arm64: handle ITS related GICv3 redistributor registers Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22 14:07   ` Marc Zyngier
2016-06-22 14:07     ` Marc Zyngier
2016-06-22 14:39     ` Andre Przywara
2016-06-22 14:39       ` Andre Przywara
2016-06-22 14:59       ` Marc Zyngier
2016-06-22 14:59         ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 07/15] KVM: arm64: introduce ITS emulation file with MMIO framework Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22 14:48   ` Marc Zyngier
2016-06-22 14:48     ` Marc Zyngier
2016-06-22 15:03     ` Andre Przywara
2016-06-22 15:03       ` Andre Przywara
2016-06-22 15:24       ` Marc Zyngier
2016-06-22 15:24         ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 08/15] KVM: arm64: introduce new KVM ITS device Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22 15:23   ` Marc Zyngier
2016-06-22 15:23     ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 09/15] KVM: arm64: implement basic ITS register handlers Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22 16:19   ` Marc Zyngier
2016-06-22 16:19     ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 10/15] KVM: arm64: connect LPIs to the VGIC emulation Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-22 16:26   ` Marc Zyngier
2016-06-22 16:26     ` Marc Zyngier
2016-06-22 17:02     ` Marc Zyngier
2016-06-22 17:02       ` Marc Zyngier
2016-06-17 12:08 ` [PATCH v6 11/15] KVM: arm64: read initial LPI pending table Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 12/15] KVM: arm64: allow updates of LPI configuration table Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 13/15] KVM: arm64: implement ITS command queue command handlers Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 14/15] KVM: arm64: implement MSI injection in ITS emulation Andre Przywara
2016-06-17 12:08   ` Andre Przywara
2016-06-17 12:08 ` [PATCH v6 15/15] KVM: arm64: enable ITS emulation as a virtual MSI controller Andre Przywara
2016-06-17 12:08   ` Andre Przywara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.