KVM: selftests: aarch64: Add vGIC library functions to deal with vIRQ state
authorRicardo Koller <ricarkol@google.com>
Tue, 9 Nov 2021 02:38:54 +0000 (18:38 -0800)
committerMarc Zyngier <maz@kernel.org>
Tue, 28 Dec 2021 19:23:35 +0000 (19:23 +0000)
Add a set of library functions for userspace code in selftests to deal
with vIRQ state (i.e., ioctl wrappers).

Signed-off-by: Ricardo Koller <ricarkol@google.com>
Acked-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20211109023906.1091208-6-ricarkol@google.com
tools/testing/selftests/kvm/include/aarch64/gic.h
tools/testing/selftests/kvm/include/aarch64/vgic.h
tools/testing/selftests/kvm/lib/aarch64/vgic.c

index c932cf3..b217ea1 100644 (file)
@@ -11,6 +11,16 @@ enum gic_type {
        GIC_TYPE_MAX,
 };
 
+#define MIN_SGI                        0
+#define MIN_PPI                        16
+#define MIN_SPI                        32
+#define MAX_SPI                        1019
+#define IAR_SPURIOUS           1023
+
+#define INTID_IS_SGI(intid)    (0       <= (intid) && (intid) < MIN_PPI)
+#define INTID_IS_PPI(intid)    (MIN_PPI <= (intid) && (intid) < MIN_SPI)
+#define INTID_IS_SPI(intid)    (MIN_SPI <= (intid) && (intid) <= MAX_SPI)
+
 void gic_init(enum gic_type type, unsigned int nr_cpus,
                void *dist_base, void *redist_base);
 void gic_irq_enable(unsigned int intid);
index 0ecfb25..ec8744b 100644 (file)
 int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus,
                uint64_t gicd_base_gpa, uint64_t gicr_base_gpa);
 
-#endif /* SELFTEST_KVM_VGIC_H */
+#define VGIC_MAX_RESERVED      1023
+
+void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level);
+int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level);
+
+void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level);
+int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level);
+
+/* The vcpu arg only applies to private interrupts. */
+void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu);
+void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu);
+
+#endif // SELFTEST_KVM_VGIC_H
index b9b271f..a1f1f6c 100644 (file)
@@ -5,11 +5,14 @@
 
 #include <linux/kvm.h>
 #include <linux/sizes.h>
+#include <asm/kvm_para.h>
 #include <asm/kvm.h>
 
 #include "kvm_util.h"
 #include "../kvm_util_internal.h"
 #include "vgic.h"
+#include "gic.h"
+#include "gic_v3.h"
 
 /*
  * vGIC-v3 default host setup
@@ -68,3 +71,93 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus,
 
        return gic_fd;
 }
+
+/* should only work for level sensitive interrupts */
+int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)
+{
+       uint64_t attr = 32 * (intid / 32);
+       uint64_t index = intid % 32;
+       uint64_t val;
+       int ret;
+
+       ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
+                                attr, &val, false);
+       if (ret != 0)
+               return ret;
+
+       val |= 1U << index;
+       ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
+                                attr, &val, true);
+       return ret;
+}
+
+void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)
+{
+       int ret = _kvm_irq_set_level_info(gic_fd, intid, level);
+
+       TEST_ASSERT(ret == 0, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO failed, "
+                       "rc: %i errno: %i", ret, errno);
+}
+
+int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)
+{
+       uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK;
+
+       if (INTID_IS_PPI(intid))
+               irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;
+       else if (INTID_IS_SPI(intid))
+               irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT;
+       else
+               TEST_FAIL("KVM_IRQ_LINE can't be used with SGIs.");
+
+       return _kvm_irq_line(vm, irq, level);
+}
+
+void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)
+{
+       int ret = _kvm_arm_irq_line(vm, intid, level);
+
+       TEST_ASSERT(ret == 0, "KVM_IRQ_LINE failed, rc: %i errno: %i",
+                       ret, errno);
+}
+
+static void vgic_poke_irq(int gic_fd, uint32_t intid,
+               uint32_t vcpu, uint64_t reg_off)
+{
+       uint64_t reg = intid / 32;
+       uint64_t index = intid % 32;
+       uint64_t attr = reg_off + reg * 4;
+       uint64_t val;
+       bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid);
+
+       /* Check that the addr part of the attr is within 32 bits. */
+       assert(attr <= KVM_DEV_ARM_VGIC_OFFSET_MASK);
+
+       uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
+                                         : KVM_DEV_ARM_VGIC_GRP_DIST_REGS;
+
+       if (intid_is_private) {
+               /* TODO: only vcpu 0 implemented for now. */
+               assert(vcpu == 0);
+               attr += SZ_64K;
+       }
+
+       /* All calls will succeed, even with invalid intid's, as long as the
+        * addr part of the attr is within 32 bits (checked above). An invalid
+        * intid will just make the read/writes point to above the intended
+        * register space (i.e., ICPENDR after ISPENDR).
+        */
+       kvm_device_access(gic_fd, group, attr, &val, false);
+       val |= 1ULL << index;
+       kvm_device_access(gic_fd, group, attr, &val, true);
+}
+
+void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu)
+{
+       vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR);
+}
+
+void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu)
+{
+       vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
+}