Skip to contentSkip to conversation

KVM/ARM vGIC support

PATCH Superseded Christoffer Dall 6 participants 58 messages Nov 10, 2012 – Dec 5, 2012

Subsystems (3)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover KVM/ARM vGIC support

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

Changes since v3:
- Change KVM_SET_DEVICE_ADDRESS to 64-bit-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

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

---

Christoffer Dall (2):
KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl
ARM: KVM: VGIC accept vcpu and dist base addresses from user space

Marc Zyngier (11):
ARM: KVM: Keep track of currently running vcpus
ARM: KVM: Initial VGIC infrastructure support
ARM: KVM: Initial VGIC MMIO support 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: vgic: reduce the number of vcpu kick
ARM: KVM: Add VGIC configuration option

 Documentation/virtual/kvm/api.txt |   37 +
 arch/arm/include/asm/kvm_arm.h    |   12 
 arch/arm/include/asm/kvm_host.h   |   17 +
 arch/arm/include/asm/kvm_mmu.h    |    2 
 arch/arm/include/asm/kvm_vgic.h   |  320 +++++++++
 arch/arm/include/uapi/asm/kvm.h   |   13 
 arch/arm/kernel/asm-offsets.c     |   12 
 arch/arm/kvm/Kconfig              |    7 
 arch/arm/kvm/Makefile             |    1 
 arch/arm/kvm/arm.c                |  138 ++++
 arch/arm/kvm/interrupts.S         |    4 
 arch/arm/kvm/interrupts_head.S    |   68 ++
 arch/arm/kvm/mmio.c               |    3 
 arch/arm/kvm/vgic.c               | 1251 +++++++++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h          |    8 
 virt/kvm/kvm_main.c               |    5 
 16 files changed, 1893 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h
 create mode 100644 arch/arm/kvm/vgic.c
#1 lore raw

PATCH v4 1/13 KVM: ARM: Introduce KVM_SET_DEVICE_ADDRESS ioctl

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                |   24 +++++++++++++++++++++++-
 include/uapi/linux/kvm.h          |    8 ++++++++
 4 files changed, 81 insertions(+), 1 deletion(-)
Documentation/virtual/kvm/api.txt +37 -0
index 9ffd702..35f6275 100644
@@ -2191,6 +2191,43 @@ This ioctl returns the guest registers that are supported for the
 KVM_GET_ONE_REG/KVM_SET_ONE_REG calls.
 
 
+4.80 KVM_SET_DEVICE_ADDRESS
+
+Capability: KVM_CAP_SET_DEVICE_ADDRESS
+Architectures: arm
+Type: vm ioctl
+Parameters: struct kvm_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_device_address {
+	__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:  | 31    ...    16 | 15    ...    0 |
+  field: |     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 5142cab..b1c7871 100644
@@ -41,6 +41,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
+
 struct kvm_vcpu_init {
 	__u32 target;
 	__u32 features[7];
arch/arm/kvm/arm.c +23 -1
index 435c5cc..2cdc07b 100644
@@ -164,6 +164,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 	case KVM_CAP_COALESCED_MMIO:
 		r = KVM_COALESCED_MMIO_PAGE_OFFSET;
 		break;
+	case KVM_CAP_SET_DEVICE_ADDR:
+		r = 1;
+		break;
 	default:
 		r = 0;
 		break;
@@ -777,10 +780,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_address(struct kvm *kvm,
+					   struct kvm_device_address *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_SET_DEVICE_ADDRESS: {
+		struct kvm_device_address dev_addr;
+
+		if (copy_from_user(&dev_addr, argp, sizeof(dev_addr)))
+			return -EFAULT;
+		return kvm_vm_ioctl_set_device_address(kvm, &dev_addr);
+	}
+	default:
+		return -EINVAL;
+	}
 }
 
 static void cpu_init_hyp_mode(void *vector)
include/uapi/linux/kvm.h +8 -0
index 1db0460..e936f8f 100644
@@ -635,6 +635,7 @@ struct kvm_ppc_smmu_info {
 #endif
 #define KVM_CAP_IRQFD_RESAMPLE 82
 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83
+#define KVM_CAP_SET_DEVICE_ADDR 84
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -782,6 +783,11 @@ struct kvm_msi {
 	__u8  pad[16];
 };
 
+struct kvm_device_address {
+	__u64 id;
+	__u64 addr;
+};
+
 /*
  * ioctls for VM fds
  */
@@ -865,6 +871,8 @@ struct kvm_s390_ucas_mapping {
 #define KVM_CREATE_SPAPR_TCE	  _IOW(KVMIO,  0xa8, struct kvm_create_spapr_tce)
 /* Available with KVM_CAP_RMA */
 #define KVM_ALLOCATE_RMA	  _IOR(KVMIO,  0xa9, struct kvm_allocate_rma)
+/* Available with KVM_CAP_SET_DEVICE_ADDR */
+#define KVM_SET_DEVICE_ADDRESS	  _IOW(KVMIO,  0xaa, struct kvm_device_address)
 
 /*
  * ioctls for vcpu fds
#2 lore raw

PATCH v4 2/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 tun in kvm_arch_vcpu_ioctl_run(), and allow the data to be retrived 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-preemptable context.
- kvm_arm_get_running_vcpus(): returns the per-CPU variable holding the the running vcpus, useable 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 e7fc249..e66cd56 100644
@@ -154,4 +154,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 2cdc07b..60b119a 100644
@@ -53,11 +53,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 on 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;
@@ -299,10 +326,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 		cpumask_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,
#3 lore raw

Re: [PATCH v4 02/13] ARM: KVM: Keep track of currently running vcpus

Just a bunch of typos in this one :)

Quoted 6 lines · 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 tun in kvm_arch_vcpu_ioctl_run(),

run

Quoted 1 line

and allow the data to be retrived using either:

retrieved

Quoted 2 lines

- kvm_arm_get_running_vcpu(): returns the vcpu running at this point on the current CPU. Can only be used in a non-preemptable context.

preemptible

Quoted 2 lines

- kvm_arm_get_running_vcpus(): returns the per-CPU variable holding the the running vcpus, useable for per-CPU interrupts.

-the
usable

Quoted 12 lines
---
 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 +0 -0
index e7fc249..e66cd56 100644

arch/arm/include/asm/kvm_host.h static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) · line 159

Quoted 6 lines
arch/arm/include/asm/kvm_host.h +3 -0
@@ -154,4 +154,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);

DECLARE_PER_CPU?

Quoted 32 lines
arch/arm/include/asm/kvm_host.h +6 -0
+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 +12 -0
index 2cdc07b..60b119a 100644
@@ -53,11 +53,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

arch/arm/kvm/arm.c static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); · line 82

Quoted 10 lines
arch/arm/kvm/arm.c +10 -0
+ */
+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 on currently running vcpus.
+ */

s/on/of/ ?

Will

#4 lore raw

Re: [PATCH v4 02/13] ARM: KVM: Keep track of currently running vcpus

Quoted 1 line · Will Deacon

Just a bunch of typos in this one :)

Typos? me? ;-)

Quoted 33 lines · Christoffer Dall
Quoted 6 lines

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 tun in kvm_arch_vcpu_ioctl_run(),

run

Quoted 1 line

and allow the data to be retrived using either:

retrieved

Quoted 2 lines

- kvm_arm_get_running_vcpu(): returns the vcpu running at this point on the current CPU. Can only be used in a non-preemptable context.

preemptible

Quoted 2 lines

- kvm_arm_get_running_vcpus(): returns the per-CPU variable holding the the running vcpus, useable for per-CPU interrupts.

-the
usable

Quoted 7 lines
---
 arch/arm/include/asm/kvm_host.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   30 ++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
Quoted 13 lines

arch/arm/include/asm/kvm_host.h static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) · line 159

Quoted 11 lines
arch/arm/include/asm/kvm_host.h +3 -0
index e7fc249..e66cd56 100644
@@ -154,4 +154,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);

DECLARE_PER_CPU?

Ah, nice one. I didn't even now it existed!

Quoted 36 lines
Quoted 36 lines
arch/arm/include/asm/kvm_host.h +6 -0
+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 +16 -0
index 2cdc07b..60b119a 100644
@@ -53,11 +53,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());
Quoted 8 lines

arch/arm/kvm/arm.c static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); · line 82

Quoted 6 lines
arch/arm/kvm/arm.c +6 -0
+	return __get_cpu_var(kvm_arm_running_vcpu);
+}
+
+/**
+ * kvm_arm_get_running_vcpus - get the per-CPU array on currently running vcpus.
+ */

s/on/of/ ?

Indeed.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#5 lore raw

Re: [PATCH v4 02/13] ARM: KVM: Keep track of currently running vcpus

In reply to Will Deacon
Quoted 34 lines · Will Deacon

Just a bunch of typos in this one :)

Quoted 6 lines · 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 tun in kvm_arch_vcpu_ioctl_run(),

run

Quoted 1 line

and allow the data to be retrived using either:

retrieved

Quoted 2 lines

- kvm_arm_get_running_vcpu(): returns the vcpu running at this point on the current CPU. Can only be used in a non-preemptable context.

preemptible

Quoted 2 lines

- kvm_arm_get_running_vcpus(): returns the per-CPU variable holding the the running vcpus, useable for per-CPU interrupts.

-the
usable

Quoted 7 lines
---
 arch/arm/include/asm/kvm_host.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   30 ++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
Quoted 14 lines

arch/arm/include/asm/kvm_host.h static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) · line 159

Quoted 11 lines
arch/arm/include/asm/kvm_host.h +3 -0
index e7fc249..e66cd56 100644
@@ -154,4 +154,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);

DECLARE_PER_CPU?

for a function prototype, how does that work?

Quoted 36 lines
Quoted 36 lines
arch/arm/include/asm/kvm_host.h +6 -0
+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 +16 -0
index 2cdc07b..60b119a 100644
@@ -53,11 +53,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());
Quoted 9 lines

arch/arm/kvm/arm.c static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); · line 82

Quoted 6 lines
arch/arm/kvm/arm.c +6 -0
+     return __get_cpu_var(kvm_arm_running_vcpu);
+}
+
+/**
+ * kvm_arm_get_running_vcpus - get the per-CPU array on currently running vcpus.
+ */

s/on/of/ ?

typos fixed.

Thanks,
-Christoffer

#6 lore raw

PATCH v4 3/13 ARM: KVM: Initial VGIC infrastructure support

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

Wire the basic framework code for VGIC support. Nothing to enable yet.

---
 arch/arm/include/asm/kvm_host.h |    7 ++++
 arch/arm/include/asm/kvm_vgic.h |   70 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/arm.c              |   21 +++++++++++-
 arch/arm/kvm/interrupts.S       |    4 ++
 arch/arm/kvm/mmio.c             |    3 ++
 virt/kvm/kvm_main.c             |    5 ++-
 6 files changed, 107 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h
arch/arm/include/asm/kvm_host.h +7 -0
index e66cd56..49ba25a 100644
@@ -23,6 +23,7 @@
 #include <asm/kvm_asm.h>
 #include <asm/fpstate.h>
 #include <asm/kvm_decode.h>
+#include <asm/kvm_vgic.h>
 
 #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS
 #define KVM_MEMORY_SLOTS 32
@@ -58,6 +59,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 +96,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 +70 -0

70 lines not on this page · raw message

arch/arm/kvm/arm.c +20 -1
index 60b119a..426828a 100644
@@ -183,6 +183,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 {
 	int r;
 	switch (ext) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CAP_IRQCHIP:
+#endif
 	case KVM_CAP_USER_MEMORY:
 	case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
 	case KVM_CAP_ONE_REG:
@@ -304,6 +307,10 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu->arch.target = -1;
+
+	/* Set up VGIC */
+	kvm_vgic_vcpu_init(vcpu);
+
 	return 0;
 }
 
@@ -363,7 +370,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);
 }
 
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -633,6 +640,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		update_vttbr(vcpu->kvm);
 
+		kvm_vgic_sync_to_cpu(vcpu);
+
 		local_irq_disable();
 
 		/*
@@ -645,6 +654,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_from_cpu(vcpu);
 			continue;
 		}
 
@@ -683,6 +693,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * Back from guest
 		 *************************************************************/
 
+		kvm_vgic_sync_from_cpu(vcpu);
+
 		ret = handle_exit(vcpu, run, ret);
 	}
 
@@ -965,6 +977,13 @@ static int init_hyp_mode(void)
 		}
 	}
 
+	/*
+	 * Init HYP view of VGIC
+	 */
+	err = kvm_vgic_hyp_init();
+	if (err)
+		goto out_free_mappings;
+
 	return 0;
 out_free_vfp:
 	free_percpu(kvm_host_vfp_state);
arch/arm/kvm/interrupts.S +4 -0
index 7c89708..e418c9b 100644
@@ -91,6 +91,8 @@ ENTRY(__kvm_vcpu_run)
 
 	save_host_regs
 
+	restore_vgic_state r0
+
 	@ Store hardware CP15 state and load guest state
 	read_cp15_state
 	write_cp15_state 1, r0
@@ -184,6 +186,8 @@ after_vfp_restore:
 	read_cp15_state 1, r1
 	write_cp15_state
 
+	save_vgic_state	r1
+
 	restore_host_regs
 	clrex				@ Clear exclusive monitor
 	bx	lr			@ return to IOCTL
arch/arm/kvm/mmio.c +3 -0
index d6a4ca0..eadec78a 100644
@@ -149,6 +149,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;
 }
virt/kvm/kvm_main.c +3 -2
index 2fb7319..665af96 100644
@@ -1880,12 +1880,13 @@ static long kvm_vcpu_ioctl(struct file *filp,
 	if (vcpu->kvm->mm != current->mm)
 		return -EIO;
 
-#if defined(CONFIG_S390) || defined(CONFIG_PPC)
+#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_ARM)
 	/*
 	 * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
 	 * so vcpu_load() would break it.
 	 */
-	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+	    ioctl == KVM_IRQ_LINE)
 		return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif
 
#7 lore raw

Re: [PATCH v4 03/13] ARM: KVM: Initial VGIC infrastructure support

Quoted 4 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

Wire the basic framework code for VGIC support. Nothing to enable yet.

Again, not sure how useful this patch is. Might as well merge it with code that actually does something. Couple of comments inline anyway...

Quoted 11 lines
---
 arch/arm/include/asm/kvm_host.h |    7 ++++
 arch/arm/include/asm/kvm_vgic.h |   70 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/arm.c              |   21 +++++++++++-
 arch/arm/kvm/interrupts.S       |    4 ++
 arch/arm/kvm/mmio.c             |    3 ++
 virt/kvm/kvm_main.c             |    5 ++-
 6 files changed, 107 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h

[...]

Quoted 26 lines
arch/arm/kvm/arm.c +7 -0
index 60b119a..426828a 100644
@@ -183,6 +183,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 {
 	int r;
 	switch (ext) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CAP_IRQCHIP:
+#endif
 	case KVM_CAP_USER_MEMORY:
 	case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
 	case KVM_CAP_ONE_REG:
@@ -304,6 +307,10 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu->arch.target = -1;
+
+	/* Set up VGIC */
+	kvm_vgic_vcpu_init(vcpu);
+
 	return 0;
 }
 

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, · line 374

Quoted 6 lines
arch/arm/kvm/arm.c +1 -1
@@ -363,7 +370,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);
 }

So interrupt injection without the in-kernel GIC updates irq_lines, but the in-kernel GIC has its own separate data structures? Why can't the in-kernel GIC just use irq_lines instead of irq_pending_on_cpu?

Quoted 12 lines
arch/arm/kvm/arm.c +2 -0
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -633,6 +640,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		update_vttbr(vcpu->kvm);
 
+		kvm_vgic_sync_to_cpu(vcpu);
+
 		local_irq_disable();
 
 		/*

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 659

Quoted 6 lines
arch/arm/kvm/arm.c +1 -0
@@ -645,6 +654,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_from_cpu(vcpu);
 			continue;
 		}

For VFP, we use different terminology (sync and flush). I don't think they're any clearer than what you have, but the consistency would be nice.

Given that both these functions are run with interrupts enabled, why doesn't the second require a lock for updating dist->irq_pending_on_cpu? I notice there's a random smp_mb() over there...

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 696

Quoted 6 lines
arch/arm/kvm/arm.c +1 -0
@@ -683,6 +693,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * Back from guest
 		 *************************************************************/
 
+		kvm_vgic_sync_from_cpu(vcpu);

Likewise.

Quoted 17 lines
arch/arm/kvm/arm.c +7 -0
 		ret = handle_exit(vcpu, run, ret);
 	}
 
@@ -965,6 +977,13 @@ static int init_hyp_mode(void)
 		}
 	}
 
+	/*
+	 * Init HYP view of VGIC
+	 */
+	err = kvm_vgic_hyp_init();
+	if (err)
+		goto out_free_mappings;
+
 	return 0;
 out_free_vfp:
 	free_percpu(kvm_host_vfp_state);

[...]

Quoted 9 lines
virt/kvm/kvm_main.c +0 -1
index 2fb7319..665af96 100644
@@ -1880,12 +1880,13 @@ static long kvm_vcpu_ioctl(struct file *filp,
 	if (vcpu->kvm->mm != current->mm)
 		return -EIO;
 
-#if defined(CONFIG_S390) || defined(CONFIG_PPC)

virt/kvm/kvm_main.c static long kvm_vcpu_ioctl(struct file *filp, · line 1891

Quoted 10 lines
virt/kvm/kvm_main.c +3 -1
+#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_ARM)
 	/*
 	 * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
 	 * so vcpu_load() would break it.
 	 */
-	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+	    ioctl == KVM_IRQ_LINE)
 		return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif

Separate patch?

Will

#8 lore raw

Re: [PATCH v4 03/13] ARM: KVM: Initial VGIC infrastructure support

Quoted 48 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

Wire the basic framework code for VGIC support. Nothing to enable yet.

Again, not sure how useful this patch is. Might as well merge it with code that actually does something. Couple of comments inline anyway...

Quoted 11 lines
---
 arch/arm/include/asm/kvm_host.h |    7 ++++
 arch/arm/include/asm/kvm_vgic.h |   70 +++++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/arm.c              |   21 +++++++++++-
 arch/arm/kvm/interrupts.S       |    4 ++
 arch/arm/kvm/mmio.c             |    3 ++
 virt/kvm/kvm_main.c             |    5 ++-
 6 files changed, 107 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_vgic.h

[...]

Quoted 25 lines
arch/arm/kvm/arm.c +7 -0
index 60b119a..426828a 100644
@@ -183,6 +183,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 {
 	int r;
 	switch (ext) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CAP_IRQCHIP:
+#endif
 	case KVM_CAP_USER_MEMORY:
 	case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
 	case KVM_CAP_ONE_REG:
@@ -304,6 +307,10 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 {
 	/* Force users to call KVM_ARM_VCPU_INIT */
 	vcpu->arch.target = -1;
+
+	/* Set up VGIC */
+	kvm_vgic_vcpu_init(vcpu);
+
 	return 0;
 }
 
Quoted 11 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, · line 374

Quoted 7 lines
arch/arm/kvm/arm.c +1 -1
@@ -363,7 +370,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);
 }

So interrupt injection without the in-kernel GIC updates irq_lines, but the in-kernel GIC has its own separate data structures? Why can't the in-kernel GIC just use irq_lines instead of irq_pending_on_cpu?

They serve very different purposes:
- irq_lines directly controls the IRQ and FIQ lines (it is or-ed into the HCR register before entering the guest)
- irq_pending_on_cpu deals with the CPU interface, and only that. Plus, it is a kernel only thing. What triggers the interrupt on the guest is the presence of list registers with a pending state.

You signal interrupts one way or the other.

Quoted 14 lines
Quoted 14 lines
arch/arm/kvm/arm.c +2 -0
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -633,6 +640,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		update_vttbr(vcpu->kvm);
 
+		kvm_vgic_sync_to_cpu(vcpu);
+
 		local_irq_disable();
 
 		/*
@@ -645,6 +654,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
Quoted 8 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 659

Quoted 5 lines
arch/arm/kvm/arm.c +1 -0
 		if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
 			local_irq_enable();
+			kvm_vgic_sync_from_cpu(vcpu);
 			continue;
 		}

For VFP, we use different terminology (sync and flush). I don't think they're any clearer than what you have, but the consistency would be nice.

Which one maps to which?

Quoted 3 lines

Given that both these functions are run with interrupts enabled, why doesn't the second require a lock for updating dist->irq_pending_on_cpu? I notice there's a random smp_mb() over there...

Updating *only* irq_pending_on_cpu doesn't require the lock (set_bit() should be safe, and I think the smp_mb() is a leftover of some debugging hack). kvm_vgic_to_cpu() does a lot more (it picks interrupt from the distributor, hence requires the lock to be taken).

Quoted 37 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 696

Quoted 6 lines
arch/arm/kvm/arm.c +1 -0
@@ -683,6 +693,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * Back from guest
 		 *************************************************************/
 
+		kvm_vgic_sync_from_cpu(vcpu);

Likewise.

Quoted 17 lines
arch/arm/kvm/arm.c +7 -0
 		ret = handle_exit(vcpu, run, ret);
 	}
 
@@ -965,6 +977,13 @@ static int init_hyp_mode(void)
 		}
 	}
 
+	/*
+	 * Init HYP view of VGIC
+	 */
+	err = kvm_vgic_hyp_init();
+	if (err)
+		goto out_free_mappings;
+
 	return 0;
 out_free_vfp:
 	free_percpu(kvm_host_vfp_state);

[...]

Quoted 8 lines
virt/kvm/kvm_main.c +0 -0
index 2fb7319..665af96 100644
@@ -1880,12 +1880,13 @@ static long kvm_vcpu_ioctl(struct file *filp,
 	if (vcpu->kvm->mm != current->mm)
 		return -EIO;
 
Quoted 13 lines

virt/kvm/kvm_main.c static long kvm_vcpu_ioctl(struct file *filp, · line 1891

Quoted 11 lines
virt/kvm/kvm_main.c +3 -2
-#if defined(CONFIG_S390) || defined(CONFIG_PPC)
+#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_ARM)
 	/*
 	 * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
 	 * so vcpu_load() would break it.
 	 */
-	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+	if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+	    ioctl == KVM_IRQ_LINE)
 		return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif

Separate patch?

Probably, yes.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#9 lore raw

Re: [PATCH v4 03/13] ARM: KVM: Initial VGIC infrastructure support

Quoted 14 lines · Marc Zyngier
Quoted 12 lines · Will Deacon

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, · line 374

Quoted 7 lines · Christoffer Dall
arch/arm/kvm/arm.c +1 -1
@@ -363,7 +370,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);
 }

So interrupt injection without the in-kernel GIC updates irq_lines, but the in-kernel GIC has its own separate data structures? Why can't the in-kernel GIC just use irq_lines instead of irq_pending_on_cpu?

Quoted 8 lines

They serve very different purposes:
- irq_lines directly controls the IRQ and FIQ lines (it is or-ed into the HCR register before entering the guest)
- irq_pending_on_cpu deals with the CPU interface, and only that. Plus, it is a kernel only thing. What triggers the interrupt on the guest is the presence of list registers with a pending state.

You signal interrupts one way or the other.

Ok, thanks for the explanation. I suspect that we could use (another) cosmetic change then. How about cpui_irq_pending and hcr_irq_pending?

Quoted 21 lines
Quoted 12 lines
Quoted 12 lines
arch/arm/kvm/arm.c +2 -0
 int kvm_arch_vcpu_in_guest_mode(struct kvm_vcpu *v)
@@ -633,6 +640,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
 		update_vttbr(vcpu->kvm);
 
+		kvm_vgic_sync_to_cpu(vcpu);
+
 		local_irq_disable();
 
 		/*
@@ -645,6 +654,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
Quoted 8 lines

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 659

Quoted 5 lines
arch/arm/kvm/arm.c +1 -0
 		if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
 			local_irq_enable();
+			kvm_vgic_sync_from_cpu(vcpu);
 			continue;
 		}

For VFP, we use different terminology (sync and flush). I don't think they're any clearer than what you have, but the consistency would be nice.

Quoted 1 line

Which one maps to which?

sync: hardware -> data structure
flush: data structure -> hardware

Quoted 8 lines
Quoted 3 lines

Given that both these functions are run with interrupts enabled, why doesn't the second require a lock for updating dist->irq_pending_on_cpu? I notice there's a random smp_mb() over there...

Updating *only* irq_pending_on_cpu doesn't require the lock (set_bit() should be safe, and I think the smp_mb() is a leftover of some debugging hack). kvm_vgic_to_cpu() does a lot more (it picks interrupt from the distributor, hence requires the lock to be taken).

Ok, if the barrier is just a hangover from something else and you don't have any races with test/clear operations then you should be alright.

Will

#10 lore raw

Re: [PATCH v4 03/13] ARM: KVM: Initial VGIC infrastructure support

In reply to Will Deacon

[...]

Quoted 8 lines
Quoted 8 lines
virt/kvm/kvm_main.c +0 -0
index 2fb7319..665af96 100644
@@ -1880,12 +1880,13 @@ static long kvm_vcpu_ioctl(struct file *filp,
      if (vcpu->kvm->mm != current->mm)
              return -EIO;
 
Quoted 14 lines

virt/kvm/kvm_main.c static long kvm_vcpu_ioctl(struct file *filp, · line 1891

Quoted 11 lines
virt/kvm/kvm_main.c +3 -2
-#if defined(CONFIG_S390) || defined(CONFIG_PPC)
+#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_ARM)
      /*
       * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
       * so vcpu_load() would break it.
       */
-     if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
+     if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT ||
+         ioctl == KVM_IRQ_LINE)
              return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
 #endif

Separate patch?

KVM_IRQ_LINE is a VM ioctl, not a vcpu ioctl, so this is leftover from the duplicate VCPU/VM same ioctl mess.

It's gone!

-Christoffer

#11 lore raw

PATCH v4 4/13 ARM: KVM: Initial VGIC MMIO support code

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

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

---
 arch/arm/include/asm/kvm_vgic.h |    6 +-
 arch/arm/kvm/Makefile           |    1 
 arch/arm/kvm/vgic.c             |  138 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/kvm/vgic.c
arch/arm/include/asm/kvm_vgic.h +5 -1
index d75540a..b444ecf 100644
@@ -30,7 +30,11 @@ struct kvm_vcpu;
 struct kvm_run;
 struct kvm_exit_mmio;
 
-#ifndef CONFIG_KVM_ARM_VGIC
+#ifdef CONFIG_KVM_ARM_VGIC
+bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
+		      struct kvm_exit_mmio *mmio);
+
+#else
 static inline int kvm_vgic_hyp_init(void)
 {
 	return 0;
arch/arm/kvm/Makefile +1 -0
index 8a4f396..c019f02 100644
@@ -20,3 +20,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc
 
 obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
 obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
+obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
arch/arm/kvm/vgic.c new file +138 -0

138 lines not on this page · raw message

#12 lore raw

Re: [PATCH v4 04/13] ARM: KVM: Initial VGIC MMIO support code

Quoted 73 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

---
 arch/arm/include/asm/kvm_vgic.h |    6 +-
 arch/arm/kvm/Makefile           |    1 
 arch/arm/kvm/vgic.c             |  138 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/kvm/vgic.c
arch/arm/include/asm/kvm_vgic.h +5 -1
index d75540a..b444ecf 100644
@@ -30,7 +30,11 @@ struct kvm_vcpu;
 struct kvm_run;
 struct kvm_exit_mmio;
 
-#ifndef CONFIG_KVM_ARM_VGIC
+#ifdef CONFIG_KVM_ARM_VGIC
+bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
+		      struct kvm_exit_mmio *mmio);
+
+#else
 static inline int kvm_vgic_hyp_init(void)
 {
 	return 0;
arch/arm/kvm/Makefile +1 -0
index 8a4f396..c019f02 100644
@@ -20,3 +20,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc
 
 obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
 obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
+obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
arch/arm/kvm/vgic.c new file +27 -0
new file mode 100644
index 0000000..26ada3b
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <[email protected]>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <asm/kvm_emulate.h>
+
+#define ACCESS_READ_VALUE	(1 << 0)
+#define ACCESS_READ_RAZ		(0 << 0)
+#define ACCESS_READ_MASK(x)	((x) & (1 << 0))

arch/arm/kvm/vgic.c line 37

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+#define ACCESS_WRITE_IGNORED	(0 << 1)
+#define ACCESS_WRITE_SETBIT	(1 << 1)
+#define ACCESS_WRITE_CLEARBIT	(2 << 1)
+#define ACCESS_WRITE_VALUE	(3 << 1)
+#define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
+
+/**
+ * vgic_reg_access - access vgic register
+ * @mmio:   pointer to the data describing the mmio access
+ * @reg:    pointer to the virtual backing of the vgic distributor struct

Is this correct?

Quoted 83 lines
arch/arm/kvm/vgic.c new file +83 -0
+ * @offset: least significant 2 bits used for word offset
+ * @mode:   ACCESS_ mode (see defines above)
+ *
+ * Helper to make vgic register access easier using one of the access
+ * modes defined for vgic register access
+ * (read,raz,write-ignored,setbit,clearbit,write)
+ */
+static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
+			    u32 offset, int mode)
+{
+	int word_offset = offset & 3;
+	int shift = word_offset * 8;
+	u32 mask;
+	u32 regval;
+
+	/*
+	 * Any alignment fault should have been delivered to the guest
+	 * directly (ARM ARM B3.12.7 "Prioritization of aborts").
+	 */
+
+	mask = (~0U) >> (word_offset * 8);
+	if (reg)
+		regval = *reg;
+	else {
+		BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
+		regval = 0;
+	}
+
+	if (mmio->is_write) {
+		u32 data = (*((u32 *)mmio->data) & mask) << shift;
+		switch (ACCESS_WRITE_MASK(mode)) {
+		case ACCESS_WRITE_IGNORED:
+			return;
+
+		case ACCESS_WRITE_SETBIT:
+			regval |= data;
+			break;
+
+		case ACCESS_WRITE_CLEARBIT:
+			regval &= ~data;
+			break;
+
+		case ACCESS_WRITE_VALUE:
+			regval = (regval & ~(mask << shift)) | data;
+			break;
+		}
+		*reg = regval;
+	} else {
+		switch (ACCESS_READ_MASK(mode)) {
+		case ACCESS_READ_RAZ:
+			regval = 0;
+			/* fall through */
+
+		case ACCESS_READ_VALUE:
+			*((u32 *)mmio->data) = (regval >> shift) & mask;
+		}
+	}
+}
+
+/* All this should be handled by kvm_bus_io_*()... FIXME!!! */
+struct mmio_range {
+	unsigned long base;
+	unsigned long len;
+	bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+			    u32 offset);
+};
+
+static const struct mmio_range vgic_ranges[] = {
+	{}
+};
+
+static const
+struct mmio_range *find_matching_range(const struct mmio_range *ranges,
+				       struct kvm_exit_mmio *mmio,
+				       unsigned long base)
+{
+	const struct mmio_range *r = ranges;
+	unsigned long addr = mmio->phys_addr - base;
+
+	while (r->len) {
+		if (addr >= r->base &&
+		    (addr + mmio->len) <= (r->base + r->len))
+			return r;

arch/arm/kvm/vgic.c line 130

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+		r++;
+	}
+
+	return NULL;
+}
+
+/**
+ * vgic_handle_mmio - handle an in-kernel MMIO access
+ * @vcpu:	pointer to the vcpu performing the access
+ * @mmio:	pointer to the data describing the access

Can we also have @run here?

arch/arm/kvm/vgic.c line 138

Quoted 9 lines
arch/arm/kvm/vgic.c new file +8 -0
+ *
+ * returns true if the MMIO access has been performed in kernel space,
+ * and false if it needs to be emulated in user space.
+ */
+bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
+{
+	return KVM_EXIT_MMIO;
+}

Regards
Dong Aisheng

#13 lore raw

Re: [PATCH v4 04/13] ARM: KVM: Initial VGIC MMIO support code

Quoted 183 lines · Dong Aisheng
Quoted 73 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

---
 arch/arm/include/asm/kvm_vgic.h |    6 +-
 arch/arm/kvm/Makefile           |    1
 arch/arm/kvm/vgic.c             |  138 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/kvm/vgic.c
arch/arm/include/asm/kvm_vgic.h +5 -1
index d75540a..b444ecf 100644
@@ -30,7 +30,11 @@ struct kvm_vcpu;
 struct kvm_run;
 struct kvm_exit_mmio;
 
-#ifndef CONFIG_KVM_ARM_VGIC
+#ifdef CONFIG_KVM_ARM_VGIC
+bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
+                   struct kvm_exit_mmio *mmio);
+
+#else
 static inline int kvm_vgic_hyp_init(void)
 {
      return 0;
arch/arm/kvm/Makefile +1 -0
index 8a4f396..c019f02 100644
@@ -20,3 +20,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc
 
 obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
 obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
+obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
arch/arm/kvm/vgic.c new file +27 -0
new file mode 100644
index 0000000..26ada3b
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <[email protected]>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <asm/kvm_emulate.h>
+
+#define ACCESS_READ_VALUE    (1 << 0)
+#define ACCESS_READ_RAZ              (0 << 0)
+#define ACCESS_READ_MASK(x)  ((x) & (1 << 0))

arch/arm/kvm/vgic.c line 37

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+#define ACCESS_WRITE_IGNORED (0 << 1)
+#define ACCESS_WRITE_SETBIT  (1 << 1)
+#define ACCESS_WRITE_CLEARBIT        (2 << 1)
+#define ACCESS_WRITE_VALUE   (3 << 1)
+#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
+
+/**
+ * vgic_reg_access - access vgic register
+ * @mmio:   pointer to the data describing the mmio access
+ * @reg:    pointer to the virtual backing of the vgic distributor struct

Is this correct?

Quoted 83 lines
arch/arm/kvm/vgic.c new file +83 -0
+ * @offset: least significant 2 bits used for word offset
+ * @mode:   ACCESS_ mode (see defines above)
+ *
+ * Helper to make vgic register access easier using one of the access
+ * modes defined for vgic register access
+ * (read,raz,write-ignored,setbit,clearbit,write)
+ */
+static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
+                         u32 offset, int mode)
+{
+     int word_offset = offset & 3;
+     int shift = word_offset * 8;
+     u32 mask;
+     u32 regval;
+
+     /*
+      * Any alignment fault should have been delivered to the guest
+      * directly (ARM ARM B3.12.7 "Prioritization of aborts").
+      */
+
+     mask = (~0U) >> (word_offset * 8);
+     if (reg)
+             regval = *reg;
+     else {
+             BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
+             regval = 0;
+     }
+
+     if (mmio->is_write) {
+             u32 data = (*((u32 *)mmio->data) & mask) << shift;
+             switch (ACCESS_WRITE_MASK(mode)) {
+             case ACCESS_WRITE_IGNORED:
+                     return;
+
+             case ACCESS_WRITE_SETBIT:
+                     regval |= data;
+                     break;
+
+             case ACCESS_WRITE_CLEARBIT:
+                     regval &= ~data;
+                     break;
+
+             case ACCESS_WRITE_VALUE:
+                     regval = (regval & ~(mask << shift)) | data;
+                     break;
+             }
+             *reg = regval;
+     } else {
+             switch (ACCESS_READ_MASK(mode)) {
+             case ACCESS_READ_RAZ:
+                     regval = 0;
+                     /* fall through */
+
+             case ACCESS_READ_VALUE:
+                     *((u32 *)mmio->data) = (regval >> shift) & mask;
+             }
+     }
+}
+
+/* All this should be handled by kvm_bus_io_*()... FIXME!!! */
+struct mmio_range {
+     unsigned long base;
+     unsigned long len;
+     bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+                         u32 offset);
+};
+
+static const struct mmio_range vgic_ranges[] = {
+     {}
+};
+
+static const
+struct mmio_range *find_matching_range(const struct mmio_range *ranges,
+                                    struct kvm_exit_mmio *mmio,
+                                    unsigned long base)
+{
+     const struct mmio_range *r = ranges;
+     unsigned long addr = mmio->phys_addr - base;
+
+     while (r->len) {
+             if (addr >= r->base &&
+                 (addr + mmio->len) <= (r->base + r->len))
+                     return r;

arch/arm/kvm/vgic.c line 130

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+             r++;
+     }
+
+     return NULL;
+}
+
+/**
+ * vgic_handle_mmio - handle an in-kernel MMIO access
+ * @vcpu:    pointer to the vcpu performing the access
+ * @mmio:    pointer to the data describing the access

Can we also have @run here?

Quoted 9 lines

arch/arm/kvm/vgic.c line 138

Quoted 9 lines
arch/arm/kvm/vgic.c new file +8 -0
+ *
+ * returns true if the MMIO access has been performed in kernel space,
+ * and false if it needs to be emulated in user space.
+ */
+bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
+{
+     return KVM_EXIT_MMIO;
+}

thanks,
fixed

-Christoffer

#14 lore raw

Re: [PATCH v4 04/13] ARM: KVM: Initial VGIC MMIO support code

In reply to Christoffer Dall
Quoted 4 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

[...]

Quoted 44 lines
arch/arm/kvm/vgic.c new file +38 -0
new file mode 100644
index 0000000..26ada3b
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <[email protected]>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <asm/kvm_emulate.h>
+
+#define ACCESS_READ_VALUE	(1 << 0)
+#define ACCESS_READ_RAZ		(0 << 0)
+#define ACCESS_READ_MASK(x)	((x) & (1 << 0))
+#define ACCESS_WRITE_IGNORED	(0 << 1)
+#define ACCESS_WRITE_SETBIT	(1 << 1)
+#define ACCESS_WRITE_CLEARBIT	(2 << 1)
+#define ACCESS_WRITE_VALUE	(3 << 1)
+#define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
+
+/**
+ * vgic_reg_access - access vgic register
+ * @mmio:   pointer to the data describing the mmio access
+ * @reg:    pointer to the virtual backing of the vgic distributor struct
+ * @offset: least significant 2 bits used for word offset

arch/arm/kvm/vgic.c line 48

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+ * @mode:   ACCESS_ mode (see defines above)
+ *
+ * Helper to make vgic register access easier using one of the access
+ * modes defined for vgic register access
+ * (read,raz,write-ignored,setbit,clearbit,write)
+ */
+static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
+			    u32 offset, int mode)
+{
+	int word_offset = offset & 3;

You can get rid of this variable.

arch/arm/kvm/vgic.c line 49

Quoted 1 line
arch/arm/kvm/vgic.c new file +1 -0
+	int shift = word_offset * 8;

shift = (offset & 3) << 3;

arch/arm/kvm/vgic.c line 58

Quoted 9 lines
arch/arm/kvm/vgic.c new file +9 -0
+	u32 mask;
+	u32 regval;
+
+	/*
+	 * Any alignment fault should have been delivered to the guest
+	 * directly (ARM ARM B3.12.7 "Prioritization of aborts").
+	 */
+
+	mask = (~0U) >> (word_offset * 8);

then use shift here.

arch/arm/kvm/vgic.c line 61

Quoted 3 lines
arch/arm/kvm/vgic.c new file +3 -0
+	if (reg)
+		regval = *reg;
+	else {

Use braces for the if clause.

Quoted 24 lines
arch/arm/kvm/vgic.c new file +24 -0
+		BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
+		regval = 0;
+	}
+
+	if (mmio->is_write) {
+		u32 data = (*((u32 *)mmio->data) & mask) << shift;
+		switch (ACCESS_WRITE_MASK(mode)) {
+		case ACCESS_WRITE_IGNORED:
+			return;
+
+		case ACCESS_WRITE_SETBIT:
+			regval |= data;
+			break;
+
+		case ACCESS_WRITE_CLEARBIT:
+			regval &= ~data;
+			break;
+
+		case ACCESS_WRITE_VALUE:
+			regval = (regval & ~(mask << shift)) | data;
+			break;
+		}
+		*reg = regval;
+	} else {

arch/arm/kvm/vgic.c line 95

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+		switch (ACCESS_READ_MASK(mode)) {
+		case ACCESS_READ_RAZ:
+			regval = 0;
+			/* fall through */
+
+		case ACCESS_READ_VALUE:
+			*((u32 *)mmio->data) = (regval >> shift) & mask;
+		}
+	}
+}

It might be a good idea to have some port accessors for mmio->data otherwise you'll likely get endianness issues creeping in.

arch/arm/kvm/vgic.c line 97

Quoted 1 line
arch/arm/kvm/vgic.c new file +1 -0
+/* All this should be handled by kvm_bus_io_*()... FIXME!!! */

I don't follow this comment :) Can you either make it clearer (and less alarming!) or just drop it please?

arch/arm/kvm/vgic.c line 103

Quoted 6 lines
arch/arm/kvm/vgic.c new file +6 -0
+struct mmio_range {
+	unsigned long base;
+	unsigned long len;
+	bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+			    u32 offset);
+};

Why not make offset a phys_addr_t?

arch/arm/kvm/vgic.c line 115

Quoted 11 lines
arch/arm/kvm/vgic.c new file +11 -0
+static const struct mmio_range vgic_ranges[] = {
+	{}
+};
+
+static const
+struct mmio_range *find_matching_range(const struct mmio_range *ranges,
+				       struct kvm_exit_mmio *mmio,
+				       unsigned long base)
+{
+	const struct mmio_range *r = ranges;
+	unsigned long addr = mmio->phys_addr - base;

Same here, I don't think we want to truncate everything to unsigned long.

arch/arm/kvm/vgic.c line 122

Quoted 6 lines
arch/arm/kvm/vgic.c new file +6 -0
+	while (r->len) {
+		if (addr >= r->base &&
+		    (addr + mmio->len) <= (r->base + r->len))
+			return r;
+		r++;
+	}

Hmm, does this work correctly for adjacent mmio devices where addr sits on the boundary (i.e. the first address of the second device)?

Will

#15 lore raw

Re: [PATCH v4 04/13] ARM: KVM: Initial VGIC MMIO support code

Quoted 69 lines · Will Deacon
Quoted 4 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

[...]

Quoted 44 lines
arch/arm/kvm/vgic.c new file +38 -0
new file mode 100644
index 0000000..26ada3b
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <[email protected]>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kvm.h>
+#include <linux/kvm_host.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <asm/kvm_emulate.h>
+
+#define ACCESS_READ_VALUE	(1 << 0)
+#define ACCESS_READ_RAZ		(0 << 0)
+#define ACCESS_READ_MASK(x)	((x) & (1 << 0))
+#define ACCESS_WRITE_IGNORED	(0 << 1)
+#define ACCESS_WRITE_SETBIT	(1 << 1)
+#define ACCESS_WRITE_CLEARBIT	(2 << 1)
+#define ACCESS_WRITE_VALUE	(3 << 1)
+#define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
+
+/**
+ * vgic_reg_access - access vgic register
+ * @mmio:   pointer to the data describing the mmio access
+ * @reg:    pointer to the virtual backing of the vgic distributor struct
+ * @offset: least significant 2 bits used for word offset

arch/arm/kvm/vgic.c line 48

Quoted 10 lines
arch/arm/kvm/vgic.c new file +10 -0
+ * @mode:   ACCESS_ mode (see defines above)
+ *
+ * Helper to make vgic register access easier using one of the access
+ * modes defined for vgic register access
+ * (read,raz,write-ignored,setbit,clearbit,write)
+ */
+static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
+			    u32 offset, int mode)
+{
+	int word_offset = offset & 3;

You can get rid of this variable.

arch/arm/kvm/vgic.c line 49

Quoted 1 line
arch/arm/kvm/vgic.c new file +1 -0
+	int shift = word_offset * 8;

shift = (offset & 3) << 3;

Quoted 11 lines

arch/arm/kvm/vgic.c line 58

Quoted 9 lines
arch/arm/kvm/vgic.c new file +9 -0
+	u32 mask;
+	u32 regval;
+
+	/*
+	 * Any alignment fault should have been delivered to the guest
+	 * directly (ARM ARM B3.12.7 "Prioritization of aborts").
+	 */
+
+	mask = (~0U) >> (word_offset * 8);

then use shift here.

Sure.

Quoted 5 lines

arch/arm/kvm/vgic.c line 61

Quoted 3 lines
arch/arm/kvm/vgic.c new file +3 -0
+	if (reg)
+		regval = *reg;
+	else {

Use braces for the if clause.

Indeed.

Quoted 30 lines
Quoted 30 lines
arch/arm/kvm/vgic.c new file +29 -0
+		BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
+		regval = 0;
+	}
+
+	if (mmio->is_write) {
+		u32 data = (*((u32 *)mmio->data) & mask) << shift;
+		switch (ACCESS_WRITE_MASK(mode)) {
+		case ACCESS_WRITE_IGNORED:
+			return;
+
+		case ACCESS_WRITE_SETBIT:
+			regval |= data;
+			break;
+
+		case ACCESS_WRITE_CLEARBIT:
+			regval &= ~data;
+			break;
+
+		case ACCESS_WRITE_VALUE:
+			regval = (regval & ~(mask << shift)) | data;
+			break;
+		}
+		*reg = regval;
+	} else {
+		switch (ACCESS_READ_MASK(mode)) {
+		case ACCESS_READ_RAZ:
+			regval = 0;
+			/* fall through */
+
Quoted 8 lines

arch/arm/kvm/vgic.c line 95

Quoted 5 lines
arch/arm/kvm/vgic.c new file +5 -0
+		case ACCESS_READ_VALUE:
+			*((u32 *)mmio->data) = (regval >> shift) & mask;
+		}
+	}
+}

It might be a good idea to have some port accessors for mmio->data otherwise you'll likely get endianness issues creeping in.

Aarrgh! This depends on the relative endianess of host and guest. 'm feeling slightly sick...

Quoted 4 lines

arch/arm/kvm/vgic.c line 97

Quoted 1 line
arch/arm/kvm/vgic.c new file +1 -0
+/* All this should be handled by kvm_bus_io_*()... FIXME!!! */

I don't follow this comment :) Can you either make it clearer (and less alarming!) or just drop it please?

The non-alarming version should read:

/*
* I would have liked to use the kvm_bus_io_*() API instead, but
* it cannot cope with banked registers (only the VM pointer is
* passed around, and we need the vcpu). One of these days, someone
* please fix it!
*/

Quoted 9 lines

arch/arm/kvm/vgic.c line 103

Quoted 7 lines
arch/arm/kvm/vgic.c new file +6 -0
+struct mmio_range {
+	unsigned long base;
+	unsigned long len;
+	bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+			    u32 offset);
+};

Why not make offset a phys_addr_t?

Very good point.

Quoted 5 lines
Quoted 5 lines
arch/arm/kvm/vgic.c new file +5 -0
+static const struct mmio_range vgic_ranges[] = {
+	{}
+};
+
+static const
Quoted 8 lines

arch/arm/kvm/vgic.c line 115

Quoted 6 lines
arch/arm/kvm/vgic.c new file +6 -0
+struct mmio_range *find_matching_range(const struct mmio_range *ranges,
+				       struct kvm_exit_mmio *mmio,
+				       unsigned long base)
+{
+	const struct mmio_range *r = ranges;
+	unsigned long addr = mmio->phys_addr - base;

Same here, I don't think we want to truncate everything to unsigned long.

Indeed.

Quoted 9 lines

arch/arm/kvm/vgic.c line 122

Quoted 6 lines
arch/arm/kvm/vgic.c new file +6 -0
+	while (r->len) {
+		if (addr >= r->base &&
+		    (addr + mmio->len) <= (r->base + r->len))
+			return r;
+		r++;
+	}

Hmm, does this work correctly for adjacent mmio devices where addr sits on the boundary (i.e. the first address of the second device)?

I think it is OK. We basically check that both ends of the access are within a single range (we do not bother with cross range accesses).

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#16 lore raw

PATCH v4 5/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.

---
 arch/arm/include/asm/kvm_mmu.h  |    2 +
 arch/arm/include/asm/kvm_vgic.h |    9 ++++++
 arch/arm/kvm/arm.c              |   16 ++++++++++
 arch/arm/kvm/vgic.c             |   61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_mmu.h +2 -0
index 9bd0508..0800531 100644
@@ -26,6 +26,8 @@
  * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
  * for now, but remember that the level-1 table must be aligned to its size.
  */
+#define KVM_PHYS_SHIFT	(38)
+#define KVM_PHYS_MASK	((1ULL << KVM_PHYS_SHIFT) - 1)
 #define PTRS_PER_PGD2	512
 #define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
arch/arm/include/asm/kvm_vgic.h +9 -0
index b444ecf..9ca8d21 100644
@@ -20,6 +20,9 @@
 #define __ASM_ARM_KVM_VGIC_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 {
@@ -31,6 +34,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);
 
@@ -40,6 +44,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/kvm/arm.c +15 -1
index 426828a..3ac1aab 100644
@@ -61,6 +61,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());
@@ -825,7 +827,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_address(struct kvm *kvm,
 					   struct kvm_device_address *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 +61 -0

70 lines not on this page · raw message

#17 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

On Sat, Nov 10, 2012 at 04:44:51PM +0100, Christoffer Dall wrote:
[...]

Quoted 24 lines
arch/arm/kvm/vgic.c +24 -0
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+	int r = 0;
+	struct vgic_dist *vgic = &kvm->arch.vgic;
+
+	if (addr & ~KVM_PHYS_MASK)
+		return -E2BIG;
+
+	if (addr & ~PAGE_MASK)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+	switch (type) {
+	case KVM_VGIC_V2_ADDR_TYPE_DIST:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+			return -EEXIST;
+		if (addr + VGIC_DIST_SIZE < addr)
+			return -EINVAL;
+		kvm->arch.vgic.vgic_dist_base = addr;
+		break;
+	case KVM_VGIC_V2_ADDR_TYPE_CPU:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+			return -EEXIST;
+		if (addr + VGIC_CPU_SIZE < addr)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 193

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+			return -EINVAL;
+		kvm->arch.vgic.vgic_cpu_base = addr;
+		break;
+	default:
+		r = -ENODEV;
+	}
+
+	if (vgic_ioaddr_overlap(kvm)) {
+		kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;

Missing mutex_unlock?

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 199

Quoted 7 lines
arch/arm/kvm/vgic.c +6 -0
+		return -EINVAL;
+	}
+
+	mutex_unlock(&kvm->lock);
+	return r;
+}

Regards
Dong Aisheng

#18 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

Quoted 30 lines · Dong Aisheng

On Sat, Nov 10, 2012 at 04:44:51PM +0100, Christoffer Dall wrote:
[...]

Quoted 28 lines
arch/arm/kvm/vgic.c +28 -0
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+     int r = 0;
+     struct vgic_dist *vgic = &kvm->arch.vgic;
+
+     if (addr & ~KVM_PHYS_MASK)
+             return -E2BIG;
+
+     if (addr & ~PAGE_MASK)
+             return -EINVAL;
+
+     mutex_lock(&kvm->lock);
+     switch (type) {
+     case KVM_VGIC_V2_ADDR_TYPE_DIST:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+                     return -EEXIST;
+             if (addr + VGIC_DIST_SIZE < addr)
+                     return -EINVAL;
+             kvm->arch.vgic.vgic_dist_base = addr;
+             break;
+     case KVM_VGIC_V2_ADDR_TYPE_CPU:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+                     return -EEXIST;
+             if (addr + VGIC_CPU_SIZE < addr)
+                     return -EINVAL;
+             kvm->arch.vgic.vgic_cpu_base = addr;
+             break;
+     default:
Quoted 8 lines

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 193

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+             r = -ENODEV;
+     }
+
+     if (vgic_ioaddr_overlap(kvm)) {
+             kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+             kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;

Missing mutex_unlock?

indeed, should be r = -EINVAL.

nice catch!

#19 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

In reply to Christoffer Dall
Quoted 19 lines · 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.

---
 arch/arm/include/asm/kvm_mmu.h  |    2 +
 arch/arm/include/asm/kvm_vgic.h |    9 ++++++
 arch/arm/kvm/arm.c              |   16 ++++++++++
 arch/arm/kvm/vgic.c             |   61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_mmu.h +0 -0
index 9bd0508..0800531 100644

arch/arm/include/asm/kvm_mmu.h line 29

Quoted 4 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -26,6 +26,8 @@
  * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
  * for now, but remember that the level-1 table must be aligned to its size.
  */
+#define KVM_PHYS_SHIFT	(38)

Seems a bit small...

Quoted 77 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
+#define KVM_PHYS_MASK	((1ULL << KVM_PHYS_SHIFT) - 1)
 #define PTRS_PER_PGD2	512
 #define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
arch/arm/include/asm/kvm_vgic.h +9 -0
index b444ecf..9ca8d21 100644
@@ -20,6 +20,9 @@
 #define __ASM_ARM_KVM_VGIC_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 {
@@ -31,6 +34,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);
 
@@ -40,6 +44,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/kvm/arm.c +15 -1
index 426828a..3ac1aab 100644
@@ -61,6 +61,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());
@@ -825,7 +827,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_address(struct kvm *kvm,
 					   struct kvm_device_address *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 +0 -0
index 26ada3b..f85b275 100644

arch/arm/kvm/vgic.c line 29

Quoted 8 lines
arch/arm/kvm/vgic.c +5 -0
@@ -22,6 +22,13 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
+#define VGIC_ADDR_UNDEF		(-1)
+#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
+
+#define VGIC_DIST_SIZE		0x1000
+#define VGIC_CPU_SIZE		0x2000

These defines might be useful to userspace so that they don't request the distributor and the cpu interface to be place too close together (been there, done that :).

Quoted 11 lines
arch/arm/kvm/vgic.c +4 -0
+
+
 #define ACCESS_READ_VALUE	(1 << 0)
 #define ACCESS_READ_RAZ		(0 << 0)
 #define ACCESS_READ_MASK(x)	((x) & (1 << 0))
@@ -136,3 +143,57 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 {
 	return KVM_EXIT_MMIO;
 }
+
+static bool vgic_ioaddr_overlap(struct kvm *kvm)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 157

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+{
+	phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
+	phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
+
+	if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
+		return false;
+	if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
+	    (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
+		return true;
+	return false;

Just return the predicate that you're testing.

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+}
+
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+	int r = 0;
+	struct vgic_dist *vgic = &kvm->arch.vgic;
+
+	if (addr & ~KVM_PHYS_MASK)
+		return -E2BIG;
+

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 177

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+	if (addr & ~PAGE_MASK)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+	switch (type) {
+	case KVM_VGIC_V2_ADDR_TYPE_DIST:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+			return -EEXIST;
+		if (addr + VGIC_DIST_SIZE < addr)
+			return -EINVAL;

I think somebody else pointed out the missing mutex_unlocks on the failure paths.

Quoted 7 lines
arch/arm/kvm/vgic.c +7 -0
+		kvm->arch.vgic.vgic_dist_base = addr;
+		break;
+	case KVM_VGIC_V2_ADDR_TYPE_CPU:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+			return -EEXIST;
+		if (addr + VGIC_CPU_SIZE < addr)
+			return -EINVAL;

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 194

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+		kvm->arch.vgic.vgic_cpu_base = addr;
+		break;
+	default:
+		r = -ENODEV;
+	}
+
+	if (vgic_ioaddr_overlap(kvm)) {
+		kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+		return -EINVAL;

Perhaps we could put all the address checking in one place, so that the wrapping round zero checks and the overlap checks can be in the same function?

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 199

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+	}
+
+	mutex_unlock(&kvm->lock);
+	return r;
+}

Will

#20 lore raw

Re: [kvmarm] [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

Quoted 15 lines · Will Deacon
Quoted 14 lines · 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.

---
 arch/arm/include/asm/kvm_mmu.h  |    2 +
 arch/arm/include/asm/kvm_vgic.h |    9 ++++++
 arch/arm/kvm/arm.c              |   16 ++++++++++
 arch/arm/kvm/vgic.c             |   61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
Quoted 11 lines

arch/arm/include/asm/kvm_mmu.h line 29

Quoted 9 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
index 9bd0508..0800531 100644
@@ -26,6 +26,8 @@
  * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
  * for now, but remember that the level-1 table must be aligned to its size.
  */
+#define KVM_PHYS_SHIFT	(38)

Seems a bit small...

It's now been fixed to be 40 bits.

Quoted 80 lines

+#define KVM_PHYS_MASK ((1ULL << KVM_PHYS_SHIFT) - 1)

Quoted 79 lines
arch/arm/include/asm/kvm_mmu.h +0 -0
 #define PTRS_PER_PGD2	512
 #define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
arch/arm/include/asm/kvm_vgic.h +9 -0
index b444ecf..9ca8d21 100644
@@ -20,6 +20,9 @@
 #define __ASM_ARM_KVM_VGIC_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 {
@@ -31,6 +34,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);
 
@@ -40,6 +44,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/kvm/arm.c +15 -1
index 426828a..3ac1aab 100644
@@ -61,6 +61,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());
@@ -825,7 +827,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_address(struct kvm *kvm,
 					   struct kvm_device_address *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 +0 -0
index 26ada3b..f85b275 100644
@@ -22,6 +22,13 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
Quoted 9 lines

arch/arm/kvm/vgic.c line 29

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+#define VGIC_ADDR_UNDEF		(-1)
+#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
+
+#define VGIC_DIST_SIZE		0x1000
+#define VGIC_CPU_SIZE		0x2000

These defines might be useful to userspace so that they don't request the distributor and the cpu interface to be place too close together (been there, done that :).

Fair enough.

Quoted 39 lines
Quoted 11 lines
arch/arm/kvm/vgic.c +4 -0
+
+
 #define ACCESS_READ_VALUE	(1 << 0)
 #define ACCESS_READ_RAZ		(0 << 0)
 #define ACCESS_READ_MASK(x)	((x) & (1 << 0))
@@ -136,3 +143,57 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 {
 	return KVM_EXIT_MMIO;
 }
+
+static bool vgic_ioaddr_overlap(struct kvm *kvm)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 157

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+{
+	phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
+	phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
+
+	if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
+		return false;
+	if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
+	    (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
+		return true;
+	return false;

Just return the predicate that you're testing.

Quoted 15 lines
arch/arm/kvm/vgic.c +15 -0
+}
+
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+	int r = 0;
+	struct vgic_dist *vgic = &kvm->arch.vgic;
+
+	if (addr & ~KVM_PHYS_MASK)
+		return -E2BIG;
+
+	if (addr & ~PAGE_MASK)
+		return -EINVAL;
+
+	mutex_lock(&kvm->lock);
+	switch (type) {
Quoted 8 lines

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 177

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+	case KVM_VGIC_V2_ADDR_TYPE_DIST:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+			return -EEXIST;
+		if (addr + VGIC_DIST_SIZE < addr)
+			return -EINVAL;

I think somebody else pointed out the missing mutex_unlocks on the failure paths.

Yes, it's been fixed in the tree already.

Quoted 33 lines
Quoted 7 lines
arch/arm/kvm/vgic.c +7 -0
+		kvm->arch.vgic.vgic_dist_base = addr;
+		break;
+	case KVM_VGIC_V2_ADDR_TYPE_CPU:
+		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+			return -EEXIST;
+		if (addr + VGIC_CPU_SIZE < addr)
+			return -EINVAL;

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 194

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+		kvm->arch.vgic.vgic_cpu_base = addr;
+		break;
+	default:
+		r = -ENODEV;
+	}
+
+	if (vgic_ioaddr_overlap(kvm)) {
+		kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+		return -EINVAL;

Perhaps we could put all the address checking in one place, so that the wrapping round zero checks and the overlap checks can be in the same function?

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 199

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+	}
+
+	mutex_unlock(&kvm->lock);
+	return r;
+}

Will
_______________________________________________
kvmarm mailing list
kvmarm at lists.cs.columbia.edu
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm

Signature · 1 line
Jazz is not dead. It just smells funny...
#21 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

In reply to Will Deacon
Quoted 183 lines · Will Deacon
Quoted 19 lines · 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.

---
 arch/arm/include/asm/kvm_mmu.h  |    2 +
 arch/arm/include/asm/kvm_vgic.h |    9 ++++++
 arch/arm/kvm/arm.c              |   16 ++++++++++
 arch/arm/kvm/vgic.c             |   61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_mmu.h +0 -0
index 9bd0508..0800531 100644

arch/arm/include/asm/kvm_mmu.h line 29

Quoted 4 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -26,6 +26,8 @@
  * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
  * for now, but remember that the level-1 table must be aligned to its size.
  */
+#define KVM_PHYS_SHIFT       (38)

Seems a bit small...

Quoted 77 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
+#define KVM_PHYS_MASK        ((1ULL << KVM_PHYS_SHIFT) - 1)
 #define PTRS_PER_PGD2        512
 #define PGD2_ORDER   get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
arch/arm/include/asm/kvm_vgic.h +9 -0
index b444ecf..9ca8d21 100644
@@ -20,6 +20,9 @@
 #define __ASM_ARM_KVM_VGIC_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 {
@@ -31,6 +34,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);
 
@@ -40,6 +44,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/kvm/arm.c +15 -1
index 426828a..3ac1aab 100644
@@ -61,6 +61,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());
@@ -825,7 +827,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_address(struct kvm *kvm,
                                         struct kvm_device_address *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 +0 -0
index 26ada3b..f85b275 100644

arch/arm/kvm/vgic.c line 29

Quoted 8 lines
arch/arm/kvm/vgic.c +5 -0
@@ -22,6 +22,13 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
+#define VGIC_ADDR_UNDEF              (-1)
+#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
+
+#define VGIC_DIST_SIZE               0x1000
+#define VGIC_CPU_SIZE                0x2000

These defines might be useful to userspace so that they don't request the distributor and the cpu interface to be place too close together (been there, done that :).

Quoted 11 lines
arch/arm/kvm/vgic.c +4 -0
+
+
 #define ACCESS_READ_VALUE    (1 << 0)
 #define ACCESS_READ_RAZ              (0 << 0)
 #define ACCESS_READ_MASK(x)  ((x) & (1 << 0))
@@ -136,3 +143,57 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 {
      return KVM_EXIT_MMIO;
 }
+
+static bool vgic_ioaddr_overlap(struct kvm *kvm)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 157

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+{
+     phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
+     phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
+
+     if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
+             return false;
+     if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
+         (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
+             return true;
+     return false;

Just return the predicate that you're testing.

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+}
+
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+     int r = 0;
+     struct vgic_dist *vgic = &kvm->arch.vgic;
+
+     if (addr & ~KVM_PHYS_MASK)
+             return -E2BIG;
+

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 177

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+     if (addr & ~PAGE_MASK)
+             return -EINVAL;
+
+     mutex_lock(&kvm->lock);
+     switch (type) {
+     case KVM_VGIC_V2_ADDR_TYPE_DIST:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+                     return -EEXIST;
+             if (addr + VGIC_DIST_SIZE < addr)
+                     return -EINVAL;

I think somebody else pointed out the missing mutex_unlocks on the failure paths.

Quoted 7 lines
arch/arm/kvm/vgic.c +7 -0
+             kvm->arch.vgic.vgic_dist_base = addr;
+             break;
+     case KVM_VGIC_V2_ADDR_TYPE_CPU:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+                     return -EEXIST;
+             if (addr + VGIC_CPU_SIZE < addr)
+                     return -EINVAL;

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 194

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+             kvm->arch.vgic.vgic_cpu_base = addr;
+             break;
+     default:
+             r = -ENODEV;
+     }
+
+     if (vgic_ioaddr_overlap(kvm)) {
+             kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+             kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+             return -EINVAL;
Quoted 3 lines

Perhaps we could put all the address checking in one place, so that the wrapping round zero checks and the overlap checks can be in the same function?

Like this (?):

(quoted diff) +0 -0

b/Documentation/virtual/kvm/api.txt
index 7f057a2..0b6b95e 100644

Documentation/virtual/kvm/api.txt +1 -0
@@ -2163,6 +2163,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_device_address {
 	__u64 id;
arch/arm/kvm/vgic.c +20 -3
index f697c14..660fe24 100644
@@ -1230,11 +1230,28 @@ static bool vgic_ioaddr_overlap(struct kvm *kvm)
 	phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
 
 	if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
-		return false;
+		return 0;
 	if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
 	    (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
-		return true;
-	return false;
+		return -EBUSY;
+	return 0;
+}
+
+static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
+			      phys_addr_t addr, phys_addr_t size)
+{
+	int ret;
+
+	if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
+		return -EEXIST;
+	if (addr + size < addr)
+		return -EINVAL;
+
+	ret = vgic_ioaddr_overlap(kvm);
+	if (ret)
+		return ret;
+	*ioaddr = addr;
+	return ret;
 }
 
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
@@ -1251,29 +1268,21 @@ int kvm_vgic_set_addr(struct kvm *kvm,

unsigned long type, u64 addr)
mutex_lock(&kvm->lock);
switch (type) {
case KVM_VGIC_V2_ADDR_TYPE_DIST:
- if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
- return -EEXIST;
- if (addr + VGIC_DIST_SIZE < addr)
- return -EINVAL;
- kvm->arch.vgic.vgic_dist_base = addr;
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,
+ addr, VGIC_DIST_SIZE);
break;
case KVM_VGIC_V2_ADDR_TYPE_CPU:
if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
return -EEXIST;
if (addr + VGIC_CPU_SIZE < addr)
return -EINVAL;
- kvm->arch.vgic.vgic_cpu_base = addr;
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
+ addr, VGIC_CPU_SIZE);
break;
default:
r = -ENODEV;
}

- if (vgic_ioaddr_overlap(kvm)) {
- kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
- kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
- r = -EINVAL;
- }
-
mutex_unlock(&kvm->lock);
return r;
}
--

Thanks,
-Christoffer

#22 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

Quoted 184 lines · Christoffer Dall
Quoted 183 lines · Will Deacon
Quoted 19 lines · 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.

---
 arch/arm/include/asm/kvm_mmu.h  |    2 +
 arch/arm/include/asm/kvm_vgic.h |    9 ++++++
 arch/arm/kvm/arm.c              |   16 ++++++++++
 arch/arm/kvm/vgic.c             |   61 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_mmu.h +0 -0
index 9bd0508..0800531 100644

arch/arm/include/asm/kvm_mmu.h line 29

Quoted 4 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -26,6 +26,8 @@
  * To save a bit of memory and to avoid alignment issues we assume 39-bit IPA
  * for now, but remember that the level-1 table must be aligned to its size.
  */
+#define KVM_PHYS_SHIFT       (38)

Seems a bit small...

Quoted 77 lines
arch/arm/include/asm/kvm_mmu.h +1 -0
+#define KVM_PHYS_MASK        ((1ULL << KVM_PHYS_SHIFT) - 1)
 #define PTRS_PER_PGD2        512
 #define PGD2_ORDER   get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
arch/arm/include/asm/kvm_vgic.h +9 -0
index b444ecf..9ca8d21 100644
@@ -20,6 +20,9 @@
 #define __ASM_ARM_KVM_VGIC_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 {
@@ -31,6 +34,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);
 
@@ -40,6 +44,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/kvm/arm.c +15 -1
index 426828a..3ac1aab 100644
@@ -61,6 +61,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());
@@ -825,7 +827,19 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 static int kvm_vm_ioctl_set_device_address(struct kvm *kvm,
                                         struct kvm_device_address *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 +0 -0
index 26ada3b..f85b275 100644

arch/arm/kvm/vgic.c line 29

Quoted 8 lines
arch/arm/kvm/vgic.c +5 -0
@@ -22,6 +22,13 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
+#define VGIC_ADDR_UNDEF              (-1)
+#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
+
+#define VGIC_DIST_SIZE               0x1000
+#define VGIC_CPU_SIZE                0x2000

These defines might be useful to userspace so that they don't request the distributor and the cpu interface to be place too close together (been there, done that :).

Quoted 11 lines
arch/arm/kvm/vgic.c +4 -0
+
+
 #define ACCESS_READ_VALUE    (1 << 0)
 #define ACCESS_READ_RAZ              (0 << 0)
 #define ACCESS_READ_MASK(x)  ((x) & (1 << 0))
@@ -136,3 +143,57 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 {
      return KVM_EXIT_MMIO;
 }
+
+static bool vgic_ioaddr_overlap(struct kvm *kvm)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 157

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+{
+     phys_addr_t dist = kvm->arch.vgic.vgic_dist_base;
+     phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
+
+     if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
+             return false;
+     if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
+         (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
+             return true;
+     return false;

Just return the predicate that you're testing.

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+}
+
+int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
+{
+     int r = 0;
+     struct vgic_dist *vgic = &kvm->arch.vgic;
+
+     if (addr & ~KVM_PHYS_MASK)
+             return -E2BIG;
+

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 177

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+     if (addr & ~PAGE_MASK)
+             return -EINVAL;
+
+     mutex_lock(&kvm->lock);
+     switch (type) {
+     case KVM_VGIC_V2_ADDR_TYPE_DIST:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
+                     return -EEXIST;
+             if (addr + VGIC_DIST_SIZE < addr)
+                     return -EINVAL;

I think somebody else pointed out the missing mutex_unlocks on the failure paths.

Quoted 17 lines
arch/arm/kvm/vgic.c +17 -0
+             kvm->arch.vgic.vgic_dist_base = addr;
+             break;
+     case KVM_VGIC_V2_ADDR_TYPE_CPU:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+                     return -EEXIST;
+             if (addr + VGIC_CPU_SIZE < addr)
+                     return -EINVAL;
+             kvm->arch.vgic.vgic_cpu_base = addr;
+             break;
+     default:
+             r = -ENODEV;
+     }
+
+     if (vgic_ioaddr_overlap(kvm)) {
+             kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+             kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+             return -EINVAL;
Quoted 6 lines
Quoted 3 lines

Perhaps we could put all the address checking in one place, so that the wrapping round zero checks and the overlap checks can be in the same function?

Like this (?):

and by this, I mean this:

(quoted diff) +0 -0

b/Documentation/virtual/kvm/api.txt
index 7f057a2..0b6b95e 100644

Documentation/virtual/kvm/api.txt +1 -0
@@ -2163,6 +2163,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_device_address {
 	__u64 id;
arch/arm/kvm/vgic.c +20 -3
index f697c14..c666b95 100644
@@ -1230,11 +1230,28 @@ static bool vgic_ioaddr_overlap(struct kvm *kvm)
 	phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
 
 	if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
-		return false;
+		return 0;
 	if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
 	    (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
-		return true;
-	return false;
+		return -EBUSY;
+	return 0;
+}
+
+static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
+			      phys_addr_t addr, phys_addr_t size)
+{
+	int ret;
+
+	if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
+		return -EEXIST;
+	if (addr + size < addr)
+		return -EINVAL;
+
+	ret = vgic_ioaddr_overlap(kvm);
+	if (ret)
+		return ret;
+	*ioaddr = addr;
+	return ret;
 }
 
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
@@ -1251,29 +1268,17 @@ int kvm_vgic_set_addr(struct kvm *kvm,

unsigned long type, u64 addr)
mutex_lock(&kvm->lock);
switch (type) {
case KVM_VGIC_V2_ADDR_TYPE_DIST:
- if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
- return -EEXIST;
- if (addr + VGIC_DIST_SIZE < addr)
- return -EINVAL;
- kvm->arch.vgic.vgic_dist_base = addr;
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,
+ addr, VGIC_DIST_SIZE);
break;
case KVM_VGIC_V2_ADDR_TYPE_CPU:
- if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
- return -EEXIST;
- if (addr + VGIC_CPU_SIZE < addr)
- return -EINVAL;
- kvm->arch.vgic.vgic_cpu_base = addr;
+ r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
+ addr, VGIC_CPU_SIZE);
break;
default:
r = -ENODEV;
}

- if (vgic_ioaddr_overlap(kvm)) {
- kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
- kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
- r = -EINVAL;
- }
-
mutex_unlock(&kvm->lock);
return r;
}
--

Thanks,
-Christoffer

#23 lore raw

Re: [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space

In reply to Christoffer Dall
Quoted 20 lines · Christoffer Dall
Quoted 19 lines · Will Deacon
Quoted 17 lines · Christoffer Dall
(quoted diff) +17 -0
+             kvm->arch.vgic.vgic_dist_base = addr;
+             break;
+     case KVM_VGIC_V2_ADDR_TYPE_CPU:
+             if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
+                     return -EEXIST;
+             if (addr + VGIC_CPU_SIZE < addr)
+                     return -EINVAL;
+             kvm->arch.vgic.vgic_cpu_base = addr;
+             break;
+     default:
+             r = -ENODEV;
+     }
+
+     if (vgic_ioaddr_overlap(kvm)) {
+             kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+             kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+             return -EINVAL;
Quoted 5 lines
Quoted 3 lines

Perhaps we could put all the address checking in one place, so that the wrapping round zero checks and the overlap checks can be in the same function?

Like this (?):

Almost:

Quoted 60 lines
Documentation/virtual/kvm/api.txt +1 -0
index 7f057a2..0b6b95e 100644
@@ -2163,6 +2163,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_device_address {
 	__u64 id;
arch/arm/kvm/vgic.c +21 -8
index f697c14..660fe24 100644
@@ -1230,11 +1230,28 @@ static bool vgic_ioaddr_overlap(struct kvm *kvm)
 	phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base;
 
 	if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu))
-		return false;
+		return 0;
 	if ((dist <= cpu && dist + VGIC_DIST_SIZE > cpu) ||
 	    (cpu <= dist && cpu + VGIC_CPU_SIZE > dist))
-		return true;
-	return false;
+		return -EBUSY;
+	return 0;
+}
+
+static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr,
+			      phys_addr_t addr, phys_addr_t size)
+{
+	int ret;
+
+	if (!IS_VGIC_ADDR_UNDEF(*ioaddr))
+		return -EEXIST;
+	if (addr + size < addr)
+		return -EINVAL;
+
+	ret = vgic_ioaddr_overlap(kvm);
+	if (ret)
+		return ret;
+	*ioaddr = addr;
+	return ret;
 }
 
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
@@ -1251,29 +1268,21 @@ int kvm_vgic_set_addr(struct kvm *kvm,
 unsigned long type, u64 addr)
 	mutex_lock(&kvm->lock);
 	switch (type) {
 	case KVM_VGIC_V2_ADDR_TYPE_DIST:
-		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
-			return -EEXIST;
-		if (addr + VGIC_DIST_SIZE < addr)
-			return -EINVAL;
-		kvm->arch.vgic.vgic_dist_base = addr;
+		r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base,

arch/arm/kvm/vgic.c int kvm_vgic_set_addr(struct kvm *kvm, · line 1281

Quoted 10 lines
arch/arm/kvm/vgic.c +3 -1
+				       addr, VGIC_DIST_SIZE);
 		break;
 	case KVM_VGIC_V2_ADDR_TYPE_CPU:
 		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
 			return -EEXIST;
 		if (addr + VGIC_CPU_SIZE < addr)
 			return -EINVAL;
-		kvm->arch.vgic.vgic_cpu_base = addr;
+		r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base,
+				       addr, VGIC_CPU_SIZE);

You've left the checking in here, so now everything is checked twice.

Also, you should probably touch bases with Marc as I'm under the impression that both of you are looking at addressing my comments so it would be good to avoid tripping over each other on this.

Will

#24 lore raw

PATCH v4 6/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 |  167 ++++++++++++++
 arch/arm/kvm/vgic.c             |  471 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 637 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +167 -0
index 9ca8d21..9e60b1d 100644
@@ -19,10 +19,177 @@
 #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>
+
+#define VGIC_NR_IRQS		128
+#define VGIC_NR_SHARED_IRQS	(VGIC_NR_IRQS - 32)
+#define VGIC_MAX_CPUS		NR_CPUS
+
+/* 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 interrups (SPI)
+ */
+struct vgic_bitmap {
+	union {
+		u32 reg[1];
+		unsigned long reg_ul[0];
+	} percpu[VGIC_MAX_CPUS];
+	union {
+		u32 reg[VGIC_NR_SHARED_IRQS / 32];
+		unsigned long reg_ul[0];
+	} shared;
+};
+
+static inline u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
+				       int cpuid, u32 offset)
+{
+	offset >>= 2;
+	BUG_ON(offset > (VGIC_NR_IRQS / 32));
+	if (!offset)
+		return x->percpu[cpuid].reg;
+	else
+		return x->shared.reg + offset - 1;
+}
+
+static inline int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
+					 int cpuid, int irq)
+{
+	if (irq < 32)
+		return test_bit(irq, x->percpu[cpuid].reg_ul);
+
+	return test_bit(irq - 32, x->shared.reg_ul);
+}
+
+static inline void vgic_bitmap_set_irq_val(struct vgic_bitmap *x,
+					   int cpuid, int irq, int val)
+{
+	unsigned long *reg;
+
+	if (irq < 32)
+		reg = x->percpu[cpuid].reg_ul;
+	else {
+		reg =  x->shared.reg_ul;
+		irq -= 32;
+	}
+
+	if (val)
+		set_bit(irq, reg);
+	else
+		clear_bit(irq, reg);
+}
+
+static inline unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x,
+						     int cpuid)
+{
+	if (unlikely(cpuid >= VGIC_MAX_CPUS))
+		return NULL;
+	return x->percpu[cpuid].reg_ul;
+}
+
+static inline unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
+{
+	return x->shared.reg_ul;
+}
+
+struct vgic_bytemap {
+	union {
+		u32 reg[8];
+		unsigned long reg_ul[0];
+	} percpu[VGIC_MAX_CPUS];
+	union {
+		u32 reg[VGIC_NR_SHARED_IRQS  / 4];
+		unsigned long reg_ul[0];
+	} shared;
+};
+
+static inline u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x,
+					int cpuid, u32 offset)
+{
+	offset >>= 2;
+	BUG_ON(offset > (VGIC_NR_IRQS / 4));
+	if (offset < 4)
+		return x->percpu[cpuid].reg + offset;
+	else
+		return x->shared.reg + offset - 8;
+}
+
+static inline int vgic_bytemap_get_irq_val(struct vgic_bytemap *x,
+					   int cpuid, int irq)
+{
+	u32 *reg, shift;
+	shift = (irq & 3) * 8;
+	reg = vgic_bytemap_get_reg(x, cpuid, irq);
+	return (*reg >> shift) & 0xff;
+}
+
+static inline void vgic_bytemap_set_irq_val(struct vgic_bytemap *x,
+					    int cpuid, int irq, int val)
+{
+	u32 *reg, shift;
+	shift = (irq & 3) * 8;
+	reg = vgic_bytemap_get_reg(x, cpuid, irq);
+	*reg &= ~(0xff << shift);
+	*reg |= (val & 0xff) << shift;
+}
+
 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][16];
+
+	/* 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 +470 -1

501 lines not on this page · raw message

#25 lore raw

Re: [PATCH v4 06/13] ARM: KVM: VGIC distributor handling

On Sat, Nov 10, 2012 at 04:44:58PM +0100, Christoffer Dall wrote:
[...]

Quoted 14 lines
arch/arm/kvm/vgic.c +9 -1
@@ -141,7 +519,98 @@ struct mmio_range *find_matching_range(const struct mmio_range *ranges,
  */
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
 {
-	return KVM_EXIT_MMIO;
+	const struct mmio_range *range;
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	unsigned long base = dist->vgic_dist_base;
+	bool updated_state;
+
+	if (!irqchip_in_kernel(vcpu->kvm) ||
+	    mmio->phys_addr < base ||
+	    (mmio->phys_addr + mmio->len) > (base + dist->vgic_dist_size))
+		return false;

arch/arm/kvm/vgic.c struct mmio_range *find_matching_range(const struct mmio_range *ranges, · line 540

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+
+	range = find_matching_range(vgic_ranges, mmio, base);
+	if (unlikely(!range || !range->handle_mmio)) {
+		pr_warn("Unhandled access %d %08llx %d\n",
+			mmio->is_write, mmio->phys_addr, mmio->len);
+		return false;
+	}
+
+	spin_lock(&vcpu->kvm->arch.vgic.lock);
+	updated_state = range->handle_mmio(vcpu, mmio,mmio->phys_addr - range->base - base);

Missing space after ','.
Checkpatch may fail here.

Regards
Dong Aisheng

#26 lore raw

Re: [PATCH v4 06/13] ARM: KVM: VGIC distributor handling

Quoted 20 lines · Dong Aisheng

On Sat, Nov 10, 2012 at 04:44:58PM +0100, Christoffer Dall wrote:
[...]

Quoted 18 lines
arch/arm/kvm/vgic.c +13 -1
@@ -141,7 +519,98 @@ struct mmio_range *find_matching_range(const struct mmio_range *ranges,
  */
 bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
 {
-     return KVM_EXIT_MMIO;
+     const struct mmio_range *range;
+     struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+     unsigned long base = dist->vgic_dist_base;
+     bool updated_state;
+
+     if (!irqchip_in_kernel(vcpu->kvm) ||
+         mmio->phys_addr < base ||
+         (mmio->phys_addr + mmio->len) > (base + dist->vgic_dist_size))
+             return false;
+
+     range = find_matching_range(vgic_ranges, mmio, base);
+     if (unlikely(!range || !range->handle_mmio)) {
+             pr_warn("Unhandled access %d %08llx %d\n",
Quoted 9 lines

arch/arm/kvm/vgic.c struct mmio_range *find_matching_range(const struct mmio_range *ranges, · line 540

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+                     mmio->is_write, mmio->phys_addr, mmio->len);
+             return false;
+     }
+
+     spin_lock(&vcpu->kvm->arch.vgic.lock);
+     updated_state = range->handle_mmio(vcpu, mmio,mmio->phys_addr - range->base - base);

Missing space after ','.
Checkpatch may fail here.

thanks,
-Christoffer

#27 lore raw

Re: [PATCH v4 06/13] ARM: KVM: VGIC distributor handling

In reply to Christoffer Dall
Quoted 18 lines · 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 |  167 ++++++++++++++
 arch/arm/kvm/vgic.c             |  471 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 637 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +0 -0
index 9ca8d21..9e60b1d 100644
@@ -19,10 +19,177 @@
 #ifndef __ASM_ARM_KVM_VGIC_H

arch/arm/include/asm/kvm_vgic.h line 29

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +8 -0
 #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>
+
+#define VGIC_NR_IRQS           128

#define VGIC_NR_PRIVATE_IRQS 32?

arch/arm/include/asm/kvm_vgic.h line 30

Quoted 1 line
arch/arm/include/asm/kvm_vgic.h +1 -0
+#define VGIC_NR_SHARED_IRQS    (VGIC_NR_IRQS - 32)

then subtract it here

arch/arm/include/asm/kvm_vgic.h line 31

Quoted 1 line
arch/arm/include/asm/kvm_vgic.h +1 -0
+#define VGIC_MAX_CPUS          NR_CPUS

We already have KVM_MAX_VCPUS, why do we need another?

arch/arm/include/asm/kvm_vgic.h line 44

Quoted 13 lines
arch/arm/include/asm/kvm_vgic.h +13 -0
+
+/* 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

Maybe put each check directly below the #define being tested, to make it super-obvious to people thinking of changing the constants?

arch/arm/include/asm/kvm_vgic.h line 49

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+/*
+ * The GIC distributor registers describing interrupts have two parts:
+ * - 32 per-CPU interrupts (SGI + PPI)
+ * - a bunch of shared interrups (SPI)

interrupts

arch/arm/include/asm/kvm_vgic.h line 60

Quoted 11 lines
arch/arm/include/asm/kvm_vgic.h +11 -0
+ */
+struct vgic_bitmap {
+       union {
+               u32 reg[1];
+               unsigned long reg_ul[0];
+       } percpu[VGIC_MAX_CPUS];
+       union {
+               u32 reg[VGIC_NR_SHARED_IRQS / 32];
+               unsigned long reg_ul[0];
+       } shared;
+};

Whoa, this is nasty!

Firstly, let's replace the `32' with sizeof(u32) for fun. Secondly, can we make the reg_ul arrays sized using the BITS_TO_LONGS macro?

arch/arm/include/asm/kvm_vgic.h line 66

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
+static inline u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
+                                      int cpuid, u32 offset)
+{
+       offset >>= 2;
+       BUG_ON(offset > (VGIC_NR_IRQS / 32));

Hmm, where is offset sanity-checked before here? Do you just rely on all trapped accesses being valid?

arch/arm/include/asm/kvm_vgic.h line 70

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+       if (!offset)
+               return x->percpu[cpuid].reg;
+       else
+               return x->shared.reg + offset - 1;

An alternative to this would be to have a single array, with the per-cpu interrupts all laid out at the start and a macro to convert an offset to an index. Might make the code more readable and the struct definition more concise.

arch/arm/include/asm/kvm_vgic.h line 76

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+}
+
+static inline int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
+                                        int cpuid, int irq)
+{
+       if (irq < 32)

VGIC_NR_PRIVATE_IRQS (inless you go with the suggestion above)

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+               return test_bit(irq, x->percpu[cpuid].reg_ul);
+
+       return test_bit(irq - 32, x->shared.reg_ul);
+}
+
+static inline void vgic_bitmap_set_irq_val(struct vgic_bitmap *x,

arch/arm/include/asm/kvm_vgic.h line 92

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+                                          int cpuid, int irq, int val)
+{
+       unsigned long *reg;
+
+       if (irq < 32)
+               reg = x->percpu[cpuid].reg_ul;
+       else {
+               reg =  x->shared.reg_ul;
+               irq -= 32;
+       }

Likewise.

Quoted 20 lines
arch/arm/include/asm/kvm_vgic.h +20 -0
+
+       if (val)
+               set_bit(irq, reg);
+       else
+               clear_bit(irq, reg);
+}
+
+static inline unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x,
+                                                    int cpuid)
+{
+       if (unlikely(cpuid >= VGIC_MAX_CPUS))
+               return NULL;
+       return x->percpu[cpuid].reg_ul;
+}
+
+static inline unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
+{
+       return x->shared.reg_ul;
+}
+

arch/arm/include/asm/kvm_vgic.h line 122

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+struct vgic_bytemap {
+       union {
+               u32 reg[8];
+               unsigned long reg_ul[0];
+       } percpu[VGIC_MAX_CPUS];
+       union {
+               u32 reg[VGIC_NR_SHARED_IRQS  / 4];
+               unsigned long reg_ul[0];
+       } shared;
+};

Argh, it's another one! :)

Quoted 46 lines
arch/arm/include/asm/kvm_vgic.h +42 -0
+
+static inline u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x,
+                                       int cpuid, u32 offset)
+{
+       offset >>= 2;
+       BUG_ON(offset > (VGIC_NR_IRQS / 4));
+       if (offset < 4)
+               return x->percpu[cpuid].reg + offset;
+       else
+               return x->shared.reg + offset - 8;
+}
+
+static inline int vgic_bytemap_get_irq_val(struct vgic_bytemap *x,
+                                          int cpuid, int irq)
+{
+       u32 *reg, shift;
+       shift = (irq & 3) * 8;
+       reg = vgic_bytemap_get_reg(x, cpuid, irq);
+       return (*reg >> shift) & 0xff;
+}
+
+static inline void vgic_bytemap_set_irq_val(struct vgic_bytemap *x,
+                                           int cpuid, int irq, int val)
+{
+       u32 *reg, shift;
+       shift = (irq & 3) * 8;
+       reg = vgic_bytemap_get_reg(x, cpuid, irq);
+       *reg &= ~(0xff << shift);
+       *reg |= (val & 0xff) << shift;
+}
+
 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) */

arch/arm/include/asm/kvm_vgic.h line 178

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+       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;

What would the bitmap component of the bytemap represent for priorities?

arch/arm/include/asm/kvm_vgic.h line 184

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
+       /* Level/edge triggered */
+       struct vgic_bitmap      irq_cfg;
+
+       /* Source CPU per SGI and target CPU */
+       u8                      irq_sgi_sources[VGIC_MAX_CPUS][16];

Ah, I guess my VGIC_NR_PRIVATE_IRQS interrupt should be further divided...

Quoted 58 lines
arch/arm/include/asm/kvm_vgic.h +7 -0
+       /* 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 +36 -0
index f85b275..82feee8 100644
@@ -22,6 +22,42 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
+/*
+ * How the whole thing works (courtesy of Christoffer Dall):
+ *
+ * - At any time, the dist->irq_pending_on_cpu is the oracle that knows if
+ *   something is pending
+ * - VGIC pending interrupts are stored on the vgic.irq_state vgic
+ *   bitmap (this bitmap is updated by both user land ioctls and guest
+ *   mmio ops) and indicate the 'wire' state.
+ * - Every time the bitmap changes, the irq_pending_on_cpu oracle is
+ *   recalculated
+ * - To calculate the oracle, we need info for each cpu from
+ *   compute_pending_for_cpu, which considers:
+ *   - PPI: dist->irq_state & dist->irq_enable
+ *   - SPI: dist->irq_state & dist->irq_enable & dist->irq_spi_target
+ *   - irq_spi_target is a 'formatted' version of the GICD_ICFGR
+ *     registers, stored on each vcpu. We only keep one bit of
+ *     information per interrupt, making sure that only one vcpu can
+ *     accept the interrupt.
+ * - The same is true when injecting an interrupt, except that we only
+ *   consider a single interrupt at a time. The irq_spi_cpu array
+ *   contains the target CPU for each SPI.
+ *
+ * The handling of level interrupts adds some extra complexity. We
+ * need to track when the interrupt has been EOIed, so we can sample
+ * the 'line' again. This is achieved as such:
+ *
+ * - When a level interrupt is moved onto a vcpu, the corresponding
+ *   bit in irq_active is set. As long as this bit is set, the line
+ *   will be ignored for further interrupts. The interrupt is injected
+ *   into the vcpu with the VGIC_LR_EOI bit set (generate a
+ *   maintenance interrupt on EOI).
+ * - When the interrupt is EOIed, the maintenance interrupt fires,
+ *   and clears the corresponding bit in irq_active. This allow the
+ *   interrupt line to be sampled again.
+ */
+
 #define VGIC_ADDR_UNDEF                (-1)
 #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
 

arch/arm/kvm/vgic.c line 83

Quoted 10 lines
arch/arm/kvm/vgic.c +7 -0
@@ -38,6 +74,14 @@
 #define ACCESS_WRITE_VALUE     (3 << 1)
 #define ACCESS_WRITE_MASK(x)   ((x) & (3 << 1))
 
+static void vgic_update_state(struct kvm *kvm);
+static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
+
+static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
+{
+       return vgic_bitmap_get_irq_val(&dist->irq_cfg, 0, irq);
+}

so vgic_bitmap_get_irq_val returns 0 for level and anything else for edge? Maybe an enum or something could make this clearer? Also, why not take a vcpu or cpuid parameter to pass through, rather than assuming 0?

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 152

Quoted 13 lines
arch/arm/kvm/vgic.c +6 -0
+
 /**
  * vgic_reg_access - access vgic register
  * @mmio:   pointer to the data describing the mmio access
@@ -101,6 +145,280 @@ static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
        }
 }
 
+static bool handle_mmio_misc(struct kvm_vcpu *vcpu,
+                            struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+       u32 u32off = offset & 3;

u32off? Bitten by a regex perhaps?

Quoted 99 lines
arch/arm/kvm/vgic.c +99 -0
+
+       switch (offset & ~3) {
+       case 0:                 /* CTLR */
+               reg = vcpu->kvm->arch.vgic.enabled;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+               if (mmio->is_write) {
+                       vcpu->kvm->arch.vgic.enabled = reg & 1;
+                       vgic_update_state(vcpu->kvm);
+                       return true;
+               }
+               break;
+
+       case 4:                 /* TYPER */
+               reg  = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
+               reg |= (VGIC_NR_IRQS >> 5) - 1;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               break;
+
+       case 8:                 /* IIDR */
+               reg = 0x4B00043B;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               break;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu,
+                              struct kvm_exit_mmio *mmio, u32 offset)
+{
+       vgic_reg_access(mmio, NULL, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED);
+       return false;
+}
+
+static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu,
+                                      struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
+                                        struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
+       if (mmio->is_write) {
+               if (offset < 4) /* Force SGI enabled */
+                       *reg |= 0xffff;
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
+                                       struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
+                                         struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 261

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+
+static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu,
+                                    struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bytemap_get_reg(&vcpu->kvm->arch.vgic.irq_priority,
+                                       vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       return false;
+}

What do you gain from returning a bool from the MMIO handlers? Why not assume that state has always been updated and kick the vcpus if something is pending?

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 272

Quoted 11 lines
arch/arm/kvm/vgic.c +11 -0
+
+static u32 vgic_get_target_reg(struct kvm *kvm, int irq)
+{
+       struct vgic_dist *dist = &kvm->arch.vgic;
+       struct kvm_vcpu *vcpu;
+       int i, c;
+       unsigned long *bmap;
+       u32 val = 0;
+
+       BUG_ON(irq & 3);
+       BUG_ON(irq < 32);

Again, these look scary because I can't see the offset sanity checking for the MMIO traps...

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 278

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+       irq -= 32;
+
+       kvm_for_each_vcpu(c, vcpu, kvm) {
+               bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]);
+               for (i = 0; i < 4; i++)

Is that 4 from sizeof(unsigned long)?

Quoted 17 lines
arch/arm/kvm/vgic.c +17 -0
+                       if (test_bit(irq + i, bmap))
+                               val |= 1 << (c + i * 8);
+       }
+
+       return val;
+}
+
+static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq)
+{
+       struct vgic_dist *dist = &kvm->arch.vgic;
+       struct kvm_vcpu *vcpu;
+       int i, c;
+       unsigned long *bmap;
+       u32 target;
+
+       BUG_ON(irq & 3);
+       BUG_ON(irq < 32);

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 305

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+
+       irq -= 32;
+
+       /*
+        * Pick the LSB in each byte. This ensures we target exactly
+        * one vcpu per IRQ. If the byte is null, assume we target
+        * CPU0.
+        */
+       for (i = 0; i < 4; i++) {
+               int shift = i * 8;

Is this from BITS_PER_BYTE?

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 307

Quoted 2 lines
arch/arm/kvm/vgic.c +2 -0
+               target = ffs((val >> shift) & 0xffU);
+               target = target ? (target - 1) : 0;

__ffs?

Quoted 36 lines
arch/arm/kvm/vgic.c +36 -0
+               dist->irq_spi_cpu[irq + i] = target;
+               kvm_for_each_vcpu(c, vcpu, kvm) {
+                       bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]);
+                       if (c == target)
+                               set_bit(irq + i, bmap);
+                       else
+                               clear_bit(irq + i, bmap);
+               }
+       }
+}
+
+static bool handle_mmio_target_reg(struct kvm_vcpu *vcpu,
+                                  struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+
+       /* We treat the banked interrupts targets as read-only */
+       if (offset < 32) {
+               u32 roreg = 1 << vcpu->vcpu_id;
+               roreg |= roreg << 8;
+               roreg |= roreg << 16;
+
+               vgic_reg_access(mmio, &roreg, offset,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               return false;
+       }
+
+       reg = vgic_get_target_reg(vcpu->kvm, offset & ~3U);
+       vgic_reg_access(mmio, &reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               vgic_set_target_reg(vcpu->kvm, reg, offset & ~3U);
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 353

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       return false;
+}
+
+static u32 vgic_cfg_expand(u16 val)
+{
+       u32 res = 0;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               res |= (val >> i) << (2 * i + 1);

Ok, you've lost me on this one but replacing some of the magic numbers with the constants they represent would be much appreciated, please!

Quoted 125 lines
arch/arm/kvm/vgic.c +118 -0
+
+       return res;
+}
+
+static u16 vgic_cfg_compress(u32 val)
+{
+       u16 res = 0;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               res |= (val >> (i * 2 + 1)) << i;
+
+       return res;
+}
+
+/*
+ * The distributor uses 2 bits per IRQ for the CFG register, but the
+ * LSB is always 0. As such, we only keep the upper bit, and use the
+ * two above functions to compress/expand the bits
+ */
+static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
+                               struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 val;
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
+                                      vcpu->vcpu_id, offset >> 1);
+       if (offset & 2)
+               val = *reg >> 16;
+       else
+               val = *reg & 0xffff;
+
+       val = vgic_cfg_expand(val);
+       vgic_reg_access(mmio, &val, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               if (offset < 4) {
+                       *reg = ~0U; /* Force PPIs/SGIs to 1 */
+                       return false;
+               }
+
+               val = vgic_cfg_compress(val);
+               if (offset & 2) {
+                       *reg &= 0xffff;
+                       *reg |= val << 16;
+               } else {
+                       *reg &= 0xffff << 16;
+                       *reg |= val;
+               }
+       }
+
+       return false;
+}
+
+static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
+                               struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+       vgic_reg_access(mmio, &reg, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               vgic_dispatch_sgi(vcpu, reg);
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
 /* All this should be handled by kvm_bus_io_*()... FIXME!!! */
 struct mmio_range {
        unsigned long base;
@@ -110,6 +428,66 @@ struct mmio_range {
 };
 
 static const struct mmio_range vgic_ranges[] = {
+       {                       /* CTRL, TYPER, IIDR */
+               .base           = 0,
+               .len            = 12,
+               .handle_mmio    = handle_mmio_misc,
+       },
+       {                       /* IGROUPRn */
+               .base           = 0x80,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* ISENABLERn */
+               .base           = 0x100,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_set_enable_reg,
+       },
+       {                       /* ICENABLERn */
+               .base           = 0x180,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_clear_enable_reg,
+       },
+       {                       /* ISPENDRn */
+               .base           = 0x200,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_set_pending_reg,
+       },
+       {                       /* ICPENDRn */
+               .base           = 0x280,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_clear_pending_reg,
+       },
+       {                       /* ISACTIVERn */
+               .base           = 0x300,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* ICACTIVERn */
+               .base           = 0x380,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* IPRIORITYRn */
+               .base           = 0x400,
+               .len            = VGIC_NR_IRQS,
+               .handle_mmio    = handle_mmio_priority_reg,
+       },
+       {                       /* ITARGETSRn */
+               .base           = 0x800,
+               .len            = VGIC_NR_IRQS,
+               .handle_mmio    = handle_mmio_target_reg,
+       },

arch/arm/kvm/vgic.c struct mmio_range { · line 490

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       {                       /* ICFGRn */
+               .base           = 0xC00,
+               .len            = VGIC_NR_IRQS / 4,
+               .handle_mmio    = handle_mmio_cfg_reg,
+       },
+       {                       /* SGIRn */
+               .base           = 0xF00,
+               .len            = 4,
+               .handle_mmio    = handle_mmio_sgi_reg,
+       },

Why not #define the offset values for the base fields instead of commenting the literals?

Will

#28 lore raw

Re: [PATCH v4 06/13] ARM: KVM: VGIC distributor handling

Quoted 32 lines · Will Deacon
Quoted 18 lines · 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 |  167 ++++++++++++++
 arch/arm/kvm/vgic.c             |  471 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 637 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +0 -0
index 9ca8d21..9e60b1d 100644
@@ -19,10 +19,177 @@
 #ifndef __ASM_ARM_KVM_VGIC_H

arch/arm/include/asm/kvm_vgic.h line 29

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +8 -0
 #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>
+
+#define VGIC_NR_IRQS           128

#define VGIC_NR_PRIVATE_IRQS 32?

Quoted 3 lines

arch/arm/include/asm/kvm_vgic.h line 30

Quoted 1 line
arch/arm/include/asm/kvm_vgic.h +1 -0
+#define VGIC_NR_SHARED_IRQS    (VGIC_NR_IRQS - 32)

then subtract it here

Sure.

Quoted 3 lines

arch/arm/include/asm/kvm_vgic.h line 31

Quoted 1 line
arch/arm/include/asm/kvm_vgic.h +1 -0
+#define VGIC_MAX_CPUS          NR_CPUS

We already have KVM_MAX_VCPUS, why do we need another?

They really should be the same, and this NR_CPUS is a bug that has already been fixed.

Quoted 8 lines
Quoted 8 lines
arch/arm/include/asm/kvm_vgic.h +8 -0
+
+/* 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"
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h line 44

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +5 -0
+#endif
+
+#if (VGIC_NR_IRQS > 1024)
+#error "VGIC_NR_IRQS must be <= 1024"
+#endif

Maybe put each check directly below the #define being tested, to make it super-obvious to people thinking of changing the constants?

OK.

Quoted 21 lines

arch/arm/include/asm/kvm_vgic.h line 49

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+/*
+ * The GIC distributor registers describing interrupts have two parts:
+ * - 32 per-CPU interrupts (SGI + PPI)
+ * - a bunch of shared interrups (SPI)

interrupts

arch/arm/include/asm/kvm_vgic.h line 60

Quoted 11 lines
arch/arm/include/asm/kvm_vgic.h +11 -0
+ */
+struct vgic_bitmap {
+       union {
+               u32 reg[1];
+               unsigned long reg_ul[0];
+       } percpu[VGIC_MAX_CPUS];
+       union {
+               u32 reg[VGIC_NR_SHARED_IRQS / 32];
+               unsigned long reg_ul[0];
+       } shared;
+};

Whoa, this is nasty!

Quoted 2 lines

Firstly, let's replace the `32' with sizeof(u32) for fun. Secondly, can we make the reg_ul arrays sized using the BITS_TO_LONGS macro?

This has already been replaced with:

struct vgic_bitmap {
	union {
		u32 reg[1];
		DECLARE_BITMAP(reg_ul, 32);
	} percpu[VGIC_MAX_CPUS];
	union {
		u32 reg[VGIC_NR_SHARED_IRQS / 32];
		DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS);
	} shared;
};

which should address most of your concerns. As for the sizeof(u32), I think assuming that u32 has a grand total of 32bits is safe enough ;-)

Quoted 9 lines

arch/arm/include/asm/kvm_vgic.h line 66

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
+static inline u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x,
+                                      int cpuid, u32 offset)
+{
+       offset >>= 2;
+       BUG_ON(offset > (VGIC_NR_IRQS / 32));

Hmm, where is offset sanity-checked before here? Do you just rely on all trapped accesses being valid?

You've already validated the access being in a valid range. Offset is just derived the access address. the BUG_ON() is a leftover from early debugging and should go away now.

Quoted 9 lines

arch/arm/include/asm/kvm_vgic.h line 70

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+       if (!offset)
+               return x->percpu[cpuid].reg;
+       else
+               return x->shared.reg + offset - 1;

An alternative to this would be to have a single array, with the per-cpu interrupts all laid out at the start and a macro to convert an offset to an index. Might make the code more readable and the struct definition more concise.

I'll try and see if this makes the code more palatable.

Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h line 76

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+}
+
+static inline int vgic_bitmap_get_irq_val(struct vgic_bitmap *x,
+                                        int cpuid, int irq)
+{
+       if (irq < 32)

VGIC_NR_PRIVATE_IRQS (inless you go with the suggestion above)

yep.

Quoted 43 lines
Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+               return test_bit(irq, x->percpu[cpuid].reg_ul);
+
+       return test_bit(irq - 32, x->shared.reg_ul);
+}
+
+static inline void vgic_bitmap_set_irq_val(struct vgic_bitmap *x,

arch/arm/include/asm/kvm_vgic.h line 92

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+                                          int cpuid, int irq, int val)
+{
+       unsigned long *reg;
+
+       if (irq < 32)
+               reg = x->percpu[cpuid].reg_ul;
+       else {
+               reg =  x->shared.reg_ul;
+               irq -= 32;
+       }

Likewise.

Quoted 24 lines
arch/arm/include/asm/kvm_vgic.h +24 -0
+
+       if (val)
+               set_bit(irq, reg);
+       else
+               clear_bit(irq, reg);
+}
+
+static inline unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x,
+                                                    int cpuid)
+{
+       if (unlikely(cpuid >= VGIC_MAX_CPUS))
+               return NULL;
+       return x->percpu[cpuid].reg_ul;
+}
+
+static inline unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x)
+{
+       return x->shared.reg_ul;
+}
+
+struct vgic_bytemap {
+       union {
+               u32 reg[8];
+               unsigned long reg_ul[0];
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h line 122

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+       } percpu[VGIC_MAX_CPUS];
+       union {
+               u32 reg[VGIC_NR_SHARED_IRQS  / 4];
+               unsigned long reg_ul[0];
+       } shared;
+};

Argh, it's another one! :)

Rest assured it is the last one, and it doesn't get much use either. ;-)

Quoted 50 lines
Quoted 50 lines
arch/arm/include/asm/kvm_vgic.h +46 -0
+
+static inline u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x,
+                                       int cpuid, u32 offset)
+{
+       offset >>= 2;
+       BUG_ON(offset > (VGIC_NR_IRQS / 4));
+       if (offset < 4)
+               return x->percpu[cpuid].reg + offset;
+       else
+               return x->shared.reg + offset - 8;
+}
+
+static inline int vgic_bytemap_get_irq_val(struct vgic_bytemap *x,
+                                          int cpuid, int irq)
+{
+       u32 *reg, shift;
+       shift = (irq & 3) * 8;
+       reg = vgic_bytemap_get_reg(x, cpuid, irq);
+       return (*reg >> shift) & 0xff;
+}
+
+static inline void vgic_bytemap_set_irq_val(struct vgic_bytemap *x,
+                                           int cpuid, int irq, int val)
+{
+       u32 *reg, shift;
+       shift = (irq & 3) * 8;
+       reg = vgic_bytemap_get_reg(x, cpuid, irq);
+       *reg &= ~(0xff << shift);
+       *reg |= (val & 0xff) << shift;
+}
+
 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;
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h line 178

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
+       /* Level-triggered interrupt in progress */
+       struct vgic_bitmap      irq_active;
+
+       /* Interrupt priority. Not used yet. */
+       struct vgic_bytemap     irq_priority;

What would the bitmap component of the bytemap represent for priorities?

Exactly nothing. These fields will die a horrible death in a few seconds ;-)

Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h line 184

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
+       /* Level/edge triggered */
+       struct vgic_bitmap      irq_cfg;
+
+       /* Source CPU per SGI and target CPU */
+       u8                      irq_sgi_sources[VGIC_MAX_CPUS][16];

Ah, I guess my VGIC_NR_PRIVATE_IRQS interrupt should be further divided...

Could do indeed.

Quoted 61 lines
Quoted 61 lines
arch/arm/include/asm/kvm_vgic.h +7 -0
+       /* 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 +36 -0
index f85b275..82feee8 100644
@@ -22,6 +22,42 @@
 #include <linux/io.h>
 #include <asm/kvm_emulate.h>
 
+/*
+ * How the whole thing works (courtesy of Christoffer Dall):
+ *
+ * - At any time, the dist->irq_pending_on_cpu is the oracle that knows if
+ *   something is pending
+ * - VGIC pending interrupts are stored on the vgic.irq_state vgic
+ *   bitmap (this bitmap is updated by both user land ioctls and guest
+ *   mmio ops) and indicate the 'wire' state.
+ * - Every time the bitmap changes, the irq_pending_on_cpu oracle is
+ *   recalculated
+ * - To calculate the oracle, we need info for each cpu from
+ *   compute_pending_for_cpu, which considers:
+ *   - PPI: dist->irq_state & dist->irq_enable
+ *   - SPI: dist->irq_state & dist->irq_enable & dist->irq_spi_target
+ *   - irq_spi_target is a 'formatted' version of the GICD_ICFGR
+ *     registers, stored on each vcpu. We only keep one bit of
+ *     information per interrupt, making sure that only one vcpu can
+ *     accept the interrupt.
+ * - The same is true when injecting an interrupt, except that we only
+ *   consider a single interrupt at a time. The irq_spi_cpu array
+ *   contains the target CPU for each SPI.
+ *
+ * The handling of level interrupts adds some extra complexity. We
+ * need to track when the interrupt has been EOIed, so we can sample
+ * the 'line' again. This is achieved as such:
+ *
+ * - When a level interrupt is moved onto a vcpu, the corresponding
+ *   bit in irq_active is set. As long as this bit is set, the line
+ *   will be ignored for further interrupts. The interrupt is injected
+ *   into the vcpu with the VGIC_LR_EOI bit set (generate a
+ *   maintenance interrupt on EOI).
+ * - When the interrupt is EOIed, the maintenance interrupt fires,
+ *   and clears the corresponding bit in irq_active. This allow the
+ *   interrupt line to be sampled again.
+ */
+
 #define VGIC_ADDR_UNDEF                (-1)
 #define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
 
@@ -38,6 +74,14 @@
 #define ACCESS_WRITE_VALUE     (3 << 1)
 #define ACCESS_WRITE_MASK(x)   ((x) & (3 << 1))
 
Quoted 11 lines

arch/arm/kvm/vgic.c line 83

Quoted 7 lines
arch/arm/kvm/vgic.c +7 -0
+static void vgic_update_state(struct kvm *kvm);
+static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
+
+static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
+{
+       return vgic_bitmap_get_irq_val(&dist->irq_cfg, 0, irq);
+}

so vgic_bitmap_get_irq_val returns 0 for level and anything else for edge? Maybe an enum or something could make this clearer? Also, why not take a vcpu or cpuid parameter to pass through, rather than assuming 0?

Fair enough.

Quoted 8 lines
Quoted 8 lines
arch/arm/kvm/vgic.c +1 -0
+
 /**
  * vgic_reg_access - access vgic register
  * @mmio:   pointer to the data describing the mmio access
@@ -101,6 +145,280 @@ static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
        }
 }
 
Quoted 7 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 152

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+static bool handle_mmio_misc(struct kvm_vcpu *vcpu,
+                            struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+       u32 u32off = offset & 3;

u32off? Bitten by a regex perhaps?

No. It used to be "off", which described the offset in a u32. People complained about the lack of clarity, hence the u32off. word_offset, maybe?

Quoted 104 lines
Quoted 104 lines
arch/arm/kvm/vgic.c +104 -0
+
+       switch (offset & ~3) {
+       case 0:                 /* CTLR */
+               reg = vcpu->kvm->arch.vgic.enabled;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+               if (mmio->is_write) {
+                       vcpu->kvm->arch.vgic.enabled = reg & 1;
+                       vgic_update_state(vcpu->kvm);
+                       return true;
+               }
+               break;
+
+       case 4:                 /* TYPER */
+               reg  = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5;
+               reg |= (VGIC_NR_IRQS >> 5) - 1;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               break;
+
+       case 8:                 /* IIDR */
+               reg = 0x4B00043B;
+               vgic_reg_access(mmio, &reg, u32off,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               break;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu,
+                              struct kvm_exit_mmio *mmio, u32 offset)
+{
+       vgic_reg_access(mmio, NULL, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED);
+       return false;
+}
+
+static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu,
+                                      struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
+                                        struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
+       if (mmio->is_write) {
+               if (offset < 4) /* Force SGI enabled */
+                       *reg |= 0xffff;
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
+                                       struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
+                                         struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state,
+                                      vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
+       if (mmio->is_write) {
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu,
+                                    struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 *reg = vgic_bytemap_get_reg(&vcpu->kvm->arch.vgic.irq_priority,
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 261

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+                                       vcpu->vcpu_id, offset);
+       vgic_reg_access(mmio, reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       return false;
+}

What do you gain from returning a bool from the MMIO handlers? Why not assume that state has always been updated and kick the vcpus if something is pending?

Computing the state for the whole distributor is quite expensive (we used to compute it all at each interrupt injection, and switching to single-nit operations have a measurable difference).

Quoted 6 lines
Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+static u32 vgic_get_target_reg(struct kvm *kvm, int irq)
+{
+       struct vgic_dist *dist = &kvm->arch.vgic;
+       struct kvm_vcpu *vcpu;
+       int i, c;
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 272

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       unsigned long *bmap;
+       u32 val = 0;
+
+       BUG_ON(irq & 3);
+       BUG_ON(irq < 32);

Again, these look scary because I can't see the offset sanity checking for the MMIO traps...

Range checking again.

Quoted 10 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 278

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+       irq -= 32;
+
+       kvm_for_each_vcpu(c, vcpu, kvm) {
+               bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]);
+               for (i = 0; i < 4; i++)

Is that 4 from sizeof(unsigned long)?

From the definition of the target registers, actually (4 bytes per

register, one byte per interrupt, one CPU per bit).

Quoted 22 lines
Quoted 22 lines
arch/arm/kvm/vgic.c +21 -0
+                       if (test_bit(irq + i, bmap))
+                               val |= 1 << (c + i * 8);
+       }
+
+       return val;
+}
+
+static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq)
+{
+       struct vgic_dist *dist = &kvm->arch.vgic;
+       struct kvm_vcpu *vcpu;
+       int i, c;
+       unsigned long *bmap;
+       u32 target;
+
+       BUG_ON(irq & 3);
+       BUG_ON(irq < 32);
+
+       irq -= 32;
+
+       /*
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 305

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+        * Pick the LSB in each byte. This ensures we target exactly
+        * one vcpu per IRQ. If the byte is null, assume we target
+        * CPU0.
+        */
+       for (i = 0; i < 4; i++) {
+               int shift = i * 8;

Is this from BITS_PER_BYTE?

Good point.

Quoted 4 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 307

Quoted 2 lines
arch/arm/kvm/vgic.c +2 -0
+               target = ffs((val >> shift) & 0xffU);
+               target = target ? (target - 1) : 0;

__ffs?

Does this look better?

	target = (val >> shift) & 0xffU;
	if (target)
		target = __ffs(target);
Quoted 42 lines
Quoted 42 lines
arch/arm/kvm/vgic.c +41 -0
+               dist->irq_spi_cpu[irq + i] = target;
+               kvm_for_each_vcpu(c, vcpu, kvm) {
+                       bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]);
+                       if (c == target)
+                               set_bit(irq + i, bmap);
+                       else
+                               clear_bit(irq + i, bmap);
+               }
+       }
+}
+
+static bool handle_mmio_target_reg(struct kvm_vcpu *vcpu,
+                                  struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+
+       /* We treat the banked interrupts targets as read-only */
+       if (offset < 32) {
+               u32 roreg = 1 << vcpu->vcpu_id;
+               roreg |= roreg << 8;
+               roreg |= roreg << 16;
+
+               vgic_reg_access(mmio, &roreg, offset,
+                               ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED);
+               return false;
+       }
+
+       reg = vgic_get_target_reg(vcpu->kvm, offset & ~3U);
+       vgic_reg_access(mmio, &reg, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               vgic_set_target_reg(vcpu->kvm, reg, offset & ~3U);
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
+static u32 vgic_cfg_expand(u16 val)
+{
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, · line 353

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       u32 res = 0;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               res |= (val >> i) << (2 * i + 1);

Ok, you've lost me on this one but replacing some of the magic numbers with the constants they represent would be much appreciated, please!

The comment below contains some explanation (but the code is buggy and has been fixed already). I'll try to come up with some detailed explanation about what this code is trying to do.

Quoted 130 lines
Quoted 130 lines
arch/arm/kvm/vgic.c +123 -0
+
+       return res;
+}
+
+static u16 vgic_cfg_compress(u32 val)
+{
+       u16 res = 0;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               res |= (val >> (i * 2 + 1)) << i;
+
+       return res;
+}
+
+/*
+ * The distributor uses 2 bits per IRQ for the CFG register, but the
+ * LSB is always 0. As such, we only keep the upper bit, and use the
+ * two above functions to compress/expand the bits
+ */
+static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
+                               struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 val;
+       u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
+                                      vcpu->vcpu_id, offset >> 1);
+       if (offset & 2)
+               val = *reg >> 16;
+       else
+               val = *reg & 0xffff;
+
+       val = vgic_cfg_expand(val);
+       vgic_reg_access(mmio, &val, offset,
+                       ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               if (offset < 4) {
+                       *reg = ~0U; /* Force PPIs/SGIs to 1 */
+                       return false;
+               }
+
+               val = vgic_cfg_compress(val);
+               if (offset & 2) {
+                       *reg &= 0xffff;
+                       *reg |= val << 16;
+               } else {
+                       *reg &= 0xffff << 16;
+                       *reg |= val;
+               }
+       }
+
+       return false;
+}
+
+static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
+                               struct kvm_exit_mmio *mmio, u32 offset)
+{
+       u32 reg;
+       vgic_reg_access(mmio, &reg, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_VALUE);
+       if (mmio->is_write) {
+               vgic_dispatch_sgi(vcpu, reg);
+               vgic_update_state(vcpu->kvm);
+               return true;
+       }
+
+       return false;
+}
+
 /* All this should be handled by kvm_bus_io_*()... FIXME!!! */
 struct mmio_range {
        unsigned long base;
@@ -110,6 +428,66 @@ struct mmio_range {
 };
 
 static const struct mmio_range vgic_ranges[] = {
+       {                       /* CTRL, TYPER, IIDR */
+               .base           = 0,
+               .len            = 12,
+               .handle_mmio    = handle_mmio_misc,
+       },
+       {                       /* IGROUPRn */
+               .base           = 0x80,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* ISENABLERn */
+               .base           = 0x100,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_set_enable_reg,
+       },
+       {                       /* ICENABLERn */
+               .base           = 0x180,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_clear_enable_reg,
+       },
+       {                       /* ISPENDRn */
+               .base           = 0x200,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_set_pending_reg,
+       },
+       {                       /* ICPENDRn */
+               .base           = 0x280,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_clear_pending_reg,
+       },
+       {                       /* ISACTIVERn */
+               .base           = 0x300,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* ICACTIVERn */
+               .base           = 0x380,
+               .len            = VGIC_NR_IRQS / 8,
+               .handle_mmio    = handle_mmio_raz_wi,
+       },
+       {                       /* IPRIORITYRn */
+               .base           = 0x400,
+               .len            = VGIC_NR_IRQS,
+               .handle_mmio    = handle_mmio_priority_reg,
+       },
+       {                       /* ITARGETSRn */
+               .base           = 0x800,
+               .len            = VGIC_NR_IRQS,
+               .handle_mmio    = handle_mmio_target_reg,
+       },
+       {                       /* ICFGRn */
+               .base           = 0xC00,
+               .len            = VGIC_NR_IRQS / 4,
+               .handle_mmio    = handle_mmio_cfg_reg,
+       },
Quoted 8 lines

arch/arm/kvm/vgic.c struct mmio_range { · line 490

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       {                       /* SGIRn */
+               .base           = 0xF00,
+               .len            = 4,
+               .handle_mmio    = handle_mmio_sgi_reg,
+       },

Why not #define the offset values for the base fields instead of commenting the literals?

I've had a separate discussion with RobH about that, and this is the pipe for the GIC move into drivers.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#29 lore raw

PATCH v4 7/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 |   41 +++++++
 arch/arm/kvm/vgic.c             |  226 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 266 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +41 -0
index 9e60b1d..7229324 100644
@@ -193,8 +193,45 @@ 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, VGIC_NR_IRQS);
+
+	/* Bitmap of used/free list registers */
+	DECLARE_BITMAP(	lr_used, 64);
+
+	/* 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[64];	/* A15 has only 4... */
+#endif
 };
 
+#define VGIC_HCR_EN		(1 << 0)
+#define VGIC_HCR_UIE		(1 << 1)
+
+#define VGIC_LR_VIRTUALID	(0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID	(7 << 10)
+#define VGIC_LR_STATE		(3 << 28)
+#define VGIC_LR_PENDING_BIT	(1 << 28)
+#define VGIC_LR_ACTIVE_BIT	(1 << 29)
+#define VGIC_LR_EOI		(1 << 19)
+
+#define VGIC_MISR_EOI		(1 << 0)
+#define VGIC_MISR_U		(1 << 1)
+
+#define LR_EMPTY	0xff
+
 struct kvm;
 struct kvm_vcpu;
 struct kvm_run;
@@ -202,9 +239,13 @@ 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_sync_to_cpu(struct kvm_vcpu *vcpu);
+void kvm_vgic_sync_from_cpu(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 +225 -1

238 lines not on this page · raw message

#30 lore raw

Re: [PATCH v4 07/13] ARM: KVM: VGIC virtual CPU interface management

Hi Marc,

I've managed to look at some more of the vgic code, so here is some more feedback. I've still not got to the end of the series, but there's light at the end of the tunnel...

Quoted 18 lines · 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 |   41 +++++++
 arch/arm/kvm/vgic.c             |  226 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 266 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +0 -0
index 9e60b1d..7229324 100644

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 198

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +3 -0
@@ -193,8 +193,45 @@ struct vgic_dist {
 };
 
 struct vgic_cpu {
+#ifdef CONFIG_KVM_ARM_VGIC
+       /* per IRQ to LR mapping */
+       u8              vgic_irq_lr_map[VGIC_NR_IRQS];

per IRQ?

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+
+       /* Pending interrupts on this VCPU */
+       DECLARE_BITMAP( pending, VGIC_NR_IRQS);
+
+       /* Bitmap of used/free list registers */
+       DECLARE_BITMAP( lr_used, 64);
+
+       /* Number of list registers on this CPU */
+       int             nr_lr;
+

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 218

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +9 -0
+       /* 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[64];    /* A15 has only 4... */
+#endif
 };

Looks like we should have a #define for the maximum number of list registers, so we keep vgic_lr and lr_user in sync.

Quoted 6 lines
arch/arm/include/asm/kvm_vgic.h +5 -0
+#define VGIC_HCR_EN            (1 << 0)
+#define VGIC_HCR_UIE           (1 << 1)
+
+#define VGIC_LR_VIRTUALID      (0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID   (7 << 10)

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 234

Quoted 10 lines
arch/arm/include/asm/kvm_vgic.h +10 -0
+#define VGIC_LR_STATE          (3 << 28)
+#define VGIC_LR_PENDING_BIT    (1 << 28)
+#define VGIC_LR_ACTIVE_BIT     (1 << 29)
+#define VGIC_LR_EOI            (1 << 19)
+
+#define VGIC_MISR_EOI          (1 << 0)
+#define VGIC_MISR_U            (1 << 1)
+
+#define LR_EMPTY       0xff
+

Could stick these in asm/hardware/gic.h. I know they're not used by the gic driver, but they're the same piece of architecture so it's probably worth keeping in one place.

You'd probably also want a s/VGIC/GICH/

Quoted 26 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
 struct kvm;
 struct kvm_vcpu;
 struct kvm_run;
@@ -202,9 +239,13 @@ 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_sync_to_cpu(struct kvm_vcpu *vcpu);
+void kvm_vgic_sync_from_cpu(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 +0 -1
index 82feee8..d7cdec5 100644
@@ -587,7 +587,25 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 
 static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
 {
-       return 0;

arch/arm/kvm/vgic.c static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) · line 599

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending, *enabled, *pend;
+       int vcpu_id;
+
+       vcpu_id = vcpu->vcpu_id;
+       pend = vcpu->arch.vgic_cpu.pending;
+
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
+       bitmap_and(pend, pending, enabled, 32);

pend and pending! vcpu_pending and dist_pending?

Quoted 13 lines
arch/arm/kvm/vgic.c +9 -0
+
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
+       bitmap_and(pend + 1, pending, enabled, VGIC_NR_SHARED_IRQS);
+       bitmap_and(pend + 1, pend + 1,
+                  vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
+                  VGIC_NR_SHARED_IRQS);
+
+       return (find_first_bit(pend, VGIC_NR_IRQS) < VGIC_NR_IRQS);
 }
 
 /*

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 634

Quoted 4 lines
arch/arm/kvm/vgic.c +1 -0
@@ -613,6 +631,212 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
+#define LR_PHYSID(lr)          (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)

Is VGIC_LR_PHYSID_CPUID wide enough for this? The CPUID is only 3 bits, but the interrupt ID could be larger. Or do you not supported hardware interrupt forwarding? (in which case, LR_PHYSID is a misleading name).

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 647

Quoted 13 lines
arch/arm/kvm/vgic.c +13 -0
+#define MK_LR_PEND(src, irq)   (VGIC_LR_PENDING_BIT | ((src) << 10) | (irq))
+/*
+ * Queue an interrupt to a CPU virtual interface. Return true on success,
+ * or false if it wasn't possible to queue it.
+ */
+static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, is_level;
+
+       /* Sanitize the input... */
+       BUG_ON(sgi_source_id & ~7);

sgi_source_id > MAX_SGI_SOURCES (or whatever we end up having for the SGI and PPI limits).

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 648

Quoted 1 line
arch/arm/kvm/vgic.c +1 -0
+       BUG_ON(sgi_source_id && irq > 15);

irq > MAX_PPI_SOURCES

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 658

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       BUG_ON(irq >= VGIC_NR_IRQS);
+
+       kvm_debug("Queue IRQ%d\n", irq);
+
+       lr = vgic_cpu->vgic_irq_lr_map[irq];
+       is_level = !vgic_irq_is_edge(dist, irq);
+
+       /* Do we have an active interrupt for the same CPUID? */
+       if (lr != LR_EMPTY &&
+           (LR_PHYSID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) {

Ok, so this does return the source.

Quoted 15 lines
arch/arm/kvm/vgic.c +15 -0
+               kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
+               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
+               if (is_level)
+                       vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+               return true;
+       }
+
+       /* Try to use another LR for this interrupt */
+       lr = find_first_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                              vgic_cpu->nr_lr);
+       if (lr >= vgic_cpu->nr_lr)
+               return false;
+
+       kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 683

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
+       if (is_level)
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
+       vgic_cpu->vgic_irq_lr_map[irq] = lr;
+       clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
+       set_bit(lr, vgic_cpu->lr_used);
+
+       return true;
+}

I can't help but feel that this could be made cleaner by moving the level-specific EOI handling out into a separate function.

Quoted 26 lines
arch/arm/kvm/vgic.c +26 -0
+
+/*
+ * Fill the list registers with pending interrupts before running the
+ * guest.
+ */
+static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending;
+       int i, c, vcpu_id;
+       int overflow = 0;
+
+       vcpu_id = vcpu->vcpu_id;
+
+       /*
+        * We may not have any pending interrupt, or the interrupts
+        * may have been serviced from another vcpu. In all cases,
+        * move along.
+        */
+       if (!kvm_vgic_vcpu_pending_irq(vcpu)) {
+               pr_debug("CPU%d has no pending interrupt\n", vcpu_id);
+               goto epilog;
+       }
+
+       /* SGIs */

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 719

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       for_each_set_bit(i, vgic_cpu->pending, 16) {
+               unsigned long sources;
+
+               sources = dist->irq_sgi_sources[vcpu_id][i];
+               for_each_set_bit(c, &sources, 8) {
+                       if (!vgic_queue_irq(vcpu, c, i)) {
+                               overflow = 1;
+                               continue;
+                       }

If there are multiple sources, why do you need to queue the interrupt multiple times? I would have thought it could be collapsed into one.

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+                       clear_bit(c, &sources);
+               }
+
+               if (!sources)
+                       clear_bit(i, pending);

What does this signify and how does it happen? An SGI without a source sounds pretty weird...

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 737

Quoted 12 lines
arch/arm/kvm/vgic.c +12 -0
+
+               dist->irq_sgi_sources[vcpu_id][i] = sources;
+       }
+
+       /* PPIs */
+       for_each_set_bit_from(i, vgic_cpu->pending, 32) {
+               if (!vgic_queue_irq(vcpu, 0, i)) {
+                       overflow = 1;
+                       continue;
+               }
+
+               clear_bit(i, pending);

You could lose the `continue' and stick the clear_bit in an else clause (same for SGIs and SPIs).

Quoted 12 lines
arch/arm/kvm/vgic.c +12 -0
+       }
+
+
+       /* SPIs */
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       for_each_set_bit_from(i, vgic_cpu->pending, VGIC_NR_IRQS) {
+               if (vgic_bitmap_get_irq_val(&dist->irq_active, 0, i))
+                       continue; /* level interrupt, already queued */
+
+               if (!vgic_queue_irq(vcpu, 0, i)) {
+                       overflow = 1;
+                       continue;

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 759

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+               }
+
+               /* Immediate clear on edge, set active on level */
+               if (vgic_irq_is_edge(dist, i)) {
+                       clear_bit(i - 32, pending);
+                       clear_bit(i, vgic_cpu->pending);
+               } else {
+                       vgic_bitmap_set_irq_val(&dist->irq_active, 0, i, 1);
+               }
+       }

Hmm, more of this edge/level handling trying to use the same code and it not really working.

Quoted 34 lines
arch/arm/kvm/vgic.c +34 -0
+
+epilog:
+       if (overflow)
+               vgic_cpu->vgic_hcr |= VGIC_HCR_UIE;
+       else {
+               vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
+               /*
+                * We're about to run this VCPU, and we've consumed
+                * everything the distributor had in store for
+                * us. Claim we don't have anything pending. We'll
+                * adjust that if needed while exiting.
+                */
+               clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+       }
+}
+
+/*
+ * Sync back the VGIC state after a guest run. We do not really touch
+ * the distributor here (the irq_pending_on_cpu bit is safe to set),
+ * so there is no need for taking its lock.
+ */
+static void __kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, pending;
+
+       /* Clear mappings for empty LRs */
+       for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
+                        vgic_cpu->nr_lr) {
+               int irq;
+
+               if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
+                       continue;

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 803

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+
+               irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+               BUG_ON(irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+       }
+
+       /* Check if we still have something up our sleeve... */
+       pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                                     vgic_cpu->nr_lr);

Does this rely on timeliness of maintenance interrupts with respect to EOIs in the guest? i.e. if a maintenance interrupt is delayed (I can't see anything in the spec stating that they're synchronous) and you end up taking one here, will you accidentally re-pend the interrupt?

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 806

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       if (pending < vgic_cpu->nr_lr) {
+               set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+               smp_mb();

What's this barrier for?

Will

#31 lore raw

Re: [PATCH v4 07/13] ARM: KVM: VGIC virtual CPU interface management

Quoted 27 lines · Will Deacon

Hi Marc,

I've managed to look at some more of the vgic code, so here is some more feedback. I've still not got to the end of the series, but there's light at the end of the tunnel...

Quoted 20 lines · 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 |   41 +++++++
 arch/arm/kvm/vgic.c             |  226 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 266 insertions(+), 1 deletion(-)
arch/arm/include/asm/kvm_vgic.h +0 -0
index 9e60b1d..7229324 100644
@@ -193,8 +193,45 @@ struct vgic_dist {
 };
 
Quoted 6 lines

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 198

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +3 -0
 struct vgic_cpu {
+#ifdef CONFIG_KVM_ARM_VGIC
+       /* per IRQ to LR mapping */
+       u8              vgic_irq_lr_map[VGIC_NR_IRQS];

per IRQ?

Yes. We need to track which IRQ maps to which LR (so we can piggyback a pending interrupt on an active one).

Quoted 15 lines
Quoted 15 lines
arch/arm/include/asm/kvm_vgic.h +15 -0
+
+       /* Pending interrupts on this VCPU */
+       DECLARE_BITMAP( pending, VGIC_NR_IRQS);
+
+       /* Bitmap of used/free list registers */
+       DECLARE_BITMAP( lr_used, 64);
+
+       /* 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 */
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 218

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+       u32             vgic_elrsr[2];  /* Saved only */
+       u32             vgic_apr;
+       u32             vgic_lr[64];    /* A15 has only 4... */
+#endif
 };

Looks like we should have a #define for the maximum number of list registers, so we keep vgic_lr and lr_user in sync.

Indeed.

Quoted 12 lines
Quoted 12 lines
arch/arm/include/asm/kvm_vgic.h +11 -0
+#define VGIC_HCR_EN            (1 << 0)
+#define VGIC_HCR_UIE           (1 << 1)
+
+#define VGIC_LR_VIRTUALID      (0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID   (7 << 10)
+#define VGIC_LR_STATE          (3 << 28)
+#define VGIC_LR_PENDING_BIT    (1 << 28)
+#define VGIC_LR_ACTIVE_BIT     (1 << 29)
+#define VGIC_LR_EOI            (1 << 19)
+
+#define VGIC_MISR_EOI          (1 << 0)
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 234

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+#define VGIC_MISR_U            (1 << 1)
+
+#define LR_EMPTY       0xff
+

Could stick these in asm/hardware/gic.h. I know they're not used by the gic driver, but they're the same piece of architecture so it's probably worth keeping in one place.

This is on my list of things to do once the GIC code is shared between arm and arm64. Could do it earlier if that makes more sense.

Quoted 1 line

You'd probably also want a s/VGIC/GICH/

Sure.

Quoted 23 lines
Quoted 23 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
 struct kvm;
 struct kvm_vcpu;
 struct kvm_run;
@@ -202,9 +239,13 @@ 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_sync_to_cpu(struct kvm_vcpu *vcpu);
+void kvm_vgic_sync_from_cpu(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 +0 -0
index 82feee8..d7cdec5 100644
@@ -587,7 +587,25 @@ static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
 
Quoted 15 lines

arch/arm/kvm/vgic.c static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) · line 599

Quoted 13 lines
arch/arm/kvm/vgic.c +10 -1
 static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
 {
-       return 0;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending, *enabled, *pend;
+       int vcpu_id;
+
+       vcpu_id = vcpu->vcpu_id;
+       pend = vcpu->arch.vgic_cpu.pending;
+
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
+       bitmap_and(pend, pending, enabled, 32);

pend and pending! vcpu_pending and dist_pending?

A lot of that code has already been reworked. See:
https://lists.cs.columbia.edu/pipermail/kvmarm/2012-November/004138.html

Quoted 16 lines
Quoted 16 lines
arch/arm/kvm/vgic.c +9 -0
+
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
+       bitmap_and(pend + 1, pending, enabled, VGIC_NR_SHARED_IRQS);
+       bitmap_and(pend + 1, pend + 1,
+                  vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
+                  VGIC_NR_SHARED_IRQS);
+
+       return (find_first_bit(pend, VGIC_NR_IRQS) < VGIC_NR_IRQS);
 }
 
 /*
@@ -613,6 +631,212 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 634

Quoted 1 line
arch/arm/kvm/vgic.c +1 -0
+#define LR_PHYSID(lr)          (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)

Is VGIC_LR_PHYSID_CPUID wide enough for this? The CPUID is only 3 bits, but the interrupt ID could be larger. Or do you not supported hardware interrupt forwarding? (in which case, LR_PHYSID is a misleading name).

Hardware interrupt forwarding is not supported. PHYSID is the name of the actual field in the spec, hence the name of the macro. LR_CPUID?

Quoted 8 lines
Quoted 8 lines
arch/arm/kvm/vgic.c +8 -0
+#define MK_LR_PEND(src, irq)   (VGIC_LR_PENDING_BIT | ((src) << 10) | (irq))
+/*
+ * Queue an interrupt to a CPU virtual interface. Return true on success,
+ * or false if it wasn't possible to queue it.
+ */
+static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 647

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, is_level;
+
+       /* Sanitize the input... */
+       BUG_ON(sgi_source_id & ~7);

sgi_source_id > MAX_SGI_SOURCES (or whatever we end up having for the SGI and PPI limits).

OK.

Quoted 3 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 648

Quoted 1 line
arch/arm/kvm/vgic.c +1 -0
+       BUG_ON(sgi_source_id && irq > 15);

irq > MAX_PPI_SOURCES

OK.

Quoted 33 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 658

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       BUG_ON(irq >= VGIC_NR_IRQS);
+
+       kvm_debug("Queue IRQ%d\n", irq);
+
+       lr = vgic_cpu->vgic_irq_lr_map[irq];
+       is_level = !vgic_irq_is_edge(dist, irq);
+
+       /* Do we have an active interrupt for the same CPUID? */
+       if (lr != LR_EMPTY &&
+           (LR_PHYSID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) {

Ok, so this does return the source.

Quoted 20 lines
arch/arm/kvm/vgic.c +20 -0
+               kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
+               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
+               if (is_level)
+                       vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+               return true;
+       }
+
+       /* Try to use another LR for this interrupt */
+       lr = find_first_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                              vgic_cpu->nr_lr);
+       if (lr >= vgic_cpu->nr_lr)
+               return false;
+
+       kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
+       vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
+       if (is_level)
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
+       vgic_cpu->vgic_irq_lr_map[irq] = lr;
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 683

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
+       set_bit(lr, vgic_cpu->lr_used);
+
+       return true;
+}

I can't help but feel that this could be made cleaner by moving the level-specific EOI handling out into a separate function.

Do you mean having two functions, one for edge and the other for level? Seems overkill to me. I could move the "if (is_level) ..." to a common spot though.

Quoted 31 lines
Quoted 31 lines
arch/arm/kvm/vgic.c +31 -0
+
+/*
+ * Fill the list registers with pending interrupts before running the
+ * guest.
+ */
+static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending;
+       int i, c, vcpu_id;
+       int overflow = 0;
+
+       vcpu_id = vcpu->vcpu_id;
+
+       /*
+        * We may not have any pending interrupt, or the interrupts
+        * may have been serviced from another vcpu. In all cases,
+        * move along.
+        */
+       if (!kvm_vgic_vcpu_pending_irq(vcpu)) {
+               pr_debug("CPU%d has no pending interrupt\n", vcpu_id);
+               goto epilog;
+       }
+
+       /* SGIs */
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       for_each_set_bit(i, vgic_cpu->pending, 16) {
+               unsigned long sources;
+
+               sources = dist->irq_sgi_sources[vcpu_id][i];
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 719

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+               for_each_set_bit(c, &sources, 8) {
+                       if (!vgic_queue_irq(vcpu, c, i)) {
+                               overflow = 1;
+                               continue;
+                       }

If there are multiple sources, why do you need to queue the interrupt multiple times? I would have thought it could be collapsed into one.

Because SGIs from different sources *are* different interrupts. In an n-CPU system (with n > 2), you could have some message passing system based on interrupts, and you'd need to know which CPU is pinging you.

Quoted 9 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+                       clear_bit(c, &sources);
+               }
+
+               if (!sources)
+                       clear_bit(i, pending);

What does this signify and how does it happen? An SGI without a source sounds pretty weird...

See the clear_bit() just above. Once all the sources for this SGI are cleared, we can make the interrupt not pending anymore.

Quoted 7 lines
Quoted 7 lines
arch/arm/kvm/vgic.c +7 -0
+
+               dist->irq_sgi_sources[vcpu_id][i] = sources;
+       }
+
+       /* PPIs */
+       for_each_set_bit_from(i, vgic_cpu->pending, 32) {
+               if (!vgic_queue_irq(vcpu, 0, i)) {
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+                       overflow = 1;
+                       continue;
+               }
+
+               clear_bit(i, pending);

You could lose the `continue' and stick the clear_bit in an else clause (same for SGIs and SPIs).

Sure.

Quoted 17 lines
Quoted 17 lines
arch/arm/kvm/vgic.c +17 -0
+       }
+
+
+       /* SPIs */
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       for_each_set_bit_from(i, vgic_cpu->pending, VGIC_NR_IRQS) {
+               if (vgic_bitmap_get_irq_val(&dist->irq_active, 0, i))
+                       continue; /* level interrupt, already queued */
+
+               if (!vgic_queue_irq(vcpu, 0, i)) {
+                       overflow = 1;
+                       continue;
+               }
+
+               /* Immediate clear on edge, set active on level */
+               if (vgic_irq_is_edge(dist, i)) {
+                       clear_bit(i - 32, pending);
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 759

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+                       clear_bit(i, vgic_cpu->pending);
+               } else {
+                       vgic_bitmap_set_irq_val(&dist->irq_active, 0, i, 1);
+               }
+       }

Hmm, more of this edge/level handling trying to use the same code and it not really working.

Hmmm. Let me think of a better way to do this without ending up duplicating too much code (it is complicated enough that I don't want to maintain two copies of it).

Quoted 41 lines
Quoted 41 lines
arch/arm/kvm/vgic.c +41 -0
+
+epilog:
+       if (overflow)
+               vgic_cpu->vgic_hcr |= VGIC_HCR_UIE;
+       else {
+               vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
+               /*
+                * We're about to run this VCPU, and we've consumed
+                * everything the distributor had in store for
+                * us. Claim we don't have anything pending. We'll
+                * adjust that if needed while exiting.
+                */
+               clear_bit(vcpu_id, &dist->irq_pending_on_cpu);
+       }
+}
+
+/*
+ * Sync back the VGIC state after a guest run. We do not really touch
+ * the distributor here (the irq_pending_on_cpu bit is safe to set),
+ * so there is no need for taking its lock.
+ */
+static void __kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, pending;
+
+       /* Clear mappings for empty LRs */
+       for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
+                        vgic_cpu->nr_lr) {
+               int irq;
+
+               if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
+                       continue;
+
+               irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+               BUG_ON(irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+       }
+
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 803

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       /* Check if we still have something up our sleeve... */
+       pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                                     vgic_cpu->nr_lr);

Does this rely on timeliness of maintenance interrupts with respect to EOIs in the guest? i.e. if a maintenance interrupt is delayed (I can't see anything in the spec stating that they're synchronous) and you end up taking one here, will you accidentally re-pend the interrupt?

I don't think so. ELRSR only indicates that the list register is empty. If we find a zero bit there, we flag that this vcpu has at least one pending interrupt (in its list registers). A delayed maintenance interrupt may race with this by also setting this bit if an interrupt is still in the active state after being EOIed, but that's not a problem (we just set_bit twice). A race between clear and set would be problematic though.

Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 806

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       if (pending < vgic_cpu->nr_lr) {
+               set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+               smp_mb();

What's this barrier for?

It is strategically placed to entertain the reviewer. And it does its job! I'll nuke it, now that you found it. ;-)

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#32 lore raw

Re: [PATCH v4 07/13] ARM: KVM: VGIC virtual CPU interface management

Quoted 22 lines · Marc Zyngier
Quoted 12 lines · Will Deacon
Quoted 12 lines
arch/arm/include/asm/kvm_vgic.h +11 -0
+#define VGIC_HCR_EN            (1 << 0)
+#define VGIC_HCR_UIE           (1 << 1)
+
+#define VGIC_LR_VIRTUALID      (0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID   (7 << 10)
+#define VGIC_LR_STATE          (3 << 28)
+#define VGIC_LR_PENDING_BIT    (1 << 28)
+#define VGIC_LR_ACTIVE_BIT     (1 << 29)
+#define VGIC_LR_EOI            (1 << 19)
+
+#define VGIC_MISR_EOI          (1 << 0)
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 234

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+#define VGIC_MISR_U            (1 << 1)
+
+#define LR_EMPTY       0xff
+

Could stick these in asm/hardware/gic.h. I know they're not used by the gic driver, but they're the same piece of architecture so it's probably worth keeping in one place.

Quoted 2 lines

This is on my list of things to do once the GIC code is shared between arm and arm64. Could do it earlier if that makes more sense.

Might as well as I found some others in a later patch too.

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines

static int compute_pending_for_cpu(struct kvm_vcpu *vcpu) {

arch/arm/kvm/vgic.c +4 -1
-       return 0;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending, *enabled, *pend;
+       int vcpu_id;
+
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) · line 599

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+       vcpu_id = vcpu->vcpu_id;
+       pend = vcpu->arch.vgic_cpu.pending;
+
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
+       bitmap_and(pend, pending, enabled, 32);

pend and pending! vcpu_pending and dist_pending?

Quoted 2 lines

A lot of that code has already been reworked. See:
https://lists.cs.columbia.edu/pipermail/kvmarm/2012-November/004138.html

Argh, too much code! Ok, as long as it's being looked at.

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
arch/arm/kvm/vgic.c +9 -0
+
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
+       bitmap_and(pend + 1, pending, enabled, VGIC_NR_SHARED_IRQS);
+       bitmap_and(pend + 1, pend + 1,
+                  vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
+                  VGIC_NR_SHARED_IRQS);
+
+       return (find_first_bit(pend, VGIC_NR_IRQS) < VGIC_NR_IRQS);
 }
 
 /*
@@ -613,6 +631,212 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
Quoted 8 lines
Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 634

Quoted 1 line
arch/arm/kvm/vgic.c +1 -0
+#define LR_PHYSID(lr)          (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)

Is VGIC_LR_PHYSID_CPUID wide enough for this? The CPUID is only 3 bits, but the interrupt ID could be larger. Or do you not supported hardware interrupt forwarding? (in which case, LR_PHYSID is a misleading name).

Hardware interrupt forwarding is not supported. PHYSID is the name of the actual field in the spec, hence the name of the macro. LR_CPUID?

Sure.

Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/vgic.c +20 -0
+               kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
+               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
+               if (is_level)
+                       vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+               return true;
+       }
+
+       /* Try to use another LR for this interrupt */
+       lr = find_first_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                              vgic_cpu->nr_lr);
+       if (lr >= vgic_cpu->nr_lr)
+               return false;
+
+       kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
+       vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
+       if (is_level)
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
+       vgic_cpu->vgic_irq_lr_map[irq] = lr;
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 683

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
+       set_bit(lr, vgic_cpu->lr_used);
+
+       return true;
+}

I can't help but feel that this could be made cleaner by moving the level-specific EOI handling out into a separate function.

Quoted 3 lines

Do you mean having two functions, one for edge and the other for level? Seems overkill to me. I could move the "if (is_level) ..." to a common spot though.

Indeed, you could just have something like vgic_eoi_irq and call that in one place, letting that function do the level check.

Quoted 40 lines
Quoted 31 lines
Quoted 31 lines
arch/arm/kvm/vgic.c +31 -0
+
+/*
+ * Fill the list registers with pending interrupts before running the
+ * guest.
+ */
+static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending;
+       int i, c, vcpu_id;
+       int overflow = 0;
+
+       vcpu_id = vcpu->vcpu_id;
+
+       /*
+        * We may not have any pending interrupt, or the interrupts
+        * may have been serviced from another vcpu. In all cases,
+        * move along.
+        */
+       if (!kvm_vgic_vcpu_pending_irq(vcpu)) {
+               pr_debug("CPU%d has no pending interrupt\n", vcpu_id);
+               goto epilog;
+       }
+
+       /* SGIs */
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       for_each_set_bit(i, vgic_cpu->pending, 16) {
+               unsigned long sources;
+
+               sources = dist->irq_sgi_sources[vcpu_id][i];
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 719

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+               for_each_set_bit(c, &sources, 8) {
+                       if (!vgic_queue_irq(vcpu, c, i)) {
+                               overflow = 1;
+                               continue;
+                       }

If there are multiple sources, why do you need to queue the interrupt multiple times? I would have thought it could be collapsed into one.

Quoted 3 lines

Because SGIs from different sources *are* different interrupts. In an n-CPU system (with n > 2), you could have some message passing system based on interrupts, and you'd need to know which CPU is pinging you.

Ok, fair point.

Quoted 10 lines
Quoted 9 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+                       clear_bit(c, &sources);
+               }
+
+               if (!sources)
+                       clear_bit(i, pending);

What does this signify and how does it happen? An SGI without a source sounds pretty weird...

Quoted 2 lines

See the clear_bit() just above. Once all the sources for this SGI are cleared, we can make the interrupt not pending anymore.

Yup, missed that.

Quoted 34 lines
Quoted 25 lines
Quoted 25 lines
arch/arm/kvm/vgic.c +25 -0
+/*
+ * Sync back the VGIC state after a guest run. We do not really touch
+ * the distributor here (the irq_pending_on_cpu bit is safe to set),
+ * so there is no need for taking its lock.
+ */
+static void __kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, pending;
+
+       /* Clear mappings for empty LRs */
+       for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
+                        vgic_cpu->nr_lr) {
+               int irq;
+
+               if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
+                       continue;
+
+               irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+               BUG_ON(irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+       }
+
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 803

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       /* Check if we still have something up our sleeve... */
+       pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                                     vgic_cpu->nr_lr);

Does this rely on timeliness of maintenance interrupts with respect to EOIs in the guest? i.e. if a maintenance interrupt is delayed (I can't see anything in the spec stating that they're synchronous) and you end up taking one here, will you accidentally re-pend the interrupt?

Quoted 7 lines

I don't think so. ELRSR only indicates that the list register is empty. If we find a zero bit there, we flag that this vcpu has at least one pending interrupt (in its list registers). A delayed maintenance interrupt may race with this by also setting this bit if an interrupt is still in the active state after being EOIed, but that's not a problem (we just set_bit twice). A race between clear and set would be problematic though.

Hmm, yes, the EOI maintenance handler only sets pending IRQs. So, to turn it around, how about __kvm_vgic_sync_to_cpu? There is a comment in the maintenance handler about it:

* level interrupt. There is a potential race with
* the queuing of an interrupt in __kvm_sync_to_cpu(), where we check
* if the interrupt is already active. Two possibilities:
*
* - The queuing is occuring on the same vcpu: cannot happen, as we're
* already in the context of this vcpu, and executing the handler

Does this still apply if the maintenance interrupt comes in late? It will then look like the stopped vcpu just EOId an interrupt...

Quoted 8 lines
Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 806

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       if (pending < vgic_cpu->nr_lr) {
+               set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+               smp_mb();

What's this barrier for?

It is strategically placed to entertain the reviewer. And it does its job! I'll nuke it, now that you found it. ;-)

Excellent! I think there may be another one on the horizon when I get into the maintenance interrupt handler proper too. Looking forward to it.

Will

#33 lore raw

Re: [PATCH v4 07/13] ARM: KVM: VGIC virtual CPU interface management

Quoted 228 lines · Will Deacon
Quoted 22 lines · Marc Zyngier
Quoted 12 lines · Will Deacon
Quoted 12 lines
arch/arm/include/asm/kvm_vgic.h +11 -0
+#define VGIC_HCR_EN            (1 << 0)
+#define VGIC_HCR_UIE           (1 << 1)
+
+#define VGIC_LR_VIRTUALID      (0x3ff << 0)
+#define VGIC_LR_PHYSID_CPUID   (7 << 10)
+#define VGIC_LR_STATE          (3 << 28)
+#define VGIC_LR_PENDING_BIT    (1 << 28)
+#define VGIC_LR_ACTIVE_BIT     (1 << 29)
+#define VGIC_LR_EOI            (1 << 19)
+
+#define VGIC_MISR_EOI          (1 << 0)
Quoted 8 lines

arch/arm/include/asm/kvm_vgic.h struct vgic_dist { · line 234

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +4 -0
+#define VGIC_MISR_U            (1 << 1)
+
+#define LR_EMPTY       0xff
+

Could stick these in asm/hardware/gic.h. I know they're not used by the gic driver, but they're the same piece of architecture so it's probably worth keeping in one place.

Quoted 2 lines

This is on my list of things to do once the GIC code is shared between arm and arm64. Could do it earlier if that makes more sense.

Might as well as I found some others in a later patch too.

Quoted 16 lines
Quoted 7 lines
Quoted 7 lines

static int compute_pending_for_cpu(struct kvm_vcpu *vcpu) {

arch/arm/kvm/vgic.c +4 -1
-       return 0;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending, *enabled, *pend;
+       int vcpu_id;
+
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) · line 599

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+       vcpu_id = vcpu->vcpu_id;
+       pend = vcpu->arch.vgic_cpu.pending;
+
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
+       bitmap_and(pend, pending, enabled, 32);

pend and pending! vcpu_pending and dist_pending?

Quoted 2 lines

A lot of that code has already been reworked. See:
https://lists.cs.columbia.edu/pipermail/kvmarm/2012-November/004138.html

Argh, too much code! Ok, as long as it's being looked at.

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
arch/arm/kvm/vgic.c +9 -0
+
+       pending = vgic_bitmap_get_shared_map(&dist->irq_state);
+       enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled);
+       bitmap_and(pend + 1, pending, enabled, VGIC_NR_SHARED_IRQS);
+       bitmap_and(pend + 1, pend + 1,
+                  vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]),
+                  VGIC_NR_SHARED_IRQS);
+
+       return (find_first_bit(pend, VGIC_NR_IRQS) < VGIC_NR_IRQS);
 }
 
 /*
@@ -613,6 +631,212 @@ static void vgic_update_state(struct kvm *kvm)
        }
 }
 
Quoted 8 lines
Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 634

Quoted 1 line
arch/arm/kvm/vgic.c +1 -0
+#define LR_PHYSID(lr)          (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)

Is VGIC_LR_PHYSID_CPUID wide enough for this? The CPUID is only 3 bits, but the interrupt ID could be larger. Or do you not supported hardware interrupt forwarding? (in which case, LR_PHYSID is a misleading name).

Hardware interrupt forwarding is not supported. PHYSID is the name of the actual field in the spec, hence the name of the macro. LR_CPUID?

Sure.

Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
arch/arm/kvm/vgic.c +20 -0
+               kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
+               BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
+               if (is_level)
+                       vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+               return true;
+       }
+
+       /* Try to use another LR for this interrupt */
+       lr = find_first_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                              vgic_cpu->nr_lr);
+       if (lr >= vgic_cpu->nr_lr)
+               return false;
+
+       kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
+       vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
+       if (is_level)
+               vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+
+       vgic_cpu->vgic_irq_lr_map[irq] = lr;
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 683

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+       clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
+       set_bit(lr, vgic_cpu->lr_used);
+
+       return true;
+}

I can't help but feel that this could be made cleaner by moving the level-specific EOI handling out into a separate function.

Quoted 3 lines

Do you mean having two functions, one for edge and the other for level? Seems overkill to me. I could move the "if (is_level) ..." to a common spot though.

Indeed, you could just have something like vgic_eoi_irq and call that in one place, letting that function do the level check.

Quoted 40 lines
Quoted 31 lines
Quoted 31 lines
arch/arm/kvm/vgic.c +31 -0
+
+/*
+ * Fill the list registers with pending interrupts before running the
+ * guest.
+ */
+static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       unsigned long *pending;
+       int i, c, vcpu_id;
+       int overflow = 0;
+
+       vcpu_id = vcpu->vcpu_id;
+
+       /*
+        * We may not have any pending interrupt, or the interrupts
+        * may have been serviced from another vcpu. In all cases,
+        * move along.
+        */
+       if (!kvm_vgic_vcpu_pending_irq(vcpu)) {
+               pr_debug("CPU%d has no pending interrupt\n", vcpu_id);
+               goto epilog;
+       }
+
+       /* SGIs */
+       pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id);
+       for_each_set_bit(i, vgic_cpu->pending, 16) {
+               unsigned long sources;
+
+               sources = dist->irq_sgi_sources[vcpu_id][i];
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 719

Quoted 5 lines
arch/arm/kvm/vgic.c +5 -0
+               for_each_set_bit(c, &sources, 8) {
+                       if (!vgic_queue_irq(vcpu, c, i)) {
+                               overflow = 1;
+                               continue;
+                       }

If there are multiple sources, why do you need to queue the interrupt multiple times? I would have thought it could be collapsed into one.

Quoted 3 lines

Because SGIs from different sources *are* different interrupts. In an n-CPU system (with n > 2), you could have some message passing system based on interrupts, and you'd need to know which CPU is pinging you.

Ok, fair point.

Quoted 10 lines
Quoted 9 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+                       clear_bit(c, &sources);
+               }
+
+               if (!sources)
+                       clear_bit(i, pending);

What does this signify and how does it happen? An SGI without a source sounds pretty weird...

Quoted 2 lines

See the clear_bit() just above. Once all the sources for this SGI are cleared, we can make the interrupt not pending anymore.

Yup, missed that.

Quoted 34 lines
Quoted 25 lines
Quoted 25 lines
arch/arm/kvm/vgic.c +25 -0
+/*
+ * Sync back the VGIC state after a guest run. We do not really touch
+ * the distributor here (the irq_pending_on_cpu bit is safe to set),
+ * so there is no need for taking its lock.
+ */
+static void __kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       int lr, pending;
+
+       /* Clear mappings for empty LRs */
+       for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr,
+                        vgic_cpu->nr_lr) {
+               int irq;
+
+               if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
+                       continue;
+
+               irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+               BUG_ON(irq >= VGIC_NR_IRQS);
+               vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+       }
+
Quoted 8 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 803

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       /* Check if we still have something up our sleeve... */
+       pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr,
+                                     vgic_cpu->nr_lr);

Does this rely on timeliness of maintenance interrupts with respect to EOIs in the guest? i.e. if a maintenance interrupt is delayed (I can't see anything in the spec stating that they're synchronous) and you end up taking one here, will you accidentally re-pend the interrupt?

Quoted 7 lines

I don't think so. ELRSR only indicates that the list register is empty. If we find a zero bit there, we flag that this vcpu has at least one pending interrupt (in its list registers). A delayed maintenance interrupt may race with this by also setting this bit if an interrupt is still in the active state after being EOIed, but that's not a problem (we just set_bit twice). A race between clear and set would be problematic though.

Hmm, yes, the EOI maintenance handler only sets pending IRQs. So, to turn it around, how about __kvm_vgic_sync_to_cpu? There is a comment in the maintenance handler about it:

* level interrupt. There is a potential race with
* the queuing of an interrupt in __kvm_sync_to_cpu(), where we check
* if the interrupt is already active. Two possibilities:
*
* - The queuing is occuring on the same vcpu: cannot happen, as we're
* already in the context of this vcpu, and executing the handler

Quoted 2 lines

Does this still apply if the maintenance interrupt comes in late? It will then look like the stopped vcpu just EOId an interrupt...

Gniiii... Yup, there is a race at the end of __kvm_vgic_sync_to_cpu(), when we decide we've injected all non-active pending interrupts. The maintenance interrupt could fire just before the clear_bit, and we'd loose the now pending interrupt for a round. Probably not fatal, but still.

I think I'll use spin_lock_irqsave() in kvm_vgic_sync_to_cpu(), it will save me a lot of headache.

But the ugliest thing with the maintenance interrupt is that if it is delayed for long enough, you could end up messing with the wrong vcpu, or no vcpu at all. But I don't think there is much you can do about this. If your hardware is broken enough to deliver late VGIC interrupt, we're screwed.

Quoted 11 lines
Quoted 8 lines
Quoted 5 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 806

Quoted 3 lines
arch/arm/kvm/vgic.c +3 -0
+       if (pending < vgic_cpu->nr_lr) {
+               set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
+               smp_mb();

What's this barrier for?

It is strategically placed to entertain the reviewer. And it does its job! I'll nuke it, now that you found it. ;-)

Excellent! I think there may be another one on the horizon when I get into the maintenance interrupt handler proper too. Looking forward to it.

Enjoy!

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#34 lore raw

Re: [PATCH v4 07/13] ARM: KVM: VGIC virtual CPU interface management

In reply to Marc Zyngier

[...]

Quoted 11 lines
Quoted 10 lines

arch/arm/kvm/vgic.c static void vgic_update_state(struct kvm *kvm) · line 725

Quoted 7 lines
arch/arm/kvm/vgic.c +6 -0
+
+                       clear_bit(c, &sources);
+               }
+
+               if (!sources)
+                       clear_bit(i, pending);

What does this signify and how does it happen? An SGI without a source sounds pretty weird...

Quoted 3 lines

See the clear_bit() just above. Once all the sources for this SGI are cleared, we can make the interrupt not pending anymore.

every time I read the code, I get completely bogged up on trying to understand this case and I tell myself we should put a comment here, then I understand why it happens and I think, oh it's obvious, no comment needed, but now I (almost) forgot again. Could we add a comment?

-Christoffer

#35 lore raw

PATCH v4 8/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 d7cdec5..dda5623 100644
@@ -633,6 +633,34 @@ static void vgic_update_state(struct kvm *kvm)
 
 #define LR_PHYSID(lr) 		(((lr) & VGIC_LR_PHYSID_CPUID) >> 10)
 #define MK_LR_PEND(src, irq)	(VGIC_LR_PENDING_BIT | ((src) << 10) | (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;
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	int lr;
+
+	for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
+		int irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+		if (!vgic_bitmap_get_irq_val(&dist->irq_enabled,
+					     vcpu->vcpu_id, irq)) {
+			vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+			clear_bit(lr, vgic_cpu->lr_used);
+			vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_STATE;
+		}
+	}
+}
+
 /*
  * Queue an interrupt to a CPU virtual interface. Return true on success,
  * or false if it wasn't possible to queue it.
@@ -696,6 +724,8 @@ static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
 
 	vcpu_id = vcpu->vcpu_id;
 
+	vgic_retire_disabled_irqs(vcpu);
+
 	/*
 	 * We may not have any pending interrupt, or the interrupts
 	 * may have been serviced from another vcpu. In all cases,
#36 lore raw

Re: [PATCH v4 08/13] ARM: KVM: vgic: retire queued, disabled interrupts

Quoted 56 lines · 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 +28 -0
index d7cdec5..dda5623 100644
@@ -633,6 +633,34 @@ static void vgic_update_state(struct kvm *kvm)
 
 #define LR_PHYSID(lr) 		(((lr) & VGIC_LR_PHYSID_CPUID) >> 10)
 #define MK_LR_PEND(src, irq)	(VGIC_LR_PENDING_BIT | ((src) << 10) | (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;
+	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+	int lr;
+
+	for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
+		int irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+		if (!vgic_bitmap_get_irq_val(&dist->irq_enabled,
+					     vcpu->vcpu_id, irq)) {
+			vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+			clear_bit(lr, vgic_cpu->lr_used);
+			vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_STATE;
+		}
+	}
+}
+
 /*
  * Queue an interrupt to a CPU virtual interface. Return true on success,
  * or false if it wasn't possible to queue it.

arch/arm/kvm/vgic.c static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) · line 727

Quoted 4 lines
arch/arm/kvm/vgic.c +1 -0
@@ -696,6 +724,8 @@ static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
 
 	vcpu_id = vcpu->vcpu_id;
 
+	vgic_retire_disabled_irqs(vcpu);

Wouldn't it be better to do this when the interrupt is disabled, rather than do the checking in the sync_to_cpu path?

Will

#37 lore raw

PATCH v4 9/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              |   29 +++++++++++++
 arch/arm/kvm/vgic.c             |   90 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 7229324..6e3d303 100644
@@ -241,6 +241,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(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);
@@ -271,6 +273,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+				      const struct kvm_irq_level *irq)
+{
+	return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
 	return 0;
arch/arm/kvm/arm.c +29 -0
index 3ac1aab..f43da01 100644
@@ -764,10 +764,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 
 	switch (irq_type) {
 	case KVM_ARM_IRQ_TYPE_CPU:
+		if (irqchip_in_kernel(kvm))
+			return -ENXIO;
+
 		if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
 			return -EINVAL;
 
 		return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_ARM_IRQ_TYPE_PPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 16 || irq_num > 31)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+	case KVM_ARM_IRQ_TYPE_SPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
 	}
 
 	return -EINVAL;
@@ -849,6 +870,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	void __user *argp = (void __user *)arg;
 
 	switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CREATE_IRQCHIP: {
+		if (vgic_present)
+			return kvm_vgic_create(kvm);
+		else
+			return -EINVAL;
+	}
+#endif
 	case KVM_SET_DEVICE_ADDRESS: {
 		struct kvm_device_address dev_addr;
 
arch/arm/kvm/vgic.c +90 -0

108 lines not on this page · raw message

#38 lore raw

Re: [PATCH v4 09/13] ARM: KVM: VGIC interrupt injection

Quoted 51 lines · 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              |   29 +++++++++++++
 arch/arm/kvm/vgic.c             |   90 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 7229324..6e3d303 100644
@@ -241,6 +241,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(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);
@@ -271,6 +273,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+				      const struct kvm_irq_level *irq)
+{
+	return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
 	return 0;
arch/arm/kvm/arm.c +3 -0
index 3ac1aab..f43da01 100644
@@ -764,10 +764,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 
 	switch (irq_type) {
 	case KVM_ARM_IRQ_TYPE_CPU:
+		if (irqchip_in_kernel(kvm))
+			return -ENXIO;
+
 		if (irq_num > KVM_ARM_IRQ_CPU_FIQ)

arch/arm/kvm/arm.c int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) · line 780

Quoted 10 lines
arch/arm/kvm/arm.c +7 -0
 			return -EINVAL;
 
 		return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_ARM_IRQ_TYPE_PPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 16 || irq_num > 31)
+			return -EINVAL;

It's our favourite two numbers again! :)

Quoted 15 lines
arch/arm/kvm/arm.c +11 -0
+
+		return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+	case KVM_ARM_IRQ_TYPE_SPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
 	}
 
 	return -EINVAL;

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 878

Quoted 9 lines
arch/arm/kvm/arm.c +6 -0
@@ -849,6 +870,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	void __user *argp = (void __user *)arg;
 
 	switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CREATE_IRQCHIP: {
+		if (vgic_present)
+			return kvm_vgic_create(kvm);
+		else
+			return -EINVAL;

ENXIO? At least, that's what you use when setting the GIC addresses.

Quoted 60 lines
arch/arm/kvm/arm.c +2 -0
+	}
+#endif
 	case KVM_SET_DEVICE_ADDRESS: {
 		struct kvm_device_address dev_addr;
 
arch/arm/kvm/vgic.c +33 -0
index dda5623..70040bb 100644
@@ -75,6 +75,7 @@
 #define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
 
 static void vgic_update_state(struct kvm *kvm);
+static void vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 
 static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
@@ -542,6 +543,9 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 	kvm_prepare_mmio(run, mmio);
 	kvm_handle_mmio_return(vcpu, run);
 
+	if (updated_state)
+		vgic_kick_vcpus(vcpu->kvm);
+
 	return true;
 }
 
@@ -867,6 +871,92 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 	return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
 }
 
+static void vgic_kick_vcpus(struct kvm *kvm)
+{
+	struct kvm_vcpu *vcpu;
+	int c;
+
+	/*
+	 * We've injected an interrupt, time to find out who deserves
+	 * a good kick...
+	 */
+	kvm_for_each_vcpu(c, vcpu, kvm) {
+		if (kvm_vgic_vcpu_pending_irq(vcpu))
+			kvm_vcpu_kick(vcpu);
+	}
+}
+
+static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
+				  unsigned int irq_num, bool level)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct kvm_vcpu *vcpu;
+	int is_edge, is_level, state;
+	int enabled;
+	bool ret = true;
+
+	spin_lock(&dist->lock);
+
+	is_edge = vgic_irq_is_edge(dist, irq_num);
+	is_level = !is_edge;
+	state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);

arch/arm/kvm/vgic.c int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) · line 912

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+
+	/*
+	 * Only inject an interrupt if:
+	 * - level triggered and we change level
+	 * - edge triggered and we have a rising edge
+	 */
+	if ((is_level && !(state ^ level)) || (is_edge && (state || !level))) {
+		ret = false;
+		goto out;
+	}

Eek, more of the edge/level combo. Can this be be restructured so that we have vgic_update_{edge,level}_irq_state, which are called from here appropriately?

Will

#39 lore raw

Re: [PATCH v4 09/13] ARM: KVM: VGIC interrupt injection

Quoted 54 lines · Will Deacon
Quoted 53 lines · 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              |   29 +++++++++++++
 arch/arm/kvm/vgic.c             |   90 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 7229324..6e3d303 100644
@@ -241,6 +241,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(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);
@@ -271,6 +273,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+				      const struct kvm_irq_level *irq)
+{
+	return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
 	return 0;
arch/arm/kvm/arm.c +3 -0
index 3ac1aab..f43da01 100644
@@ -764,10 +764,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 
 	switch (irq_type) {
 	case KVM_ARM_IRQ_TYPE_CPU:
+		if (irqchip_in_kernel(kvm))
+			return -ENXIO;
+
 		if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
 			return -EINVAL;
 
Quoted 10 lines

arch/arm/kvm/arm.c int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) · line 780

Quoted 8 lines
arch/arm/kvm/arm.c +7 -0
 		return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_ARM_IRQ_TYPE_PPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 16 || irq_num > 31)
+			return -EINVAL;

It's our favourite two numbers again! :)

I already fixed a number of them. Probably missed this one though.

Quoted 17 lines
Quoted 17 lines
arch/arm/kvm/arm.c +11 -0
+
+		return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+	case KVM_ARM_IRQ_TYPE_SPI:
+		if (!irqchip_in_kernel(kvm))
+			return -ENXIO;
+
+		if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+			return -EINVAL;
+
+		return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
 	}
 
 	return -EINVAL;
@@ -849,6 +870,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
 	void __user *argp = (void __user *)arg;
 
Quoted 9 lines

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 878

Quoted 7 lines
arch/arm/kvm/arm.c +6 -0
 	switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+	case KVM_CREATE_IRQCHIP: {
+		if (vgic_present)
+			return kvm_vgic_create(kvm);
+		else
+			return -EINVAL;

ENXIO? At least, that's what you use when setting the GIC addresses.

-EINVAL seems to be one of the values other archs are using. -ENXIO is not one of them for KVM_CREATE_IRQCHIP. Doesn't mean they are right, but for the sake of keeping userspace happy, I'm not really inclined to change this.

Christoffer?

Quoted 66 lines
Quoted 66 lines
arch/arm/kvm/arm.c +2 -0
+	}
+#endif
 	case KVM_SET_DEVICE_ADDRESS: {
 		struct kvm_device_address dev_addr;
 
arch/arm/kvm/vgic.c +39 -0
index dda5623..70040bb 100644
@@ -75,6 +75,7 @@
 #define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
 
 static void vgic_update_state(struct kvm *kvm);
+static void vgic_kick_vcpus(struct kvm *kvm);
 static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
 
 static inline int vgic_irq_is_edge(struct vgic_dist *dist, int irq)
@@ -542,6 +543,9 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 	kvm_prepare_mmio(run, mmio);
 	kvm_handle_mmio_return(vcpu, run);
 
+	if (updated_state)
+		vgic_kick_vcpus(vcpu->kvm);
+
 	return true;
 }
 
@@ -867,6 +871,92 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 	return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu);
 }
 
+static void vgic_kick_vcpus(struct kvm *kvm)
+{
+	struct kvm_vcpu *vcpu;
+	int c;
+
+	/*
+	 * We've injected an interrupt, time to find out who deserves
+	 * a good kick...
+	 */
+	kvm_for_each_vcpu(c, vcpu, kvm) {
+		if (kvm_vgic_vcpu_pending_irq(vcpu))
+			kvm_vcpu_kick(vcpu);
+	}
+}
+
+static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
+				  unsigned int irq_num, bool level)
+{
+	struct vgic_dist *dist = &kvm->arch.vgic;
+	struct kvm_vcpu *vcpu;
+	int is_edge, is_level, state;
+	int enabled;
+	bool ret = true;
+
+	spin_lock(&dist->lock);
+
+	is_edge = vgic_irq_is_edge(dist, irq_num);
+	is_level = !is_edge;
+	state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);
+
+	/*
+	 * Only inject an interrupt if:
+	 * - level triggered and we change level
+	 * - edge triggered and we have a rising edge
+	 */
Quoted 8 lines

arch/arm/kvm/vgic.c int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) · line 912

Quoted 4 lines
arch/arm/kvm/vgic.c +4 -0
+	if ((is_level && !(state ^ level)) || (is_edge && (state || !level))) {
+		ret = false;
+		goto out;
+	}

Eek, more of the edge/level combo. Can this be be restructured so that we have vgic_update_{edge,level}_irq_state, which are called from here appropriately?

I'll have a look.

Thanks,

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#40 lore raw

Re: [PATCH v4 09/13] ARM: KVM: VGIC interrupt injection

[...]

Quoted 30 lines
Quoted 21 lines
Quoted 21 lines
arch/arm/kvm/vgic.c +21 -0
+
+static bool vgic_update_irq_state(struct kvm *kvm, int cpuid,
+                              unsigned int irq_num, bool level)
+{
+    struct vgic_dist *dist = &kvm->arch.vgic;
+    struct kvm_vcpu *vcpu;
+    int is_edge, is_level, state;
+    int enabled;
+    bool ret = true;
+
+    spin_lock(&dist->lock);
+
+    is_edge = vgic_irq_is_edge(dist, irq_num);
+    is_level = !is_edge;
+    state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq_num);
+
+    /*
+     * Only inject an interrupt if:
+     * - level triggered and we change level
+     * - edge triggered and we have a rising edge
+     */
Quoted 8 lines

arch/arm/kvm/vgic.c int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) · line 912

Quoted 4 lines
arch/arm/kvm/vgic.c +4 -0
+    if ((is_level && !(state ^ level)) || (is_edge && (state || !level))) {
+            ret = false;
+            goto out;
+    }

Eek, more of the edge/level combo. Can this be be restructured so that we have vgic_update_{edge,level}_irq_state, which are called from here appropriately?

Quoted 2 lines

I'll have a look.

oh, you're no fun anymore. That if statement is one of the funniest pieces of this code.

-Christoffer

#41 lore raw

Re: [PATCH v4 09/13] ARM: KVM: VGIC interrupt injection

In reply to Marc Zyngier
Quoted 95 lines · Marc Zyngier
Quoted 54 lines · Will Deacon
Quoted 53 lines · 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              |   29 +++++++++++++
 arch/arm/kvm/vgic.c             |   90 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 7229324..6e3d303 100644
@@ -241,6 +241,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(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);
@@ -271,6 +273,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+                                  const struct kvm_irq_level *irq)
+{
+    return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
     return 0;
arch/arm/kvm/arm.c +3 -0
index 3ac1aab..f43da01 100644
@@ -764,10 +764,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 
     switch (irq_type) {
     case KVM_ARM_IRQ_TYPE_CPU:
+            if (irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
             if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
                     return -EINVAL;
 
Quoted 10 lines

arch/arm/kvm/arm.c int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) · line 780

Quoted 8 lines
arch/arm/kvm/arm.c +7 -0
             return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+    case KVM_ARM_IRQ_TYPE_PPI:
+            if (!irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
+            if (irq_num < 16 || irq_num > 31)
+                    return -EINVAL;

It's our favourite two numbers again! :)

I already fixed a number of them. Probably missed this one though.

Quoted 17 lines
Quoted 17 lines
arch/arm/kvm/arm.c +11 -0
+
+            return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+    case KVM_ARM_IRQ_TYPE_SPI:
+            if (!irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
+            if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+                    return -EINVAL;
+
+            return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
     }
 
     return -EINVAL;
@@ -849,6 +870,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
     void __user *argp = (void __user *)arg;
 
Quoted 9 lines

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 878

Quoted 7 lines
arch/arm/kvm/arm.c +6 -0
     switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+    case KVM_CREATE_IRQCHIP: {
+            if (vgic_present)
+                    return kvm_vgic_create(kvm);
+            else
+                    return -EINVAL;

ENXIO? At least, that's what you use when setting the GIC addresses.

Quoted 5 lines

-EINVAL seems to be one of the values other archs are using. -ENXIO is not one of them for KVM_CREATE_IRQCHIP. Doesn't mean they are right, but for the sake of keeping userspace happy, I'm not really inclined to change this.

We don't have user space code relying on this, and EINVAL is misleading, so let's use ENXIO to be consistent with SET_DEVICE_ADDRESS. No error values are specified in the API docs, so we should use the most appropriate one.

You fix?

-Christoffer

#42 lore raw

Re: [PATCH v4 09/13] ARM: KVM: VGIC interrupt injection

Quoted 102 lines · Christoffer Dall
Quoted 95 lines · Marc Zyngier
Quoted 54 lines · Will Deacon
Quoted 53 lines · 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              |   29 +++++++++++++
 arch/arm/kvm/vgic.c             |   90 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
arch/arm/include/asm/kvm_vgic.h +8 -0
index 7229324..6e3d303 100644
@@ -241,6 +241,8 @@ struct kvm_exit_mmio;
 int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(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);
@@ -271,6 +273,12 @@ static inline void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu) {}
 
+static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid,
+                                  const struct kvm_irq_level *irq)
+{
+    return 0;
+}
+
 static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
 {
     return 0;
arch/arm/kvm/arm.c +3 -0
index 3ac1aab..f43da01 100644
@@ -764,10 +764,31 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level)
 
     switch (irq_type) {
     case KVM_ARM_IRQ_TYPE_CPU:
+            if (irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
             if (irq_num > KVM_ARM_IRQ_CPU_FIQ)
                     return -EINVAL;
 
Quoted 10 lines

arch/arm/kvm/arm.c int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) · line 780

Quoted 8 lines
arch/arm/kvm/arm.c +7 -0
             return vcpu_interrupt_line(vcpu, irq_num, level);
+#ifdef CONFIG_KVM_ARM_VGIC
+    case KVM_ARM_IRQ_TYPE_PPI:
+            if (!irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
+            if (irq_num < 16 || irq_num > 31)
+                    return -EINVAL;

It's our favourite two numbers again! :)

I already fixed a number of them. Probably missed this one though.

Quoted 17 lines
Quoted 17 lines
arch/arm/kvm/arm.c +11 -0
+
+            return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level);
+    case KVM_ARM_IRQ_TYPE_SPI:
+            if (!irqchip_in_kernel(kvm))
+                    return -ENXIO;
+
+            if (irq_num < 32 || irq_num > KVM_ARM_IRQ_GIC_MAX)
+                    return -EINVAL;
+
+            return kvm_vgic_inject_irq(kvm, 0, irq_num, level);
+#endif
     }
 
     return -EINVAL;
@@ -849,6 +870,14 @@ long kvm_arch_vm_ioctl(struct file *filp,
     void __user *argp = (void __user *)arg;
 
Quoted 9 lines

arch/arm/kvm/arm.c long kvm_arch_vm_ioctl(struct file *filp, · line 878

Quoted 7 lines
arch/arm/kvm/arm.c +6 -0
     switch (ioctl) {
+#ifdef CONFIG_KVM_ARM_VGIC
+    case KVM_CREATE_IRQCHIP: {
+            if (vgic_present)
+                    return kvm_vgic_create(kvm);
+            else
+                    return -EINVAL;

ENXIO? At least, that's what you use when setting the GIC addresses.

Quoted 5 lines

-EINVAL seems to be one of the values other archs are using. -ENXIO is not one of them for KVM_CREATE_IRQCHIP. Doesn't mean they are right, but for the sake of keeping userspace happy, I'm not really inclined to change this.

Quoted 6 lines

We don't have user space code relying on this, and EINVAL is misleading, so let's use ENXIO to be consistent with SET_DEVICE_ADDRESS. No error values are specified in the API docs, so we should use the most appropriate one.

You fix?

Yes, I'll fix it as part the whole vgic series.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#43 lore raw

PATCH v4 10/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/include/asm/kvm_arm.h |   12 +++++++
 arch/arm/kernel/asm-offsets.c  |   12 +++++++
 arch/arm/kvm/interrupts_head.S |   68 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+)
arch/arm/include/asm/kvm_arm.h +12 -0
index 246afd7..8f5dd22 100644
@@ -192,4 +192,16 @@
 #define HSR_EC_DABT	(0x24)
 #define HSR_EC_DABT_HYP	(0x25)
 
+/* GICH offsets */
+#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
+
 #endif /* __ARM_KVM_ARM_H__ */
arch/arm/kernel/asm-offsets.c +12 -0
index 95cab37..39b6221 100644
@@ -167,6 +167,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 +68 -0

80 lines not on this page · raw message

#44 lore raw

Re: [PATCH v4 10/13] ARM: KVM: VGIC control interface world switch

Quoted 21 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

---
 arch/arm/include/asm/kvm_arm.h |   12 +++++++
 arch/arm/kernel/asm-offsets.c  |   12 +++++++
 arch/arm/kvm/interrupts_head.S |   68 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+)
arch/arm/include/asm/kvm_arm.h +1 -0
index 246afd7..8f5dd22 100644
@@ -192,4 +192,16 @@
 #define HSR_EC_DABT	(0x24)
 #define HSR_EC_DABT_HYP	(0x25)
 
+/* GICH offsets */

arch/arm/include/asm/kvm_arm.h line 205

Quoted 10 lines
arch/arm/include/asm/kvm_arm.h +10 -0
+#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

Similar thing to the other new gic defines -- they're probably better off in gic.h

Quoted 32 lines
arch/arm/include/asm/kvm_arm.h +1 -0
+
 #endif /* __ARM_KVM_ARM_H__ */
arch/arm/kernel/asm-offsets.c +12 -0
index 95cab37..39b6221 100644
@@ -167,6 +167,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 +0 -0
index 2ac8b4a..c2423d8 100644
@@ -341,6 +341,45 @@
  * @vcpup: Register pointing to VCPU struct
  */

arch/arm/kvm/interrupts_head.S line 352

Quoted 10 lines
arch/arm/kvm/interrupts_head.S +9 -0
 .macro save_vgic_state	vcpup
+#ifdef CONFIG_KVM_ARM_VGIC
+	/* Get VGIC VCTRL base into r2 */
+	ldr	r2, [\vcpup, #VCPU_KVM]
+	ldr	r2, [r2, #KVM_VGIC_VCTRL]
+	cmp	r2, #0
+	beq	2f
+
+	/* Compute the address of struct vgic_cpu */
+	add	r11, \vcpup, #VCPU_VGIC_CPU

Given that we're dealing with constants, it would be more efficient to express this addition as part of the immediate offset and let gas spit out the final computed address for the stores below.

Quoted 21 lines
arch/arm/kvm/interrupts_head.S +21 -0
+
+	/* Save all interesting registers */
+	ldr	r3, [r2, #GICH_HCR]
+	ldr	r4, [r2, #GICH_VMCR]
+	ldr	r5, [r2, #GICH_MISR]
+	ldr	r6, [r2, #GICH_EISR0]
+	ldr	r7, [r2, #GICH_EISR1]
+	ldr	r8, [r2, #GICH_ELRSR0]
+	ldr	r9, [r2, #GICH_ELRSR1]
+	ldr	r10, [r2, #GICH_APR]
+
+	str	r3, [r11, #VGIC_CPU_HCR]
+	str	r4, [r11, #VGIC_CPU_VMCR]
+	str	r5, [r11, #VGIC_CPU_MISR]
+	str	r6, [r11, #VGIC_CPU_EISR]
+	str	r7, [r11, #(VGIC_CPU_EISR + 4)]
+	str	r8, [r11, #VGIC_CPU_ELRSR]
+	str	r9, [r11, #(VGIC_CPU_ELRSR + 4)]
+	str	r10, [r11, #VGIC_CPU_APR]
+
+	/* Save list registers */

arch/arm/kvm/interrupts_head.S line 383

Quoted 10 lines
arch/arm/kvm/interrupts_head.S +9 -0
+	add	r2, r2, #GICH_LR0
+	add	r3, r11, #VGIC_CPU_LR
+	ldr	r4, [r11, #VGIC_CPU_NR_LR]
+1:	ldr	r6, [r2], #4
+	str	r6, [r3], #4
+	subs	r4, r4, #1
+	bne	1b
+2:
+#endif
 .endm

Will

#45 lore raw

Re: [PATCH v4 10/13] ARM: KVM: VGIC control interface world switch

Quoted 21 lines · Will Deacon
Quoted 20 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

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

---
 arch/arm/include/asm/kvm_arm.h |   12 +++++++
 arch/arm/kernel/asm-offsets.c  |   12 +++++++
 arch/arm/kvm/interrupts_head.S |   68 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+)
arch/arm/include/asm/kvm_arm.h +0 -0
index 246afd7..8f5dd22 100644
@@ -192,4 +192,16 @@
 #define HSR_EC_DABT	(0x24)
 #define HSR_EC_DABT_HYP	(0x25)
 
Quoted 14 lines

arch/arm/include/asm/kvm_arm.h line 205

Quoted 11 lines
arch/arm/include/asm/kvm_arm.h +11 -0
+/* GICH offsets */
+#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

Similar thing to the other new gic defines -- they're probably better off in gic.h

Agreed.

Quoted 38 lines
Quoted 38 lines
arch/arm/include/asm/kvm_arm.h +1 -0
+
 #endif /* __ARM_KVM_ARM_H__ */
arch/arm/kernel/asm-offsets.c +12 -0
index 95cab37..39b6221 100644
@@ -167,6 +167,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 +5 -0
index 2ac8b4a..c2423d8 100644
@@ -341,6 +341,45 @@
  * @vcpup: Register pointing to VCPU struct
  */
 .macro save_vgic_state	vcpup
+#ifdef CONFIG_KVM_ARM_VGIC
+	/* Get VGIC VCTRL base into r2 */
+	ldr	r2, [\vcpup, #VCPU_KVM]
+	ldr	r2, [r2, #KVM_VGIC_VCTRL]
+	cmp	r2, #0
Quoted 8 lines

arch/arm/kvm/interrupts_head.S line 352

Quoted 4 lines
arch/arm/kvm/interrupts_head.S +4 -0
+	beq	2f
+
+	/* Compute the address of struct vgic_cpu */
+	add	r11, \vcpup, #VCPU_VGIC_CPU

Given that we're dealing with constants, it would be more efficient to express this addition as part of the immediate offset and let gas spit out the final computed address for the stores below.

We had that for a while, and with the kvm_vcpu structure growing, we ended up having fields out of the reach of an immediate offset. Is kvm_vcpu too big? Yes.

Quoted 34 lines
Quoted 21 lines
arch/arm/kvm/interrupts_head.S +21 -0
+
+	/* Save all interesting registers */
+	ldr	r3, [r2, #GICH_HCR]
+	ldr	r4, [r2, #GICH_VMCR]
+	ldr	r5, [r2, #GICH_MISR]
+	ldr	r6, [r2, #GICH_EISR0]
+	ldr	r7, [r2, #GICH_EISR1]
+	ldr	r8, [r2, #GICH_ELRSR0]
+	ldr	r9, [r2, #GICH_ELRSR1]
+	ldr	r10, [r2, #GICH_APR]
+
+	str	r3, [r11, #VGIC_CPU_HCR]
+	str	r4, [r11, #VGIC_CPU_VMCR]
+	str	r5, [r11, #VGIC_CPU_MISR]
+	str	r6, [r11, #VGIC_CPU_EISR]
+	str	r7, [r11, #(VGIC_CPU_EISR + 4)]
+	str	r8, [r11, #VGIC_CPU_ELRSR]
+	str	r9, [r11, #(VGIC_CPU_ELRSR + 4)]
+	str	r10, [r11, #VGIC_CPU_APR]
+
+	/* Save list registers */

arch/arm/kvm/interrupts_head.S line 383

Quoted 10 lines
arch/arm/kvm/interrupts_head.S +9 -0
+	add	r2, r2, #GICH_LR0
+	add	r3, r11, #VGIC_CPU_LR
+	ldr	r4, [r11, #VGIC_CPU_NR_LR]
+1:	ldr	r6, [r2], #4
+	str	r6, [r3], #4
+	subs	r4, r4, #1
+	bne	1b
+2:
+#endif
 .endm

Will

Signature · 1 line
Jazz is not dead. It just smells funny...
#46 lore raw

PATCH v4 11/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.

---
 arch/arm/include/asm/kvm_vgic.h |   11 ++
 arch/arm/kvm/arm.c              |   14 ++
 arch/arm/kvm/vgic.c             |  237 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+), 4 deletions(-)
arch/arm/include/asm/kvm_vgic.h +11 -0
index 6e3d303..a8e7a93 100644
@@ -154,6 +154,7 @@ static inline void vgic_bytemap_set_irq_val(struct vgic_bytemap *x,
 struct vgic_dist {
 #ifdef CONFIG_KVM_ARM_VGIC
 	spinlock_t		lock;
+	bool			ready;
 
 	/* Virtual control interface mapping */
 	void __iomem		*vctrl_base;
@@ -239,6 +240,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);
+void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
@@ -248,6 +253,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)
 {
@@ -294,6 +300,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 +12 -2
index f43da01..a633d9d 100644
@@ -187,6 +187,8 @@ int kvm_dev_ioctl_check_extension(long ext)
 	switch (ext) {
 #ifdef CONFIG_KVM_ARM_VGIC
 	case KVM_CAP_IRQCHIP:
+		r = vgic_present;
+		break;
 #endif
 	case KVM_CAP_USER_MEMORY:
 	case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
@@ -623,6 +625,14 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	if (unlikely(vcpu->arch.target < 0))
 		return -ENOEXEC;
 
+	/* Initalize the VGIC before running a vcpu the first time on this VM */
+	if (unlikely(irqchip_in_kernel(vcpu->kvm) &&
+		     !vgic_initialized(vcpu->kvm))) {
+		ret = kvm_vgic_init(vcpu->kvm);
+		if (ret)
+			return ret;
+	}
+
 	if (run->exit_reason == KVM_EXIT_MMIO) {
 		ret = kvm_handle_mmio_return(vcpu, vcpu->run);
 		if (ret)
@@ -1024,8 +1034,8 @@ static int init_hyp_mode(void)
 	 * Init HYP view of VGIC
 	 */
 	err = kvm_vgic_hyp_init();
-	if (err)
-		goto out_free_mappings;
+	if (!err)
+		vgic_present = true;
 
 	return 0;
 out_free_vfp:
arch/arm/kvm/vgic.c +235 -2

266 lines not on this page · raw message

#47 lore raw

Re: [PATCH v4 11/13] ARM: KVM: VGIC initialisation code

Quoted 49 lines · 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.

---
 arch/arm/include/asm/kvm_vgic.h |   11 ++
 arch/arm/kvm/arm.c              |   14 ++
 arch/arm/kvm/vgic.c             |  237 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+), 4 deletions(-)
arch/arm/include/asm/kvm_vgic.h +6 -0
index 6e3d303..a8e7a93 100644
@@ -154,6 +154,7 @@ static inline void vgic_bytemap_set_irq_val(struct vgic_bytemap *x,
 struct vgic_dist {
 #ifdef CONFIG_KVM_ARM_VGIC
        spinlock_t              lock;
+       bool                    ready;
 
        /* Virtual control interface mapping */
        void __iomem            *vctrl_base;
@@ -239,6 +240,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);
+void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_from_cpu(struct kvm_vcpu *vcpu);
 int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
@@ -248,6 +253,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)
 {

arch/arm/include/asm/kvm_vgic.h static inline int irqchip_in_kernel(struct kvm *kvm) · line 308

Quoted 9 lines
arch/arm/include/asm/kvm_vgic.h +5 -0
@@ -294,6 +300,11 @@ static inline int irqchip_in_kernel(struct kvm *kvm)
 {
        return 0;
 }
+
+static inline bool vgic_initialized(struct kvm *kvm)
+{
+       return true;
+}
 #endif

Shouldn't this return false?

Quoted 16 lines
arch/arm/include/asm/kvm_vgic.h +0 -0
 #endif
arch/arm/kvm/arm.c +2 -0
index f43da01..a633d9d 100644
@@ -187,6 +187,8 @@ int kvm_dev_ioctl_check_extension(long ext)
        switch (ext) {
 #ifdef CONFIG_KVM_ARM_VGIC
        case KVM_CAP_IRQCHIP:
+               r = vgic_present;
+               break;
 #endif
        case KVM_CAP_USER_MEMORY:
        case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 628

Quoted 4 lines
arch/arm/kvm/arm.c +1 -0
@@ -623,6 +625,14 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        if (unlikely(vcpu->arch.target < 0))
                return -ENOEXEC;
 
+       /* Initalize the VGIC before running a vcpu the first time on this VM */

Initialize

arch/arm/kvm/arm.c int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) · line 629

Quoted 1 line
arch/arm/kvm/arm.c +1 -0
+       if (unlikely(irqchip_in_kernel(vcpu->kvm) &&

Why unlikely? I'd just drop the hint.

Quoted 10 lines
arch/arm/kvm/arm.c +6 -0
+                    !vgic_initialized(vcpu->kvm))) {
+               ret = kvm_vgic_init(vcpu->kvm);
+               if (ret)
+                       return ret;
+       }
+
        if (run->exit_reason == KVM_EXIT_MMIO) {
                ret = kvm_handle_mmio_return(vcpu, vcpu->run);
                if (ret)

arch/arm/kvm/arm.c static int init_hyp_mode(void) · line 1040

Quoted 9 lines
arch/arm/kvm/arm.c +2 -2
@@ -1024,8 +1034,8 @@ static int init_hyp_mode(void)
         * Init HYP view of VGIC
         */
        err = kvm_vgic_hyp_init();
-       if (err)
-               goto out_free_mappings;
+       if (!err)
+               vgic_present = true;
 
        return 0;

Is that an intended change in behaviour? If kvm_vgic_hyp_init fails, you'll return 0...

Quoted 21 lines
arch/arm/kvm/arm.c +0 -0
 out_free_vfp:
arch/arm/kvm/vgic.c +7 -0
index 70040bb..415ddb8 100644
@@ -20,7 +20,14 @@
 #include <linux/kvm_host.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
 #include <asm/kvm_emulate.h>
+#include <asm/hardware/gic.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_mmu.h>
 
 /*
  * How the whole thing works (courtesy of Christoffer Dall):

arch/arm/kvm/vgic.c line 69

Quoted 5 lines
arch/arm/kvm/vgic.c +1 -1
@@ -59,11 +66,18 @@
  */
 
 #define VGIC_ADDR_UNDEF                (-1)
-#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == (typeof(_x))VGIC_ADDR_UNDEF)
+#define IS_VGIC_ADDR_UNDEF(_x)  ((_x) == VGIC_ADDR_UNDEF)

Huh?

Quoted 15 lines
arch/arm/kvm/vgic.c +7 -0
 #define VGIC_DIST_SIZE         0x1000
 #define VGIC_CPU_SIZE          0x2000
 
+/* Physical address of vgic virtual cpu interface */
+static phys_addr_t vgic_vcpu_base;
+
+/* Virtual control interface base address */
+static void __iomem *vgic_vctrl_base;
+
+static struct device_node *vgic_node;
 
 #define ACCESS_READ_VALUE      (1 << 0)
 #define ACCESS_READ_RAZ                (0 << 0)

arch/arm/kvm/vgic.c bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi · line 545

Quoted 6 lines
arch/arm/kvm/vgic.c +1 -1
@@ -527,7 +541,7 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exi
 
        if (!irqchip_in_kernel(vcpu->kvm) ||
            mmio->phys_addr < base ||
-           (mmio->phys_addr + mmio->len) > (base + dist->vgic_dist_size))
+           (mmio->phys_addr + mmio->len) > (base + VGIC_DIST_SIZE))
                return false;

Again, this is an odd hunk. It looks like you've accidentally got some older context lying around in this patch.

Quoted 5 lines
arch/arm/kvm/vgic.c +0 -0
        range = find_matching_range(vgic_ranges, mmio, base);
@@ -957,6 +971,225 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
        return 0;
 }

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 982

Quoted 10 lines
arch/arm/kvm/vgic.c +9 -0
 
+static irqreturn_t vgic_maintenance_handler(int irq, void *data)
+{
+       struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)data;
+       struct vgic_dist *dist;
+       struct vgic_cpu *vgic_cpu;
+
+       if (WARN(!vcpu,
+                "VGIC interrupt on CPU %d with no vcpu\n", smp_processor_id()))
+               return IRQ_HANDLED;

We need to get an answer about the potential delaying of this interrupt, otherwise this might kick more often that we'd like.

Quoted 8 lines
arch/arm/kvm/vgic.c +8 -0
+
+       vgic_cpu = &vcpu->arch.vgic_cpu;
+       dist = &vcpu->kvm->arch.vgic;
+       kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr);
+
+       /*
+        * We do not need to take the distributor lock here, since the only
+        * action we perform is clearing the irq_active_bit for an EOIed

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1000

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+        * level interrupt.  There is a potential race with
+        * the queuing of an interrupt in __kvm_sync_to_cpu(), where we check
+        * if the interrupt is already active. Two possibilities:
+        *
+        * - The queuing is occuring on the same vcpu: cannot happen, as we're
+        *   already in the context of this vcpu, and executing the handler
+        * - The interrupt has been migrated to another vcpu, and we ignore
+        *   this interrupt for this run. Big deal. It is still pending though,
+        *   and will get considered when this vcpu exits.
+        */

We've already discussed this in an earlier thread.

Quoted 8 lines
arch/arm/kvm/vgic.c +8 -0
+       if (vgic_cpu->vgic_misr & VGIC_MISR_EOI) {
+               /*
+                * Some level interrupts have been EOIed. Clear their
+                * active bit.
+                */
+               int lr, irq;
+
+               for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr,

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1018

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+                                vgic_cpu->nr_lr) {
+                       irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+                       vgic_bitmap_set_irq_val(&dist->irq_active,
+                                               vcpu->vcpu_id, irq, 0);
+                       vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
+                       writel_relaxed(vgic_cpu->vgic_lr[lr],
+                                      dist->vctrl_base + GICH_LR0 + (lr << 2));
+
+                       /* Any additionnal pending interrupt? */

additional

Quoted 8 lines
arch/arm/kvm/vgic.c +8 -0
+                       if (vgic_bitmap_get_irq_val(&dist->irq_state,
+                                                   vcpu->vcpu_id, irq)) {
+                               set_bit(irq, vcpu->arch.vgic_cpu.pending);
+                               set_bit(vcpu->vcpu_id,
+                                       &dist->irq_pending_on_cpu);
+                       } else {
+                               clear_bit(irq, vgic_cpu->pending);
+                       }

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1036

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+               }
+       }
+
+       if (vgic_cpu->vgic_misr & VGIC_MISR_U) {
+               vgic_cpu->vgic_hcr &= ~VGIC_HCR_UIE;
+               writel_relaxed(vgic_cpu->vgic_hcr, dist->vctrl_base + GICH_HCR);
+       }
+
+       return IRQ_HANDLED;
+}

I wonder whether we could instead simplify the irq handler so it just sets a flag indicating that the list registers need updating, then we pick that up on the resume path and keep all the work in one place. Hard to tell.

Quoted 9 lines
arch/arm/kvm/vgic.c +9 -0
+
+void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
+{
+       struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+       struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+       u32 reg;
+       int i;
+
+       if (!irqchip_in_kernel(vcpu->kvm))

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1055

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+               return;
+
+       for (i = 0; i < VGIC_NR_IRQS; i++) {
+               if (i < 16)
+                       vgic_bitmap_set_irq_val(&dist->irq_enabled,
+                                               vcpu->vcpu_id, i, 1);
+               if (i < 32)
+                       vgic_bitmap_set_irq_val(&dist->irq_cfg,
+                                               vcpu->vcpu_id, i, 1);
+

Usual comments here :)

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1064

Quoted 9 lines
arch/arm/kvm/vgic.c +9 -0
+               vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY;
+       }
+
+       BUG_ON(!vcpu->kvm->arch.vgic.vctrl_base);
+       reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VTR);
+       vgic_cpu->nr_lr = (reg & 0x1f) + 1;
+
+       reg = readl_relaxed(vcpu->kvm->arch.vgic.vctrl_base + GICH_VMCR);
+       vgic_cpu->vgic_vmcr = reg | (0x1f << 27); /* Priority */

Why isn't this defined with the other registers?

Quoted 15 lines
arch/arm/kvm/vgic.c +15 -0
+
+       vgic_cpu->vgic_hcr |= VGIC_HCR_EN; /* Get the show on the road... */
+}
+
+static void vgic_init_maintenance_interrupt(void *info)
+{
+       unsigned int *irqp = info;
+
+       enable_percpu_irq(*irqp, 0);
+}
+
+int kvm_vgic_hyp_init(void)
+{
+       int ret;
+       unsigned int irq;

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1089

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       struct resource vctrl_res;
+       struct resource vcpu_res;
+
+       vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic");
+       if (!vgic_node)
+               return -ENODEV;
+
+       irq = irq_of_parse_and_map(vgic_node, 0);
+       if (!irq)
+               return -ENXIO;

Don't you need to put of_node_put to decrement the refcount before returning?

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1095

Quoted 6 lines
arch/arm/kvm/vgic.c +6 -0
+
+       ret = request_percpu_irq(irq, vgic_maintenance_handler,
+                                "vgic", kvm_get_running_vcpus());
+       if (ret) {
+               kvm_err("Cannot register interrupt %d\n", irq);
+               return ret;

Likewise (and similarly throughout this function).

Quoted 15 lines
arch/arm/kvm/vgic.c +15 -0
+       }
+
+       ret = of_address_to_resource(vgic_node, 2, &vctrl_res);
+       if (ret) {
+               kvm_err("Cannot obtain VCTRL resource\n");
+               goto out_free_irq;
+       }
+
+       vgic_vctrl_base = of_iomap(vgic_node, 2);
+       if (!vgic_vctrl_base) {
+               kvm_err("Cannot ioremap VCTRL\n");
+               ret = -ENOMEM;
+               goto out_free_irq;
+       }
+

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1120

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+       ret = create_hyp_io_mappings(vgic_vctrl_base,
+                                    vgic_vctrl_base + resource_size(&vctrl_res),
+                                    vctrl_res.start);
+       if (ret) {
+               kvm_err("Cannot map VCTRL into hyp\n");
+               goto out_unmap;
+       }
+
+       kvm_info("%s@%llx IRQ%d\n", vgic_node->name, vctrl_res.start, irq);
+       on_each_cpu(vgic_init_maintenance_interrupt, &irq, 1);

What if all your CPUs aren't online at this point? Do you need a hotplug notifier to re-enable the PPI? (you should try hotplug on the host actually, it could be hilarious fun :)

Quoted 57 lines
arch/arm/kvm/vgic.c +57 -0
+
+       if (of_address_to_resource(vgic_node, 3, &vcpu_res)) {
+               kvm_err("Cannot obtain VCPU resource\n");
+               ret = -ENXIO;
+               goto out_unmap;
+       }
+       vgic_vcpu_base = vcpu_res.start;
+
+       return 0;
+
+out_unmap:
+       iounmap(vgic_vctrl_base);
+out_free_irq:
+       free_percpu_irq(irq, kvm_get_running_vcpus());
+
+       return ret;
+}
+
+int kvm_vgic_init(struct kvm *kvm)
+{
+       int ret = 0, i;
+
+       mutex_lock(&kvm->lock);
+
+       if (vgic_initialized(kvm))
+               goto out;
+
+       if (IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_dist_base) ||
+           IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_cpu_base)) {
+               kvm_err("Need to set vgic cpu and dist addresses first\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base,
+                                   vgic_vcpu_base, VGIC_CPU_SIZE);
+       if (ret) {
+               kvm_err("Unable to remap VGIC CPU to VCPU\n");
+               goto out;
+       }
+
+       for (i = 32; i < VGIC_NR_IRQS; i += 4)
+               vgic_set_target_reg(kvm, 0, i);
+
+       kvm->arch.vgic.ready = true;
+out:
+       mutex_unlock(&kvm->lock);
+       return ret;
+}
+
+int kvm_vgic_create(struct kvm *kvm)
+{
+       int ret;
+
+       mutex_lock(&kvm->lock);
+
+       if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) {

arch/arm/kvm/vgic.c int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, · line 1187

Quoted 10 lines
arch/arm/kvm/vgic.c +10 -0
+               ret = -EEXIST;
+               goto out;
+       }
+
+       spin_lock_init(&kvm->arch.vgic.lock);
+       kvm->arch.vgic.vctrl_base = vgic_vctrl_base;
+       kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+       kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+
+       ret = 0;

Might as well assign this when it's declared.

Will

#48 lore raw

PATCH v4 12/13 ARM: KVM: vgic: reduce the number of vcpu kick

In reply to Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +10 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)
+
 #else
 static inline int kvm_vgic_hyp_init(void)
 {
@@ -305,6 +310,11 @@ static inline bool vgic_initialized(struct kvm *kvm)
 {
 	return true;
 }
+
+static inline int vgic_active_irq(struct kvm_vcpu *vcpu)
+{
+	return 0;
+}
 #endif
 
 #endif
arch/arm/kvm/arm.c +9 -1
index a633d9d..1716f12 100644
@@ -94,7 +94,15 @@ int kvm_arch_hardware_enable(void *garbage)
 
 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 {
-	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+	if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+		if (vgic_active_irq(vcpu) &&
+		    cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+			return 0;
+
+		return 1;
+	}
+
+	return 0;
 }
 
 void kvm_arch_hardware_disable(void *garbage)
arch/arm/kvm/vgic.c +8 -2
index 415ddb8..146de1d 100644
@@ -705,8 +705,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 		kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
 		BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
-		if (is_level)
+		if (is_level) {
 			vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+			atomic_inc(&vgic_cpu->irq_active_count);
+		}
 		return true;
 	}
 
@@ -718,8 +720,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 
 	kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
 	vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
-	if (is_level)
+	if (is_level) {
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+		atomic_inc(&vgic_cpu->irq_active_count);
+	}
 
 	vgic_cpu->vgic_irq_lr_map[irq] = lr;
 	clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
@@ -1011,6 +1015,8 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
 
 			vgic_bitmap_set_irq_val(&dist->irq_active,
 						vcpu->vcpu_id, irq, 0);
+			atomic_dec(&vgic_cpu->irq_active_count);
+			smp_mb();
 			vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
 			writel_relaxed(vgic_cpu->vgic_lr[lr],
 				       dist->vctrl_base + GICH_LR0 + (lr << 2));
#49 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 32 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 260

Quoted 4 lines
arch/arm/include/asm/kvm_vgic.h +1 -0
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)

When is the atomic_t initialised to zero? I can only see increments.

Quoted 24 lines
arch/arm/include/asm/kvm_vgic.h +6 -0
+
 #else
 static inline int kvm_vgic_hyp_init(void)
 {
@@ -305,6 +310,11 @@ static inline bool vgic_initialized(struct kvm *kvm)
 {
 	return true;
 }
+
+static inline int vgic_active_irq(struct kvm_vcpu *vcpu)
+{
+	return 0;
+}
 #endif
 
 #endif
arch/arm/kvm/arm.c +0 -0
index a633d9d..1716f12 100644
@@ -94,7 +94,15 @@ int kvm_arch_hardware_enable(void *garbage)
 
 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 {

arch/arm/kvm/arm.c int kvm_arch_hardware_enable(void *garbage) · line 105

Quoted 10 lines
arch/arm/kvm/arm.c +9 -1
-	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+	if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+		if (vgic_active_irq(vcpu) &&
+		    cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+			return 0;
+
+		return 1;
+	}
+
+	return 0;

That's pretty nasty... why don't you check if there's an active interrupt before trying to change the vcpu mode? That way, you can avoid the double cmpxchg.

Quoted 32 lines
arch/arm/kvm/arm.c +0 -0
 }
 
 void kvm_arch_hardware_disable(void *garbage)
arch/arm/kvm/vgic.c +6 -2
index 415ddb8..146de1d 100644
@@ -705,8 +705,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 		kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
 		BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
-		if (is_level)
+		if (is_level) {
 			vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+			atomic_inc(&vgic_cpu->irq_active_count);
+		}
 		return true;
 	}
 
@@ -718,8 +720,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 
 	kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
 	vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
-	if (is_level)
+	if (is_level) {
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+		atomic_inc(&vgic_cpu->irq_active_count);
+	}
 
 	vgic_cpu->vgic_irq_lr_map[irq] = lr;
 	clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);

arch/arm/kvm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1019

Quoted 5 lines
arch/arm/kvm/vgic.c +2 -0
@@ -1011,6 +1015,8 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
 
 			vgic_bitmap_set_irq_val(&dist->irq_active,
 						vcpu->vcpu_id, irq, 0);
+			atomic_dec(&vgic_cpu->irq_active_count);
+			smp_mb();

If you actually need this, try smp_mb__after_atomic_dec although of course I'd like to know why it's required :)

Will

#50 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 
Quoted 7 lines

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 260

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +1 -0
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)

When is the atomic_t initialised to zero? I can only see increments.

I'd question whether an atomic type is correct for this; the only protection that it's offering is to ensure that the atomic increment and decrement occur atomically - there's nothing else that they're doing in this code.

If those atomic increments and decrements are occuring beneath a common lock, then using atomic types is just mere code obfuscation.

For example, I'd like to question the correctness of this:

+ if (vgic_active_irq(vcpu) &&
+ cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)

What if vgic_active_irq() reads the atomic type, immediately after it gets decremented to zero before the cmpxchg() is executed? Would that be a problem?

If yes, yet again this illustrates why the use of atomic types leads people down the path of believing that their code somehow becomes magically safe through the use of this smoke-screen. IMHO, every use of atomic_t must be questioned and carefully analysed before it gets into the kernel - many are buggy through assumptions that atomic_t buys you something magic.

#51 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 41 lines · Russell King - ARM Linux
Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 
Quoted 7 lines

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 260

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +1 -0
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)

When is the atomic_t initialised to zero? I can only see increments.

Quoted 7 lines

I'd question whether an atomic type is correct for this; the only protection that it's offering is to ensure that the atomic increment and decrement occur atomically - there's nothing else that they're doing in this code.

If those atomic increments and decrements are occuring beneath a common lock, then using atomic types is just mere code obfuscation.

No, they occur on code paths that do not have a common lock (one of them being an interrupt handler). This may change though, after one comment Will made earlier (the thing about delayed interrupts).

If these two code sections become mutually exclusive, then indeed there will be no point in having an atomic type anymore.

Quoted 8 lines

For example, I'd like to question the correctness of this:

arch/arm/kvm/arm.c +2 -0
+               if (vgic_active_irq(vcpu) &&
+                   cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)

What if vgic_active_irq() reads the atomic type, immediately after it gets decremented to zero before the cmpxchg() is executed? Would that be a problem?

I do not think so. If the value gets decremented, it means we took a maintenance interrupt, which means we exited the guest at some point. Two possibilities:

- We're not in guest mode anymore (vcpu->mode = OUTSIDE_GUEST_MODE), and cmpxchg will fail, hence signaling the guest to reload its state. This is not needed (the guest will reload its state anyway), but doesn't cause any harm.

- We're back into the guest (vcpu->mode = IN_GUEST_MODE), and cmpxchg will fail as well, triggering a reload which is needed this time.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#52 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 66 lines · Marc Zyngier
Quoted 41 lines · Russell King - ARM Linux
Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 
Quoted 7 lines

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 260

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +1 -0
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)

When is the atomic_t initialised to zero? I can only see increments.

Quoted 7 lines

I'd question whether an atomic type is correct for this; the only protection that it's offering is to ensure that the atomic increment and decrement occur atomically - there's nothing else that they're doing in this code.

If those atomic increments and decrements are occuring beneath a common lock, then using atomic types is just mere code obfuscation.

No, they occur on code paths that do not have a common lock (one of them being an interrupt handler). This may change though, after one comment Will made earlier (the thing about delayed interrupts).

If these two code sections become mutually exclusive, then indeed there will be no point in having an atomic type anymore.

Quoted 8 lines

For example, I'd like to question the correctness of this:

arch/arm/kvm/arm.c +2 -0
+               if (vgic_active_irq(vcpu) &&
+                   cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)

What if vgic_active_irq() reads the atomic type, immediately after it gets decremented to zero before the cmpxchg() is executed? Would that be a problem?

Quoted 8 lines

I do not think so. If the value gets decremented, it means we took a maintenance interrupt, which means we exited the guest at some point. Two possibilities:

- We're not in guest mode anymore (vcpu->mode = OUTSIDE_GUEST_MODE), and cmpxchg will fail, hence signaling the guest to reload its state. This is not needed (the guest will reload its state anyway), but doesn't cause any harm.

What is the relative ordering of the atomic decrement and setting vcpu->mode to be OUTSIDE_GUEST_MODE ? Is there a window where we have decremented this atomic type but vcpu->mode is still set to IN_GUEST_MODE.

Quoted 2 lines

- We're back into the guest (vcpu->mode = IN_GUEST_MODE), and cmpxchg will fail as well, triggering a reload which is needed this time.

Well, the whole code looks really weird to me, especially that:

+ if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+ if (vgic_active_irq(vcpu) &&
+ cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+ return 0;
+
+ return 1;
+ }

I've no idea what kvm_vcpu_exiting_guest_mode() is (it doesn't exist in any tree I have access to)...

In any case, look at the version I converted to spinlocks and see whether you think the code looks reasonable in that form. If it doesn't then it isn't reasonable in atomic types either.

#53 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 71 lines · Russell King - ARM Linux
Quoted 70 lines · Marc Zyngier
Quoted 41 lines · Russell King - ARM Linux
Quoted 32 lines · Will Deacon
Quoted 31 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

If we have level interrupts already programmed to fire on a vcpu, there is no reason to kick it after injecting a new interrupt, as we're guaranteed that we'll exit when the level interrupt will be EOId (VGIC_LR_EOI is set).

The exit will force a reload of the VGIC, injecting the new interrupts.

---
 arch/arm/include/asm/kvm_vgic.h |   10 ++++++++++
 arch/arm/kvm/arm.c              |   10 +++++++++-
 arch/arm/kvm/vgic.c             |   10 ++++++++--
 3 files changed, 27 insertions(+), 3 deletions(-)
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;
 #endif
 };
 
Quoted 7 lines

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 260

Quoted 5 lines
arch/arm/include/asm/kvm_vgic.h +1 -0
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)

When is the atomic_t initialised to zero? I can only see increments.

Quoted 7 lines

I'd question whether an atomic type is correct for this; the only protection that it's offering is to ensure that the atomic increment and decrement occur atomically - there's nothing else that they're doing in this code.

If those atomic increments and decrements are occuring beneath a common lock, then using atomic types is just mere code obfuscation.

No, they occur on code paths that do not have a common lock (one of them being an interrupt handler). This may change though, after one comment Will made earlier (the thing about delayed interrupts).

If these two code sections become mutually exclusive, then indeed there will be no point in having an atomic type anymore.

Quoted 8 lines

For example, I'd like to question the correctness of this:

arch/arm/kvm/arm.c +2 -0
+               if (vgic_active_irq(vcpu) &&
+                   cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)

What if vgic_active_irq() reads the atomic type, immediately after it gets decremented to zero before the cmpxchg() is executed? Would that be a problem?

I do not think so. If the value gets decremented, it means we took a maintenance interrupt, which means we exited the guest at some point. Two possibilities:

Quoted 8 lines
Quoted 4 lines

- We're not in guest mode anymore (vcpu->mode = OUTSIDE_GUEST_MODE), and cmpxchg will fail, hence signaling the guest to reload its state. This is not needed (the guest will reload its state anyway), but doesn't cause any harm.

What is the relative ordering of the atomic decrement and setting vcpu->mode to be OUTSIDE_GUEST_MODE ? Is there a window where we have decremented this atomic type but vcpu->mode is still set to IN_GUEST_MODE.

OUTSIDE_GUEST_MODE always occurs first, while interrupts are still masked in SVC. We then unmask the interrupts, causing the maintenance interrupt to be handled. Only this handler causes the active count to be decremented.

Quoted 13 lines
Quoted 2 lines

- We're back into the guest (vcpu->mode = IN_GUEST_MODE), and cmpxchg will fail as well, triggering a reload which is needed this time.

Well, the whole code looks really weird to me, especially that:

arch/arm/kvm/arm.c +7 -0
+       if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+               if (vgic_active_irq(vcpu) &&
+                   cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+                       return 0;
+
+               return 1;
+       }
Quoted 2 lines

I've no idea what kvm_vcpu_exiting_guest_mode() is (it doesn't exist in any tree I have access to)...

You should find it in include/linux/kvm_host.h. It reads:
static inline int kvm_vcpu_exiting_guest_mode(struct kvm_vcpu *vcpu)

{
        return cmpxchg(&vcpu->mode, IN_GUEST_MODE, EXITING_GUEST_MODE);
}

Admittedly, the whole sequence should be rewritten to be clearer. What it does is "If we're running a guest and there is no active interrupt, then kick the guest".

It probably means the above code should read:

if (!vgic_active_irq(vcpu))

        return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;

return 0;

Quoted 3 lines

In any case, look at the version I converted to spinlocks and see whether you think the code looks reasonable in that form. If it doesn't then it isn't reasonable in atomic types either.

I had a look, and I don't find it problematic.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...
#54 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

Quoted 3 lines · Marc Zyngier

Admittedly, the whole sequence should be rewritten to be clearer. What it does is "If we're running a guest and there is no active interrupt, then kick the guest".

On the whole this entire thing should be written clearer; from the explanations you've given it seems that the only reason this code works is because you're relying on several behaviours all coming together to achieve the right result - which makes for fragile code.

You're partly relying on atomic types to ensure that the increment and decrement happen exclusively. You're then relying on a combination of IRQ protection and cmpxchg() to ensure that the non-atomic read of the atomic type won't be a problem.

This doesn't inspire confidence, and I have big concerns over whether this code will still be understandable in a number of years time.

And I still wonder how safe this is even with your explanations. IRQ disabling only works for the local CPU core so I still have questions over this wrt a SMP host OS.

#55 lore raw

Re: [PATCH v4 12/13] ARM: KVM: vgic: reduce the number of vcpu kick

In reply to Christoffer Dall

For the sake of public education, let me rewrite this patch a bit to illustrate why atomic_t's are bad, and then people can review this instead.

Every change I've made here is functionally equivalent to the behaviour of the atomic type; I have not added any new bugs here that aren't present in the original code.

It is my hope that through education like this, people will see that atomic types have no magic properties, and their use does not make code automatically race free and correct; in fact, the inappropriate use of atomic types is pure obfuscation and causes confusion.

arch/arm/include/asm/kvm_vgic.h struct vgic_cpu { · line 220

Quoted 11 lines · Christoffer Dall
arch/arm/include/asm/kvm_vgic.h +3 -0
index a8e7a93..7d2662c 100644
@@ -215,6 +215,9 @@ struct vgic_cpu {
 	u32		vgic_elrsr[2];	/* Saved only */
 	u32		vgic_apr;
 	u32		vgic_lr[64];	/* A15 has only 4... */
+
+	/* Number of level-triggered interrupt in progress */
+	atomic_t	irq_active_count;

+ int irq_active_count;

arch/arm/include/asm/kvm_vgic.h bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 261

Quoted 9 lines
arch/arm/include/asm/kvm_vgic.h +2 -0
 #endif
 };
 
@@ -254,6 +257,8 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
 
 #define irqchip_in_kernel(k)	(!!((k)->arch.vgic.vctrl_base))
 #define vgic_initialized(k)	((k)->arch.vgic.ready)
+#define vgic_active_irq(v)	(atomic_read(&(v)->arch.vgic_cpu.irq_active_count) == 0)
+

+#define vgic_active_irq(v) ((v)->arch.vgic_cpu.irq_active_count)

Quoted 20 lines
arch/arm/include/asm/kvm_vgic.h +5 -0
 #else
 static inline int kvm_vgic_hyp_init(void)
 {
@@ -305,6 +310,11 @@ static inline bool vgic_initialized(struct kvm *kvm)
 {
 	return true;
 }
+
+static inline int vgic_active_irq(struct kvm_vcpu *vcpu)
+{
+	return 0;
+}
 #endif
 
 #endif
arch/arm/kvm/arm.c +0 -0
index a633d9d..1716f12 100644

arch/arm/kvm/arm.c int kvm_arch_hardware_enable(void *garbage) · line 100

Quoted 8 lines
arch/arm/kvm/arm.c +4 -1
@@ -94,7 +94,15 @@ int kvm_arch_hardware_enable(void *garbage)
 
 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 {
-	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+	if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) {
+		if (vgic_active_irq(vcpu) &&
+		    cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)
+			return 0;

So with the above change to the macro, this becomes:
+ if (vcpu->arch.vgic_cpu.irq_active_count &&
+ cmpxchg(&vcpu->mode, EXITING_GUEST_MODE, IN_GUEST_MODE) == EXITING_GUEST_MODE)

Quoted 13 lines
arch/arm/kvm/arm.c +5 -0
+
+		return 1;
+	}
+
+	return 0;
 }
 
 void kvm_arch_hardware_disable(void *garbage)
arch/arm/kvm/vgic.c +0 -0
index 415ddb8..146de1d 100644

arch/arm/kvm/vgic.c static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) · line 710

Quoted 7 lines
arch/arm/kvm/vgic.c +2 -1
@@ -705,8 +705,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 		kvm_debug("LR%d piggyback for IRQ%d %x\n", lr, irq, vgic_cpu->vgic_lr[lr]);
 		BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_PENDING_BIT;
-		if (is_level)
+		if (is_level) {
 			vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+			atomic_inc(&vgic_cpu->irq_active_count);

+ spin_lock_irqsave(&atomic_lock, flags);
+ vgic_cpu->irq_active_count++;
+ spin_unlock_irqrestore(&atomic_lock, flags);

arch/arm/kvm/vgic.c static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) · line 725

Quoted 12 lines
arch/arm/kvm/vgic.c +3 -1
+		}
 		return true;
 	}
 
@@ -718,8 +720,10 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
 
 	kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
 	vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
-	if (is_level)
+	if (is_level) {
 		vgic_cpu->vgic_lr[lr] |= VGIC_LR_EOI;
+		atomic_inc(&vgic_cpu->irq_active_count);

+ spin_lock_irqsave(&atomic_lock, flags);
+ vgic_cpu->irq_active_count++;
+ spin_unlock_irqrestore(&atomic_lock, flags);

arch/arm/kvm/vgic.c static irqreturn_t vgic_maintenance_handler(int irq, void *data) · line 1018

Quoted 9 lines
arch/arm/kvm/vgic.c +2 -0
+	}
 
 	vgic_cpu->vgic_irq_lr_map[irq] = lr;
 	clear_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr);
@@ -1011,6 +1015,8 @@ static irqreturn_t vgic_maintenance_handler(int irq, void *data)
 
 			vgic_bitmap_set_irq_val(&dist->irq_active,
 						vcpu->vcpu_id, irq, 0);
+			atomic_dec(&vgic_cpu->irq_active_count);

+ spin_lock_irqsave(&atomic_lock, flags);
+ vgic_cpu->irq_active_count--;
+ spin_unlock_irqrestore(&atomic_lock, flags);

Quoted 10 lines
arch/arm/kvm/vgic.c +1 -0
+			smp_mb();
 			vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_EOI;
 			writel_relaxed(vgic_cpu->vgic_lr[lr],
 				       dist->vctrl_base + GICH_LR0 + (lr << 2));

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#56 lore raw

PATCH v4 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 |    7 +++++++
 1 file changed, 7 insertions(+)
arch/arm/kvm/Kconfig +7 -0
index bfa3174..3c979ce 100644
@@ -51,6 +51,13 @@ 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
+	---help---
+	  Adds support for a hardware assisted, in-kernel GIC emulation.
+
 source drivers/virtio/Kconfig
 
 endif # VIRTUALIZATION
#57 lore raw

Re: [PATCH v4 13/13] ARM: KVM: Add VGIC configuration option

Hello.

Quoted 15 lines · Christoffer Dall

From: Marc Zyngier <[email protected]>

It is now possible to select the VGIC configuration option.

---
  arch/arm/kvm/Kconfig |    7 +++++++
  1 file changed, 7 insertions(+)
arch/arm/kvm/Kconfig +0 -0
index bfa3174..3c979ce 100644

arch/arm/kvm/Kconfig config KVM_ARM_MAX_VCPUS · line 55

Quoted 5 lines
arch/arm/kvm/Kconfig +2 -0
@@ -51,6 +51,13 @@ 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"

Indent with tabs uniformaly, please.

arch/arm/kvm/Kconfig config KVM_ARM_MAX_VCPUS · line 60

Quoted 5 lines
arch/arm/kvm/Kconfig +5 -0
+	depends on KVM_ARM_HOST && OF
+	select HAVE_KVM_IRQCHIP
+	---help---
+	  Adds support for a hardware assisted, in-kernel GIC emulation.
+

WBR, Sergei