Skip to contentSkip to conversation

KVM/ARM vGIC support

PATCH In review Christoffer Dall 2 participants 17 messages Jan 16, 2013 – Jan 21, 2013

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v6 cover KVM/ARM vGIC support

The following series implements support for the virtual generic interrupt controller architecture for KVM/ARM.

This patch series can also be pulled from:
git://github.com/virtualopensystems/linux-kvm-arm.git
branch: kvm-arm-v16-vgic

Changes since v5:
- Renamed sync_{to,from} to {flush,sync}_hwstate
- Fix read/write length for acceses shorter than words
- Cosmetic fixes

Changes since v4:
- Large number of commenting and formatting changes
- Don't rely on the vgic_hcr default value
- Fix bug in irq_fgx expand/collapse functions
- Sanitize use of bitmaps
- Do not use elrsr when queueing an interrupt
- Deassert SGIs and SPIs that have already been queued
- Set VMCR to 0 on vcpu init
- Compute number of list registers once at init time
- Restructure bytemap/bitmap accessor functions
- Improve readability by adding accessor functions
- Define GICH_ constants in gic.h and use these in the vgic code
- Explain SGI handling in comment
- Error patch and return value fixes and cleanups
- Use defines instead of all hardcoded IRQ numbers
- Refactor level interrupt handling
- Fix several maintenace-interrupt related bugs
- Add CPU hotplug notifier

Changes since v3:
- Change struct kvm_device_addr to use 64 bits for ID field

Changes since v2:
- Get rid of hardcoded guest cpu and distributor physical addresses and instead provide the address through the KVM_SET_DEVICE_ADDRESS ioctl.
- Fix level/edge bugs
- Fix reboot bug: retire queued, disabled interrupts

---

Christoffer Dall (3):
ARM: gic: add missing distributor defintions
KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl
ARM: KVM: VGIC accept vcpu and dist base addresses from user space

Marc Zyngier (10):
ARM: KVM: Keep track of currently running vcpus
ARM: gic: define GICH offsets for VGIC support
ARM: KVM: Initial VGIC infrastructure code
ARM: KVM: VGIC distributor handling
ARM: KVM: VGIC virtual CPU interface management
ARM: KVM: vgic: retire queued, disabled interrupts
ARM: KVM: VGIC interrupt injection
ARM: KVM: VGIC control interface world switch
ARM: KVM: VGIC initialisation code
ARM: KVM: Add VGIC configuration option

 Documentation/virtual/kvm/api.txt   |   38 +
 arch/arm/include/asm/hardware/gic.h |   29 +
 arch/arm/include/asm/kvm_host.h     |   18 
 arch/arm/include/asm/kvm_vgic.h     |  221 +++++
 arch/arm/include/uapi/asm/kvm.h     |   16 
 arch/arm/kernel/asm-offsets.c       |   12 
 arch/arm/kvm/Kconfig                |    8 
 arch/arm/kvm/Makefile               |    1 
 arch/arm/kvm/arm.c                  |  162 +++-
 arch/arm/kvm/interrupts.S           |    4 
 arch/arm/kvm/interrupts_head.S      |   74 ++
 arch/arm/kvm/mmio.c                 |    3 
 arch/arm/kvm/vgic.c                 | 1504 +++++++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h            |    8 
 14 files changed, 2085 insertions(+), 13 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h
 create mode 100644 arch/arm/kvm/vgic.c
#1 lore raw

PATCH v6 1/13 ARM: gic: add missing distributor defintions

Add missing register map offsets for the distributor and rename GIC_DIST_ACTIVE_BIT to GIC_DIST_ACTIVE_SET to be consistent.

---
 arch/arm/include/asm/hardware/gic.h |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm/include/asm/hardware/gic.h +3 -1
index 4b1ce6c..caf5df3 100644
@@ -22,11 +22,13 @@
 
 #define GIC_DIST_CTRL			0x000
 #define GIC_DIST_CTR			0x004
+#define GIC_DIST_IGROUP			0x080
 #define GIC_DIST_ENABLE_SET		0x100
 #define GIC_DIST_ENABLE_CLEAR		0x180
 #define GIC_DIST_PENDING_SET		0x200
 #define GIC_DIST_PENDING_CLEAR		0x280
-#define GIC_DIST_ACTIVE_BIT		0x300
+#define GIC_DIST_ACTIVE_SET		0x300
+#define GIC_DIST_ACTIVE_CLEAR		0x380
 #define GIC_DIST_PRI			0x400
 #define GIC_DIST_TARGET			0x800
 #define GIC_DIST_CONFIG			0xc00
#2 lore raw

PATCH v6 2/13 KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

In reply to Christoffer Dall

On ARM (and possibly other architectures) some bits are specific to the model being emulated for the guest and user space needs a way to tell the kernel about those bits. An example is mmio device base addresses, where KVM must know the base address for a given device to properly emulate mmio accesses within a certain address range or directly map a device with virtualiation extensions into the guest address space.

We try to make this API slightly more generic than for our specific use, but so far only the VGIC uses this feature.

---
 Documentation/virtual/kvm/api.txt |   37 +++++++++++++++++++++++++++++++++++++
 arch/arm/include/uapi/asm/kvm.h   |   13 +++++++++++++
 arch/arm/kvm/arm.c                |   23 ++++++++++++++++++++++-
 include/uapi/linux/kvm.h          |    8 ++++++++
 4 files changed, 80 insertions(+), 1 deletion(-)
Documentation/virtual/kvm/api.txt +37 -0
index c25439a..4505f86 100644
@@ -2210,6 +2210,43 @@ This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
 
+4.80 KVM_ARM_SET_DEVICE_ADDR
+
+Capability: KVM_CAP_ARM_SET_DEVICE_ADDR
+Architectures: arm
+Type: vm ioctl
+Parameters: struct kvm_arm_device_address (in)
+Returns: 0 on success, -1 on error
+Errors:
+  ENODEV: The device id is unknown
+  ENXIO:  Device not supported on current system
+  EEXIST: Address already set
+  E2BIG:  Address outside guest physical address space
+
+struct kvm_arm_device_addr {
+	__u64 id;
+	__u64 addr;
+};
+
+Specify a device address in the guest's physical address space where guests
+can access emulated or directly exposed devices, which the host kernel needs
+to know about. The id field is an architecture specific identifier for a
+specific device.
+
+ARM divides the id field into two parts, a device id and an address type id
+specific to the individual device.
+
+ ?bits:  | 63        ...       32 | 31    ...    16 | 15    ...    0 |
+  field: |        0x00000000      |     device id   |  addr type id  |
+
+ARM currently only require this when using the in-kernel GIC support for the
+hardware VGIC features, using KVM_ARM_DEVICE_VGIC_V2 as the device id.  When
+setting the base address for the guest's mapping of the VGIC virtual CPU
+and distributor interface, the ioctl must be called after calling
+KVM_CREATE_IRQCHIP, but before calling KVM_RUN on any of the VCPUs.  Calling
+this ioctl twice for any of the base addresses will return -EEXIST.
+
+
 5. The kvm_run structure
 ------------------------
 
arch/arm/include/uapi/asm/kvm.h +13 -0
index 3303ff5..d6c8b9c 100644
@@ -65,6 +65,19 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15	0
 #define KVM_ARM_NUM_TARGETS		1
 
+/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
+#define KVM_DEVICE_TYPE_SHIFT		0
+#define KVM_DEVICE_TYPE_MASK		(0xffff << KVM_DEVICE_TYPE_SHIFT)
+#define KVM_DEVICE_ID_SHIFT		16
+#define KVM_DEVICE_ID_MASK		(0xffff << KVM_DEVICE_ID_SHIFT)
+
+/* Supported device IDs */
+#define KVM_ARM_DEVICE_VGIC_V2		0
+
+/* Supported VGIC address types  */
+#define KVM_VGIC_V2_ADDR_TYPE_DIST	0
+#define KVM_VGIC_V2_ADDR_TYPE_CPU	1
+
 #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
 
 struct kvm_vcpu_init {
arch/arm/kvm/arm.c +22 -1
index 6ff5337..6ab0040 100644
@@ -167,6 +167,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_COALESCED_MMIO:
 		r = KVM_COALESCED_MMIO_PAGE_OFFSET;
 		break;
+	case KVM_CAP_ARM_SET_DEVICE_ADDR:
+		r = 1;
 	case KVM_CAP_NR_VCPUS:
 		r = num_online_cpus();
 		break;
@@ -822,10 +824,29 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 	return -EINVAL;
 }
 
+static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
+					struct kvm_arm_device_addr *dev_addr)
+{
+	return -ENODEV;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
 		       unsigned int ioctl, unsigned long arg)
 {
-	return -EINVAL;
+	struct kvm *kvm = filp->private_data;
+	void __user *argp = (void __user *)arg;
+
+	switch (ioctl) {
+	case KVM_ARM_SET_DEVICE_ADDR: {
+		struct kvm_arm_device_addr dev_addr;
+
+		if (copy_from_user(&dev_addr, argp, sizeof(dev_addr)))
+			return -EFAULT;
+		return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr);
+	}
+	default:
+		return -EINVAL;
+	}
 }
 
 static void cpu_init_hyp_mode(void *vector)
include/uapi/linux/kvm.h +8 -0
index 500acda..9ff7f70 100644
@@ -636,6 +636,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_IRQFD_RESAMPLE 82
 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83
 #define KVM_CAP_PPC_HTAB_FD 84
+#define KVM_CAP_ARM_SET_DEVICE_ADDR 85
 #define KVM_CAP_ARM_PSCI 86
 
 #ifdef KVM_CAP_IRQ_ROUTING
@@ -784,6 +785,11 @@ struct kvm_msi {
 	__u8  pad[16];
 };
 
+struct kvm_arm_device_addr {
+	__u64 id;
+	__u64 addr;
+};
+
 /*
  * ioctls for VM fds
  */
@@ -869,6 +875,8 @@ struct kvm_s390_ucas_mapping {
 #define KVM_ALLOCATE_RMA	  _IOR(KVMIO,  0xa9, struct kvm_allocate_rma)
 /* Available with KVM_CAP_PPC_HTAB_FD */
 #define KVM_PPC_GET_HTAB_FD	  _IOW(KVMIO,  0xaa, struct kvm_get_htab_fd)
+/* Available with KVM_CAP_SET_DEVICE_ADDR */
+#define KVM_ARM_SET_DEVICE_ADDR	  _IOW(KVMIO,  0xab, struct kvm_arm_device_addr)
 
 /*
  * ioctls for vcpu fds
#3 lore raw

Re: [kvmarm] [PATCH v6 02/13] KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Quoted 1 line · Christoffer Dall

KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Patch subject needs updating with new name of this ioctl (KVM_ARM_SET_DEVICE_ADDR)...

Quoted 6 lines

On ARM (and possibly other architectures) some bits are specific to the model being emulated for the guest and user space needs a way to tell the kernel about those bits. An example is mmio device base addresses, where KVM must know the base address for a given device to properly emulate mmio accesses within a certain address range or directly map a device with virtualiation extensions into the guest address space.

"virtualization", while I'm here.

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 72

Quoted 11 lines
arch/arm/include/uapi/asm/kvm.h +5 -0
@@ -65,6 +65,19 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15      0
 #define KVM_ARM_NUM_TARGETS            1
 
+/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
+#define KVM_DEVICE_TYPE_SHIFT          0
+#define KVM_DEVICE_TYPE_MASK           (0xffff << KVM_DEVICE_TYPE_SHIFT)
+#define KVM_DEVICE_ID_SHIFT            16
+#define KVM_DEVICE_ID_MASK             (0xffff << KVM_DEVICE_ID_SHIFT)

...and this comment and I guess these constant names presumably should have "ARM" in them?

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 73

Quoted 1 line
arch/arm/include/uapi/asm/kvm.h +1 -0
+/* Available with KVM_CAP_SET_DEVICE_ADDR */

KVM_CAP_ARM_SET_DEVICE_ADDR.

-- PMM

#4 lore raw

Re: [kvmarm] [PATCH v6 02/13] KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Quoted 31 lines · Peter Maydell
Quoted 1 line · Christoffer Dall

KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Patch subject needs updating with new name of this ioctl (KVM_ARM_SET_DEVICE_ADDR)...

Quoted 6 lines

On ARM (and possibly other architectures) some bits are specific to the model being emulated for the guest and user space needs a way to tell the kernel about those bits. An example is mmio device base addresses, where KVM must know the base address for a given device to properly emulate mmio accesses within a certain address range or directly map a device with virtualiation extensions into the guest address space.

"virtualization", while I'm here.

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 72

Quoted 11 lines
arch/arm/include/uapi/asm/kvm.h +5 -0
@@ -65,6 +65,19 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15      0
 #define KVM_ARM_NUM_TARGETS            1
 
+/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
+#define KVM_DEVICE_TYPE_SHIFT          0
+#define KVM_DEVICE_TYPE_MASK           (0xffff << KVM_DEVICE_TYPE_SHIFT)
+#define KVM_DEVICE_ID_SHIFT            16
+#define KVM_DEVICE_ID_MASK             (0xffff << KVM_DEVICE_ID_SHIFT)

...and this comment and I guess these constant names presumably should have "ARM" in them?

Quoted 4 lines

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 73

Quoted 1 line
arch/arm/include/uapi/asm/kvm.h +1 -0
+/* Available with KVM_CAP_SET_DEVICE_ADDR */

KVM_CAP_ARM_SET_DEVICE_ADDR.

right, thanks:

commit 92c7530ddee0d1e1a0b5c3fbd01aa05457812030

Author: Christoffer Dall <[email protected]>
Date:   Sun Jan 20 18:20:52 2013 -0500

KVM: ARM: Update comments and defines for KVM_ARM_SET_DEVICE_ADDR

Update comments and defines to reflect the name change of KVM_SET_DEVICE_ADDRESS to KVM_ARM_SET_DEVICE_ADDR.

arch/arm/include/uapi/asm/kvm.h +5 -5
index 236f528..023bfeb 100644
@@ -65,11 +65,11 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15	0
 #define KVM_ARM_NUM_TARGETS		1
 
-/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
-#define KVM_DEVICE_TYPE_SHIFT		0
-#define KVM_DEVICE_TYPE_MASK		(0xffff << KVM_DEVICE_TYPE_SHIFT)
-#define KVM_DEVICE_ID_SHIFT		16
-#define KVM_DEVICE_ID_MASK		(0xffff << KVM_DEVICE_ID_SHIFT)
+/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
+#define KVM_ARM_DEVICE_TYPE_SHIFT	0
+#define KVM_ARM_DEVICE_TYPE_MASK	(0xffff << KVM_ARM_DEVICE_TYPE_SHIFT)
+#define KVM_ARM_DEVICE_ID_SHIFT		16
+#define KVM_ARM_DEVICE_ID_MASK		(0xffff << KVM_ARM_DEVICE_ID_SHIFT)
 
 /* Supported device IDs */
 #define KVM_ARM_DEVICE_VGIC_V2		0
include/uapi/linux/kvm.h +1 -1
index 9ff7f70..33887e7 100644
@@ -875,7 +875,7 @@ struct kvm_s390_ucas_mapping {
 #define KVM_ALLOCATE_RMA	  _IOR(KVMIO,  0xa9, struct kvm_allocate_rma)
 /* Available with KVM_CAP_PPC_HTAB_FD */
 #define KVM_PPC_GET_HTAB_FD	  _IOW(KVMIO,  0xaa, struct kvm_get_htab_fd)
-/* Available with KVM_CAP_SET_DEVICE_ADDR */
+/* Available with KVM_CAP_ARM_SET_DEVICE_ADDR */
 #define KVM_ARM_SET_DEVICE_ADDR	  _IOW(KVMIO,  0xab, struct

kvm_arm_device_addr)

/*
--

-Christoffer

#5 lore raw

Re: [kvmarm] [PATCH v6 02/13] KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Quoted 84 lines · Christoffer Dall
Quoted 31 lines · Peter Maydell
Quoted 1 line · Christoffer Dall

KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

Patch subject needs updating with new name of this ioctl (KVM_ARM_SET_DEVICE_ADDR)...

Quoted 6 lines

On ARM (and possibly other architectures) some bits are specific to the model being emulated for the guest and user space needs a way to tell the kernel about those bits. An example is mmio device base addresses, where KVM must know the base address for a given device to properly emulate mmio accesses within a certain address range or directly map a device with virtualiation extensions into the guest address space.

"virtualization", while I'm here.

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 72

Quoted 11 lines
arch/arm/include/uapi/asm/kvm.h +5 -0
@@ -65,6 +65,19 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15      0
 #define KVM_ARM_NUM_TARGETS            1
 
+/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
+#define KVM_DEVICE_TYPE_SHIFT          0
+#define KVM_DEVICE_TYPE_MASK           (0xffff << KVM_DEVICE_TYPE_SHIFT)
+#define KVM_DEVICE_ID_SHIFT            16
+#define KVM_DEVICE_ID_MASK             (0xffff << KVM_DEVICE_ID_SHIFT)

...and this comment and I guess these constant names presumably should have "ARM" in them?

Quoted 4 lines

arch/arm/include/uapi/asm/kvm.h struct kvm_regs { · line 73

Quoted 1 line
arch/arm/include/uapi/asm/kvm.h +1 -0
+/* Available with KVM_CAP_SET_DEVICE_ADDR */

KVM_CAP_ARM_SET_DEVICE_ADDR.

right, thanks:

commit 92c7530ddee0d1e1a0b5c3fbd01aa05457812030

Author: Christoffer Dall <[email protected]>
Date:   Sun Jan 20 18:20:52 2013 -0500

KVM: ARM: Update comments and defines for KVM_ARM_SET_DEVICE_ADDR

Update comments and defines to reflect the name change of KVM_SET_DEVICE_ADDRESS to KVM_ARM_SET_DEVICE_ADDR.

arch/arm/include/uapi/asm/kvm.h +5 -5
index 236f528..023bfeb 100644
@@ -65,11 +65,11 @@ struct kvm_regs {
 #define KVM_ARM_TARGET_CORTEX_A15      0
 #define KVM_ARM_NUM_TARGETS            1
 
-/* KVM_SET_DEVICE_ADDRESS ioctl id encoding */
-#define KVM_DEVICE_TYPE_SHIFT          0
-#define KVM_DEVICE_TYPE_MASK           (0xffff << KVM_DEVICE_TYPE_SHIFT)
-#define KVM_DEVICE_ID_SHIFT            16
-#define KVM_DEVICE_ID_MASK             (0xffff << KVM_DEVICE_ID_SHIFT)
+/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
+#define KVM_ARM_DEVICE_TYPE_SHIFT      0
+#define KVM_ARM_DEVICE_TYPE_MASK       (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT)
+#define KVM_ARM_DEVICE_ID_SHIFT                16
+#define KVM_ARM_DEVICE_ID_MASK         (0xffff << KVM_ARM_DEVICE_ID_SHIFT)
 
 /* Supported device IDs */
 #define KVM_ARM_DEVICE_VGIC_V2         0
include/uapi/linux/kvm.h +1 -1
index 9ff7f70..33887e7 100644
@@ -875,7 +875,7 @@ struct kvm_s390_ucas_mapping {
 #define KVM_ALLOCATE_RMA         _IOR(KVMIO,  0xa9, struct kvm_allocate_rma)
 /* Available with KVM_CAP_PPC_HTAB_FD */
 #define KVM_PPC_GET_HTAB_FD      _IOW(KVMIO,  0xaa, struct kvm_get_htab_fd)
-/* Available with KVM_CAP_SET_DEVICE_ADDR */
+/* Available with KVM_CAP_ARM_SET_DEVICE_ADDR */
 #define KVM_ARM_SET_DEVICE_ADDR          _IOW(KVMIO,  0xab, struct
 kvm_arm_device_addr)
 
Quoted 2 lines

/*
--

and this:

arch/arm/kvm/arm.c +4 -2
index a67392a..134df21 100644
@@ -924,8 +924,10 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
 {
 	unsigned long dev_id, type;
 
-	dev_id = (dev_addr->id & KVM_DEVICE_ID_MASK) >> KVM_DEVICE_ID_SHIFT;
-	type = (dev_addr->id & KVM_DEVICE_TYPE_MASK) >> KVM_DEVICE_TYPE_SHIFT;
+	dev_id = (dev_addr->id & KVM_ARM_DEVICE_ID_MASK) >>
+		KVM_ARM_DEVICE_ID_SHIFT;
+	type = (dev_addr->id & KVM_ARM_DEVICE_TYPE_MASK) >>
+		KVM_ARM_DEVICE_TYPE_SHIFT;
 
 	switch (dev_id) {
 	case KVM_ARM_DEVICE_VGIC_V2:

--

-Christoffer

#6 lore raw

PATCH v6 3/13 ARM: KVM: Keep track of currently running vcpus

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

When an interrupt occurs for the guest, it is sometimes necessary to find out which vcpu was running at that point.

Keep track of which vcpu is being run in kvm_arch_vcpu_ioctl_run(), and allow the data to be retrieved using either:
- kvm_arm_get_running_vcpu(): returns the vcpu running at this point on the current CPU. Can only be used in a non-preemptible context.
- kvm_arm_get_running_vcpus(): returns the per-CPU variable holding the running vcpus, usable for per-CPU interrupts.

---
 arch/arm/include/asm/kvm_host.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   30 ++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
arch/arm/include/asm/kvm_host.h +10 -0
index c9ba918..869253c 100644
@@ -158,4 +158,14 @@ static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
 {
 	return 0;
 }
+
+struct kvm_vcpu *kvm_arm_get_running_vcpu(void);
+struct kvm_vcpu __percpu **kvm_get_running_vcpus(void);
+
+int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
+unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu);
+struct kvm_one_reg;
+int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
+int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *);
+
 #endif /* __ARM_KVM_HOST_H__ */
arch/arm/kvm/arm.c +30 -0
index 6ab0040..c715751 100644
@@ -54,11 +54,38 @@ static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 static struct vfp_hard_struct __percpu *kvm_host_vfp_state;
 static unsigned long hyp_default_vectors;
 
+/* Per-CPU variable containing the currently running vcpu. */
+static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
+
 /* The VMID used in the VTTBR */
 static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
 static u8 kvm_next_vmid;
 static DEFINE_SPINLOCK(kvm_vmid_lock);
 
+static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
+{
+	BUG_ON(preemptible());
+	__get_cpu_var(kvm_arm_running_vcpu) = vcpu;
+}
+
+/**
+ * kvm_arm_get_running_vcpu - get the vcpu running on the current CPU.
+ * Must be called from non-preemptible context
+ */
+struct kvm_vcpu *kvm_arm_get_running_vcpu(void)
+{
+	BUG_ON(preemptible());
+	return __get_cpu_var(kvm_arm_running_vcpu);
+}
+
+/**
+ * kvm_arm_get_running_vcpus - get the per-CPU array of currently running vcpus.
+ */
+struct kvm_vcpu __percpu **kvm_get_running_vcpus(void)
+{
+	return &kvm_arm_running_vcpu;
+}
+
 int kvm_arch_hardware_enable(void *garbage)
 {
 	return 0;
@@ -310,10 +337,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	 */
 	if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush))
 		flush_cache_all(); /* We'd really want v7_flush_dcache_all() */
+
+	kvm_arm_set_running_vcpu(vcpu);
 }
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
+	kvm_arm_set_running_vcpu(NULL);
 }
 
 int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
#7 lore raw

PATCH v6 4/13 ARM: gic: define GICH offsets for VGIC support

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

The GICH_* constants are defined by the GIC HW spec, and even though they only be used by KVM to begin with, define them generically in gic.h.

---
 arch/arm/include/asm/hardware/gic.h |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
arch/arm/include/asm/hardware/gic.h +25 -0
index caf5df3..6cad421 100644
@@ -34,6 +34,31 @@
 #define GIC_DIST_CONFIG			0xc00
 #define GIC_DIST_SOFTINT		0xf00
 
+#define GICH_HCR			0x0
+#define GICH_VTR			0x4
+#define GICH_VMCR			0x8
+#define GICH_MISR			0x10
+#define GICH_EISR0 			0x20
+#define GICH_EISR1 			0x24
+#define GICH_ELRSR0 			0x30
+#define GICH_ELRSR1 			0x34
+#define GICH_APR			0xf0
+#define GICH_LR0			0x100
+
+#define GICH_HCR_EN			(1 << 0)
+#define GICH_HCR_UIE			(1 << 1)
+
+#define GICH_LR_VIRTUALID		(0x3ff << 0)
+#define GICH_LR_PHYSID_CPUID_SHIFT	(10)
+#define GICH_LR_PHYSID_CPUID		(7 << GICH_LR_PHYSID_CPUID_SHIFT)
+#define GICH_LR_STATE			(3 << 28)
+#define GICH_LR_PENDING_BIT		(1 << 28)
+#define GICH_LR_ACTIVE_BIT		(1 << 29)
+#define GICH_LR_EOI			(1 << 19)
+
+#define GICH_MISR_EOI			(1 << 0)
+#define GICH_MISR_U			(1 << 1)
+
 #ifndef __ASSEMBLY__
 #include <linux/irqdomain.h>
 struct device_node;
#8 lore raw

PATCH v6 5/13 ARM: KVM: Initial VGIC infrastructure code

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Wire the basic framework code for VGIC support and the initial in-kernel MMIO support code for the VGIC, used for the distributor emulation.

---
 arch/arm/include/asm/kvm_host.h |    8 ++
 arch/arm/include/asm/kvm_vgic.h |   80 ++++++++++++++++++++
 arch/arm/kvm/Makefile           |    1 
 arch/arm/kvm/arm.c              |   27 +++++++
 arch/arm/kvm/interrupts.S       |    4 +
 arch/arm/kvm/mmio.c             |    3 +
 arch/arm/kvm/vgic.c             |  153 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 275 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h
 create mode 100644 arch/arm/kvm/vgic.c
arch/arm/include/asm/kvm_host.h +8 -0
index 869253c..feac7f4 100644
@@ -37,6 +37,8 @@
 #define KVM_NR_PAGE_SIZES	1
 #define KVM_PAGES_PER_HPAGE(x)	(1UL<<31)
 
+#include <asm/kvm_vgic.h>
+
 struct kvm_vcpu;
 u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode);
 int kvm_target_cpu(void);
@@ -58,6 +60,9 @@ struct kvm_arch {
 
 	/* Stage-2 page table */
 	pgd_t *pgd;
+
+	/* Interrupt controller */
+	struct vgic_dist	vgic;
 };
 
 #define KVM_NR_MEM_OBJS     40
@@ -92,6 +97,9 @@ struct kvm_vcpu_arch {
 	struct vfp_hard_struct vfp_guest;
 	struct vfp_hard_struct *vfp_host;
 
+	/* VGIC state */
+	struct vgic_cpu vgic_cpu;
+
 	/*
 	 * Anything that is not used directly from assembly code goes
 	 * here.
arch/arm/include/asm/kvm_vgic.h new file +80 -0

80 lines not on this page · raw message

arch/arm/kvm/Makefile +1 -0
index ea27987..dece8ed 100644
@@ -19,3 +19,4 @@ kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 obj-y += kvm-arm.o init.o interrupts.o
 obj-y += arm.o guest.o mmu.o emulate.o reset.o
 obj-y += coproc.o coproc_a15.o mmio.o psci.o
+obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
arch/arm/kvm/arm.c +26 -1
index c715751..d30a48c 100644
@@ -62,6 +62,8 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
 static u8 kvm_next_vmid;
 static DEFINE_SPINLOCK(kvm_vmid_lock);
 
+static bool vgic_present;
+
 static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
 {
 	BUG_ON(preemptible());
@@ -184,6 +186,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 {
 	int r;
 	switch (ext) {
+	case KVM_CAP_IRQCHIP:
+		r = vgic_present;
+		break;
 	case KVM_CAP_USER_MEMORY:
 	case KVM_CAP_SYNC_MMU:
 	case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
@@ -315,8 +320,16 @@ int __attribute_const__ kvm_target_cpu(void)
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
+	int ret;
+
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu->arch.target = -1;
+
+	/* Set up VGIC */
+	ret = kvm_vgic_vcpu_init(vcpu);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -374,7 +387,7 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
  */
 int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 {
-	return !!v->arch.irq_lines;
+	return !!v->arch.irq_lines || kvm_vgic_vcpu_pending_irq(v);
 }
 
 /* Just ensure a guest exit from a particular CPU */
@@ -682,6 +695,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		update_vttbr(vcpu->kvm);
 
+		kvm_vgic_flush_hwstate(vcpu);
+
 		local_irq_disable();
 
 		/*
@@ -694,6 +709,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
 			local_irq_enable();
+			kvm_vgic_sync_hwstate(vcpu);
 			continue;
 		}
 
@@ -732,6 +748,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * Back from guest
 		 *************************************************************/
 
+		kvm_vgic_sync_hwstate(vcpu);
+
 		ret = handle_exit(vcpu, run, ret);
 	}
 
@@ -1006,6 +1024,13 @@ static int init_hyp_mode(void)
 		}
 	}
 
+	/*
+	 * Init HYP view of VGIC
+	 */
+	err = kvm_vgic_hyp_init();
+	if (err)
+		goto out_free_vfp;
+
 	kvm_info("Hyp mode initialized successfully\n");
 	return 0;
 out_free_vfp:
arch/arm/kvm/interrupts.S +4 -0
index 702743e..b19ee80 100644
@@ -94,6 +94,8 @@ ENTRY(__kvm_vcpu_run)
 
 	save_host_regs
 
+	restore_vgic_state
+
 	@ Store hardware CP15 state and load guest state
 	read_cp15_state store_to_vcpu = 0
 	write_cp15_state read_from_vcpu = 1
@@ -187,6 +189,8 @@ after_vfp_restore:
 	read_cp15_state store_to_vcpu = 1
 	write_cp15_state read_from_vcpu = 0
 
+	save_vgic_state
+
 	restore_host_regs
 	clrex				@ Clear exclusive monitor
 	mov	r0, r1			@ Return the return code
arch/arm/kvm/mmio.c +3 -0
index 0144baf..98a870f 100644
@@ -148,6 +148,9 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
 	if (mmio.is_write)
 		memcpy(mmio.data, vcpu_reg(vcpu, rt), mmio.len);
 
+	if (vgic_handle_mmio(vcpu, run, &mmio))
+		return 1;
+
 	kvm_prepare_mmio(run, &mmio);
 	return 0;
 }
arch/arm/kvm/vgic.c new file +153 -0

153 lines not on this page · raw message

#9 lore raw

PATCH v6 6/13 ARM: KVM: VGIC accept vcpu and dist base addresses from user space

In reply to Christoffer Dall

User space defines the model to emulate to a guest and should therefore decide which addresses are used for both the virtual CPU interface directly mapped in the guest physical address space and for the emulated distributor interface, which is mapped in software by the in-kernel VGIC support.

---
 Documentation/virtual/kvm/api.txt |    1 +
 arch/arm/include/asm/kvm_vgic.h   |    9 +++++
 arch/arm/include/uapi/asm/kvm.h   |    3 ++
 arch/arm/kvm/arm.c                |   14 ++++++++
 arch/arm/kvm/vgic.c               |   62 +++++++++++++++++++++++++++++++++++++
 5 files changed, 88 insertions(+), 1 deletion(-)
Documentation/virtual/kvm/api.txt +1 -0
index 4505f86..e0fa0ea 100644
@@ -2222,6 +2222,7 @@ Errors:
   ENXIO:  Device not supported on current system
   EEXIST: Address already set
   E2BIG:  Address outside guest physical address space
+  EBUSY:  Address overlaps with other device range
 
 struct kvm_arm_device_addr {
 	__u64 id;
arch/arm/include/asm/kvm_vgic.h +9 -0
index 8a1aa9f..6b00735 100644
@@ -22,6 +22,9 @@
 #include <asm/hardware/gic.h>
 
 struct vgic_dist {
+	/* Distributor and vcpu interface mapping in the guest */
+	phys_addr_t		vgic_dist_base;
+	phys_addr_t		vgic_cpu_base;
 };
 
 struct vgic_cpu {
@@ -33,6 +36,7 @@ struct kvm_run;
 struct kvm_exit_mmio;
 
 #ifdef CONFIG_KVM_ARM_VGIC
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		      struct kvm_exit_mmio *mmio);
 
@@ -42,6 +46,11 @@ static inline int kvm_vgic_hyp_init(void)
 	return 0;
 }
 
+static inline int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+	return 0;
+}
+
 static inline int kvm_vgic_init(struct kvm *kvm)
 {
 	return 0;
arch/arm/include/uapi/asm/kvm.h +3 -0
index d6c8b9c..236f528 100644
@@ -78,6 +78,9 @@ struct kvm_regs {
 #define KVM_VGIC_V2_ADDR_TYPE_DIST	0
 #define KVM_VGIC_V2_ADDR_TYPE_CPU	1
 
+#define KVM_VGIC_V2_DIST_SIZE		0x1000
+#define KVM_VGIC_V2_CPU_SIZE		0x2000
+
 #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
 
 struct kvm_vcpu_init {
arch/arm/kvm/arm.c +13 -1
index d30a48c..6e60fd8 100644
@@ -875,7 +875,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
 					struct kvm_arm_device_addr *dev_addr)
 {
-	return -ENODEV;
+	unsigned long dev_id, type;
+
+	dev_id = (dev_addr->id & KVM_DEVICE_ID_MASK) >> KVM_DEVICE_ID_SHIFT;
+	type = (dev_addr->id & KVM_DEVICE_TYPE_MASK) >> KVM_DEVICE_TYPE_SHIFT;
+
+	switch (dev_id) {
+	case KVM_ARM_DEVICE_VGIC_V2:
+		if (!vgic_present)
+			return -ENXIO;
+		return kvm_vgic_set_addr(kvm, type, dev_addr->addr);
+	default:
+		return -ENODEV;
+	}
 }
 
 long kvm_arch_vm_ioctl(struct file *filp,
arch/arm/kvm/vgic.c +62 -0

71 lines not on this page · raw message

#10 lore raw

PATCH v6 7/13 ARM: KVM: VGIC distributor handling

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Add the GIC distributor emulation code. A number of the GIC features are simply ignored as they are not required to boot a Linux guest.

---
 arch/arm/include/asm/kvm_vgic.h |   82 +++++
 arch/arm/kvm/vgic.c             |  596 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 677 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +82 -0
index 6b00735..b1ea1b6 100644
@@ -19,12 +19,94 @@
 #ifndef __ASM_ARM_KVM_VGIC_H
 #define __ASM_ARM_KVM_VGIC_H
 
+#include <linux/kernel.h>
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/irqreturn.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
 #include <asm/hardware/gic.h>
 
+#define VGIC_NR_IRQS		128
+#define VGIC_NR_SGIS		16
+#define VGIC_NR_PPIS		16
+#define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
+#define VGIC_NR_SHARED_IRQS	(VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
+#define VGIC_MAX_CPUS		KVM_MAX_VCPUS
+
+/* Sanity checks... */
+#if (VGIC_MAX_CPUS > 8)
+#error	Invalid number of CPU interfaces
+#endif
+
+#if (VGIC_NR_IRQS & 31)
+#error "VGIC_NR_IRQS must be a multiple of 32"
+#endif
+
+#if (VGIC_NR_IRQS > 1024)
+#error "VGIC_NR_IRQS must be <= 1024"
+#endif
+
+/*
+ * The GIC distributor registers describing interrupts have two parts:
+ * - 32 per-CPU interrupts (SGI + PPI)
+ * - a bunch of shared interrupts (SPI)
+ */
+struct vgic_bitmap {
+	union {
+		u32 reg[VGIC_NR_PRIVATE_IRQS / 32];
+		DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS);
+	} percpu[VGIC_MAX_CPUS];
+	union {
+		u32 reg[VGIC_NR_SHARED_IRQS / 32];
+		DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
+	} shared;
+};
+
+struct vgic_bytemap {
+	u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4];
+	u32 shared[VGIC_NR_SHARED_IRQS  / 4];
+};
+
 struct vgic_dist {
+#ifdef CONFIG_KVM_ARM_VGIC
+	spinlock_t		lock;
+
+	/* Virtual control interface mapping */
+	void __iomem		*vctrl_base;
+
 	/* Distributor and vcpu interface mapping in the guest */
 	phys_addr_t		vgic_dist_base;
 	phys_addr_t		vgic_cpu_base;
+
+	/* Distributor enabled */
+	u32			enabled;
+
+	/* Interrupt enabled (one bit per IRQ) */
+	struct vgic_bitmap	irq_enabled;
+
+	/* Interrupt 'pin' level */
+	struct vgic_bitmap	irq_state;
+
+	/* Level-triggered interrupt in progress */
+	struct vgic_bitmap	irq_active;
+
+	/* Interrupt priority. Not used yet. */
+	struct vgic_bytemap	irq_priority;
+
+	/* Level/edge triggered */
+	struct vgic_bitmap	irq_cfg;
+
+	/* Source CPU per SGI and target CPU */
+	u8			irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS];
+
+	/* Target CPU for each IRQ */
+	u8			irq_spi_cpu[VGIC_NR_SHARED_IRQS];
+	struct vgic_bitmap	irq_spi_target[VGIC_MAX_CPUS];
+
+	/* Bitmap indicating which CPU has something pending */
+	unsigned long		irq_pending_on_cpu;
+#endif
 };
 
 struct vgic_cpu {
arch/arm/kvm/vgic.c +595 -1

626 lines not on this page · raw message

#11 lore raw

PATCH v6 8/13 ARM: KVM: VGIC virtual CPU interface management

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Add VGIC virtual CPU interface code, picking pending interrupts from the distributor and stashing them in the VGIC control interface list registers.

---
 arch/arm/include/asm/kvm_vgic.h |   31 ++++
 arch/arm/kvm/vgic.c             |  327 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 357 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +31 -0
index b1ea1b6..1081649 100644
@@ -33,6 +33,7 @@
 #define VGIC_NR_PRIVATE_IRQS	(VGIC_NR_SGIS + VGIC_NR_PPIS)
 #define VGIC_NR_SHARED_IRQS	(VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS)
 #define VGIC_MAX_CPUS		KVM_MAX_VCPUS
+#define VGIC_MAX_LRS		(1 << 6)
 
 /* Sanity checks... */
 #if (VGIC_MAX_CPUS > 8)
@@ -110,8 +111,33 @@ struct vgic_dist {
 };
 
 struct vgic_cpu {
+#ifdef CONFIG_KVM_ARM_VGIC
+	/* per IRQ to LR mapping */
+	u8		vgic_irq_lr_map[VGIC_NR_IRQS];
+
+	/* Pending interrupts on this VCPU */
+	DECLARE_BITMAP(	pending_percpu, VGIC_NR_PRIVATE_IRQS);
+	DECLARE_BITMAP(	pending_shared, VGIC_NR_SHARED_IRQS);
+
+	/* Bitmap of used/free list registers */
+	DECLARE_BITMAP(	lr_used, VGIC_MAX_LRS);
+
+	/* Number of list registers on this CPU */
+	int		nr_lr;
+
+	/* CPU vif control registers for world switch */
+	u32		vgic_hcr;
+	u32		vgic_vmcr;
+	u32		vgic_misr;	/* Saved only */
+	u32		vgic_eisr[2];	/* Saved only */
+	u32		vgic_elrsr[2];	/* Saved only */
+	u32		vgic_apr;
+	u32		vgic_lr[VGIC_MAX_LRS];
+#endif
 };
 
+#define LR_EMPTY	0xff
+
 struct kvm;
 struct kvm_vcpu;
 struct kvm_run;
@@ -119,9 +145,14 @@ struct kvm_exit_mmio;
 
 #ifdef CONFIG_KVM_ARM_VGIC
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
+void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
+void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
+int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		      struct kvm_exit_mmio *mmio);
 
+#define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
+
 #else
 static inline int kvm_vgic_hyp_init(void)
 {
arch/arm/kvm/vgic.c +326 -1

345 lines not on this page · raw message

#12 lore raw

PATCH v6 9/13 ARM: KVM: vgic: retire queued, disabled interrupts

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

An interrupt may have been disabled after being made pending on the CPU interface (the classic case is a timer running while we're rebooting the guest - the interrupt would kick as soon as the CPU interface gets enabled, with deadly consequences).

The solution is to examine already active LRs, and check the interrupt is still enabled. If not, just retire it.

---
 arch/arm/kvm/vgic.c |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/kvm/vgic.c +30 -0
index 8f32702..2d5e29f 100644
@@ -71,6 +71,7 @@
 #define ACCESS_WRITE_VALUE	(3 << 1)
 #define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
 
+static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
 static void vgic_update_state(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 
@@ -353,6 +354,7 @@ static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
 	if (mmio->is_write) {
 		if (offset < 4) /* Force SGI enabled */
 			*reg |= 0xffff;
+		vgic_retire_disabled_irqs(vcpu);
 		vgic_update_state(vcpu->kvm);
 		return true;
 	}
@@ -804,6 +806,34 @@ static void vgic_update_state(struct kvm *kvm)
 	(((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
 #define MK_LR_PEND(src, irq)	\
 	(GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
+
+/*
+ * An interrupt may have been disabled after being made pending on the
+ * CPU interface (the classic case is a timer running while we're
+ * rebooting the guest - the interrupt would kick as soon as the CPU
+ * interface gets enabled, with deadly consequences).
+ *
+ * The solution is to examine already active LRs, and check the
+ * interrupt is still enabled. If not, just retire it.
+ */
+static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
+{
+	struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+	int lr;
+
+	for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
+		int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
+
+		if (!vgic_irq_is_enabled(vcpu, irq)) {
+			vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+			clear_bit(lr, vgic_cpu->lr_used);
+			vgic_cpu->vgic_lr[lr] &= ~GICH_LR_STATE;
+			if (vgic_irq_is_active(vcpu, irq))
+				vgic_irq_clear_active(vcpu, irq);
+		}
+	}
+}
+
 /*
  * Queue an interrupt to a CPU virtual interface. Return true on success,
  * or false if it wasn't possible to queue it.
#13 lore raw

PATCH v6 10/13 ARM: KVM: VGIC interrupt injection

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Plug the interrupt injection code. Interrupts can now be generated from user space.

---
 arch/arm/include/asm/kvm_vgic.h |    8 +++
 arch/arm/kvm/arm.c              |   55 +++++++++++++++---
 arch/arm/kvm/vgic.c             |  117 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+), 10 deletions(-)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 1081649..8aea9df 100644
@@ -147,6 +147,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
+int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
+			bool level);
 int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		      struct kvm_exit_mmio *mmio);
@@ -182,6 +184,12 @@ static inline int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
 static inline void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+				      unsigned int irq_num, bool level)
+{
+	return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
 	return 0;
arch/arm/kvm/arm.c +45 -10

71 lines not on this page · raw message

arch/arm/kvm/vgic.c +117 -0

135 lines not on this page · raw message

#14 lore raw

PATCH v6 11/13 ARM: KVM: VGIC control interface world switch

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Enable the VGIC control interface to be save-restored on world switch.

---
 arch/arm/kernel/asm-offsets.c  |   12 ++++++
 arch/arm/kvm/interrupts_head.S |   74 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)
arch/arm/kernel/asm-offsets.c +12 -0
index c8b3272..17cea2e 100644
@@ -169,6 +169,18 @@ int main(void)
   DEFINE(VCPU_HxFAR,		offsetof(struct kvm_vcpu, arch.hxfar));
   DEFINE(VCPU_HPFAR,		offsetof(struct kvm_vcpu, arch.hpfar));
   DEFINE(VCPU_HYP_PC,		offsetof(struct kvm_vcpu, arch.hyp_pc));
+#ifdef CONFIG_KVM_ARM_VGIC
+  DEFINE(VCPU_VGIC_CPU,		offsetof(struct kvm_vcpu, arch.vgic_cpu));
+  DEFINE(VGIC_CPU_HCR,		offsetof(struct vgic_cpu, vgic_hcr));
+  DEFINE(VGIC_CPU_VMCR,		offsetof(struct vgic_cpu, vgic_vmcr));
+  DEFINE(VGIC_CPU_MISR,		offsetof(struct vgic_cpu, vgic_misr));
+  DEFINE(VGIC_CPU_EISR,		offsetof(struct vgic_cpu, vgic_eisr));
+  DEFINE(VGIC_CPU_ELRSR,	offsetof(struct vgic_cpu, vgic_elrsr));
+  DEFINE(VGIC_CPU_APR,		offsetof(struct vgic_cpu, vgic_apr));
+  DEFINE(VGIC_CPU_LR,		offsetof(struct vgic_cpu, vgic_lr));
+  DEFINE(VGIC_CPU_NR_LR,	offsetof(struct vgic_cpu, nr_lr));
+  DEFINE(KVM_VGIC_VCTRL,	offsetof(struct kvm, arch.vgic.vctrl_base));
+#endif
   DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));
 #endif
   return 0; 
arch/arm/kvm/interrupts_head.S +74 -0

89 lines not on this page · raw message

#15 lore raw

PATCH v6 12/13 ARM: KVM: VGIC initialisation code

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Add the init code for the hypervisor, the virtual machine, and the virtual CPUs.

An interrupt handler is also wired to allow the VGIC maintenance interrupts, used to deal with level triggered interrupts and LR underflows.

A CPU hotplug notifier is registered to disable/enable the interrupt as requested.

---
 arch/arm/include/asm/kvm_vgic.h |   11 ++
 arch/arm/kvm/arm.c              |   15 +++
 arch/arm/kvm/vgic.c             |  223 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 249 insertions(+)
arch/arm/include/asm/kvm_vgic.h +11 -0
index 8aea9df..3103ed3 100644
@@ -72,6 +72,7 @@ struct vgic_bytemap {
 struct vgic_dist {
 #ifdef CONFIG_KVM_ARM_VGIC
 	spinlock_t		lock;
+	bool			ready;
 
 	/* Virtual control interface mapping */
 	void __iomem		*vctrl_base;
@@ -145,6 +146,10 @@ struct kvm_exit_mmio;
 
 #ifdef CONFIG_KVM_ARM_VGIC
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
+int kvm_vgic_hyp_init(void);
+int kvm_vgic_init(struct kvm *kvm);
+int kvm_vgic_create(struct kvm *kvm);
+int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
@@ -154,6 +159,7 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 		      struct kvm_exit_mmio *mmio);
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
+#define vgic_initialized(k)	((k)->arch.vgic.ready)
 
 #else
 static inline int kvm_vgic_hyp_init(void)
@@ -205,6 +211,11 @@ static inline int irqchip_in_kernel(struct kvm *kvm)
 {
 	return 0;
 }
+
+static inline bool vgic_initialized(struct kvm *kvm)
+{
+	return true;
+}
 #endif
 
 #endif
arch/arm/kvm/arm.c +15 -0
index a0e93d3..68cb2b550 100644
@@ -641,6 +641,17 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 	vcpu->arch.has_run_once = true;
 
 	/*
+	 * Initialize the VGIC before running a vcpu the first time on
+	 * this VM.
+	 */
+	if (irqchip_in_kernel(vcpu->kvm) &&
+	    unlikely(!vgic_initialized(vcpu->kvm))) {
+		int ret = kvm_vgic_init(vcpu->kvm);
+		if (ret)
+			return ret;
+	}
+
+	/*
 	 * Handle the "start in power-off" case by calling into the
 	 * PSCI code.
 	 */
@@ -1078,6 +1089,10 @@ static int init_hyp_mode(void)
 	if (err)
 		goto out_free_vfp;
 
+#ifdef CONFIG_KVM_ARM_VGIC
+		vgic_present = true;
+#endif
+
 	kvm_info("Hyp mode initialized successfully\n");
 	return 0;
 out_free_vfp:
arch/arm/kvm/vgic.c +223 -0

252 lines not on this page · raw message

#16 lore raw

PATCH v6 13/13 ARM: KVM: Add VGIC configuration option

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

It is now possible to select the VGIC configuration option.

---
 arch/arm/kvm/Kconfig |    8 ++++++++
 1 file changed, 8 insertions(+)
arch/arm/kvm/Kconfig +8 -0
index 05227cb..d8126f2 100644
@@ -51,6 +51,14 @@ config KVM_ARM_MAX_VCPUS
 	  large, so only choose a reasonable number that you expect to
 	  actually use.
 
+config KVM_ARM_VGIC
+	bool "KVM support for Virtual GIC"
+	depends on KVM_ARM_HOST && OF
+	select HAVE_KVM_IRQCHIP
+	default y
+	---help---
+	  Adds support for a hardware assisted, in-kernel GIC emulation.
+
 source drivers/virtio/Kconfig
 
 endif # VIRTUALIZATION