From 33a1ca736e74839d08948973d30f6def820b8b14 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:50 -0800 Subject: [PATCH 01/17] KVM: selftests: aarch64: Move gic_v3.h to shared headers Move gic_v3.h to the shared headers location. There are some definitions that will be used in the vgic-irq test. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-2-ricarkol@google.com --- tools/testing/selftests/kvm/{lib => include}/aarch64/gic_v3.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tools/testing/selftests/kvm/{lib => include}/aarch64/gic_v3.h (100%) diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.h b/tools/testing/selftests/kvm/include/aarch64/gic_v3.h similarity index 100% rename from tools/testing/selftests/kvm/lib/aarch64/gic_v3.h rename to tools/testing/selftests/kvm/include/aarch64/gic_v3.h From 745068367ccbf33d69cf4acf7b1a3d5478978e8e Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:51 -0800 Subject: [PATCH 02/17] KVM: selftests: aarch64: Add function for accessing GICv3 dist and redist registers Add a generic library function for reading and writing GICv3 distributor and redistributor registers. Then adapt some functions to use it; more will come and use it in the next commit. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-3-ricarkol@google.com --- .../selftests/kvm/lib/aarch64/gic_v3.c | 120 +++++++++++++++--- 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c index 2dbf3339b62e..00e944fd8148 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c +++ b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c @@ -19,7 +19,8 @@ struct gicv3_data { unsigned int nr_spis; }; -#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K) +#define sgi_base_from_redist(redist_base) (redist_base + SZ_64K) +#define DIST_BIT (1U << 31) enum gicv3_intid_range { SGI_RANGE, @@ -50,6 +51,14 @@ static void gicv3_gicr_wait_for_rwp(void *redist_base) } } +static void gicv3_wait_for_rwp(uint32_t cpu_or_dist) +{ + if (cpu_or_dist & DIST_BIT) + gicv3_gicd_wait_for_rwp(); + else + gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu_or_dist]); +} + static enum gicv3_intid_range get_intid_range(unsigned int intid) { switch (intid) { @@ -81,39 +90,108 @@ static void gicv3_write_eoir(uint32_t irq) isb(); } -static void -gicv3_config_irq(unsigned int intid, unsigned int offset) +uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset) +{ + void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base + : sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]); + return readl(base + offset); +} + +void gicv3_reg_writel(uint32_t cpu_or_dist, uint64_t offset, uint32_t reg_val) +{ + void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base + : sgi_base_from_redist(gicv3_data.redist_base[cpu_or_dist]); + writel(reg_val, base + offset); +} + +uint32_t gicv3_getl_fields(uint32_t cpu_or_dist, uint64_t offset, uint32_t mask) +{ + return gicv3_reg_readl(cpu_or_dist, offset) & mask; +} + +void gicv3_setl_fields(uint32_t cpu_or_dist, uint64_t offset, + uint32_t mask, uint32_t reg_val) +{ + uint32_t tmp = gicv3_reg_readl(cpu_or_dist, offset) & ~mask; + + tmp |= (reg_val & mask); + gicv3_reg_writel(cpu_or_dist, offset, tmp); +} + +/* + * We use a single offset for the distributor and redistributor maps as they + * have the same value in both. The only exceptions are registers that only + * exist in one and not the other, like GICR_WAKER that doesn't exist in the + * distributor map. Such registers are conveniently marked as reserved in the + * map that doesn't implement it; like GICR_WAKER's offset of 0x0014 being + * marked as "Reserved" in the Distributor map. + */ +static void gicv3_access_reg(uint32_t intid, uint64_t offset, + uint32_t reg_bits, uint32_t bits_per_field, + bool write, uint32_t *val) { uint32_t cpu = guest_get_vcpuid(); - uint32_t mask = 1 << (intid % 32); enum gicv3_intid_range intid_range = get_intid_range(intid); - void *reg; + uint32_t fields_per_reg, index, mask, shift; + uint32_t cpu_or_dist; - /* We care about 'cpu' only for SGIs or PPIs */ - if (intid_range == SGI_RANGE || intid_range == PPI_RANGE) { - GUEST_ASSERT(cpu < gicv3_data.nr_cpus); + GUEST_ASSERT(bits_per_field <= reg_bits); + GUEST_ASSERT(*val < (1U << bits_per_field)); + /* Some registers like IROUTER are 64 bit long. Those are currently not + * supported by readl nor writel, so just asserting here until then. + */ + GUEST_ASSERT(reg_bits == 32); - reg = sgi_base_from_redist(gicv3_data.redist_base[cpu]) + - offset; - writel(mask, reg); - gicv3_gicr_wait_for_rwp(gicv3_data.redist_base[cpu]); - } else if (intid_range == SPI_RANGE) { - reg = gicv3_data.dist_base + offset + (intid / 32) * 4; - writel(mask, reg); - gicv3_gicd_wait_for_rwp(); - } else { - GUEST_ASSERT(0); - } + fields_per_reg = reg_bits / bits_per_field; + index = intid % fields_per_reg; + shift = index * bits_per_field; + mask = ((1U << bits_per_field) - 1) << shift; + + /* Set offset to the actual register holding intid's config. */ + offset += (intid / fields_per_reg) * (reg_bits / 8); + + cpu_or_dist = (intid_range == SPI_RANGE) ? DIST_BIT : cpu; + + if (write) + gicv3_setl_fields(cpu_or_dist, offset, mask, *val << shift); + *val = gicv3_getl_fields(cpu_or_dist, offset, mask) >> shift; +} + +static void gicv3_write_reg(uint32_t intid, uint64_t offset, + uint32_t reg_bits, uint32_t bits_per_field, uint32_t val) +{ + gicv3_access_reg(intid, offset, reg_bits, + bits_per_field, true, &val); +} + +static uint32_t gicv3_read_reg(uint32_t intid, uint64_t offset, + uint32_t reg_bits, uint32_t bits_per_field) +{ + uint32_t val; + + gicv3_access_reg(intid, offset, reg_bits, + bits_per_field, false, &val); + return val; } static void gicv3_irq_enable(unsigned int intid) { - gicv3_config_irq(intid, GICD_ISENABLER); + bool is_spi = get_intid_range(intid) == SPI_RANGE; + unsigned int val = 1; + uint32_t cpu = guest_get_vcpuid(); + + gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, val); + gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } static void gicv3_irq_disable(unsigned int intid) { - gicv3_config_irq(intid, GICD_ICENABLER); + bool is_spi = get_intid_range(intid) == SPI_RANGE; + uint32_t val = 1; + uint32_t cpu = guest_get_vcpuid(); + + gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, val); + gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } static void gicv3_enable_redist(void *redist_base) From 17ce617bf76a7c1d3b553ed01607706434b9ed59 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:52 -0800 Subject: [PATCH 03/17] KVM: selftests: aarch64: Add GICv3 register accessor library functions Add library functions for accessing GICv3 registers: DIR, PMR, CTLR, ISACTIVER, ISPENDR. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-4-ricarkol@google.com --- .../selftests/kvm/include/aarch64/gic.h | 16 ++++ .../selftests/kvm/include/aarch64/gic_v3.h | 12 +++ tools/testing/selftests/kvm/lib/aarch64/gic.c | 66 ++++++++++++++ .../selftests/kvm/lib/aarch64/gic_private.h | 11 +++ .../selftests/kvm/lib/aarch64/gic_v3.c | 90 +++++++++++++++++-- 5 files changed, 189 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/kvm/include/aarch64/gic.h b/tools/testing/selftests/kvm/include/aarch64/gic.h index 85dd1e53048e..c932cf3d0771 100644 --- a/tools/testing/selftests/kvm/include/aarch64/gic.h +++ b/tools/testing/selftests/kvm/include/aarch64/gic.h @@ -17,5 +17,21 @@ void gic_irq_enable(unsigned int intid); void gic_irq_disable(unsigned int intid); unsigned int gic_get_and_ack_irq(void); void gic_set_eoi(unsigned int intid); +void gic_set_dir(unsigned int intid); + +/* + * Sets the EOI mode. When split is false, EOI just drops the priority. When + * split is true, EOI drops the priority and deactivates the interrupt. + */ +void gic_set_eoi_split(bool split); +void gic_set_priority_mask(uint64_t mask); +void gic_set_priority(uint32_t intid, uint32_t prio); +void gic_irq_set_active(unsigned int intid); +void gic_irq_clear_active(unsigned int intid); +bool gic_irq_get_active(unsigned int intid); +void gic_irq_set_pending(unsigned int intid); +void gic_irq_clear_pending(unsigned int intid); +bool gic_irq_get_pending(unsigned int intid); +void gic_irq_set_config(unsigned int intid, bool is_edge); #endif /* SELFTEST_KVM_GIC_H */ diff --git a/tools/testing/selftests/kvm/include/aarch64/gic_v3.h b/tools/testing/selftests/kvm/include/aarch64/gic_v3.h index b51536d469a6..ba0886e8a2bb 100644 --- a/tools/testing/selftests/kvm/include/aarch64/gic_v3.h +++ b/tools/testing/selftests/kvm/include/aarch64/gic_v3.h @@ -16,8 +16,12 @@ #define GICD_IGROUPR 0x0080 #define GICD_ISENABLER 0x0100 #define GICD_ICENABLER 0x0180 +#define GICD_ISPENDR 0x0200 +#define GICD_ICPENDR 0x0280 #define GICD_ICACTIVER 0x0380 +#define GICD_ISACTIVER 0x0300 #define GICD_IPRIORITYR 0x0400 +#define GICD_ICFGR 0x0C00 /* * The assumption is that the guest runs in a non-secure mode. @@ -49,16 +53,24 @@ #define GICR_IGROUPR0 GICD_IGROUPR #define GICR_ISENABLER0 GICD_ISENABLER #define GICR_ICENABLER0 GICD_ICENABLER +#define GICR_ISPENDR0 GICD_ISPENDR +#define GICR_ISACTIVER0 GICD_ISACTIVER #define GICR_ICACTIVER0 GICD_ICACTIVER +#define GICR_ICENABLER GICD_ICENABLER +#define GICR_ICACTIVER GICD_ICACTIVER #define GICR_IPRIORITYR0 GICD_IPRIORITYR /* CPU interface registers */ #define SYS_ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0) #define SYS_ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0) #define SYS_ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1) +#define SYS_ICC_DIR_EL1 sys_reg(3, 0, 12, 11, 1) +#define SYS_ICC_CTLR_EL1 sys_reg(3, 0, 12, 12, 4) #define SYS_ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5) #define SYS_ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7) +#define SYS_ICV_AP1R0_EL1 sys_reg(3, 0, 12, 9, 0) + #define ICC_PMR_DEF_PRIO 0xf0 #define ICC_SRE_EL1_SRE (1U << 0) diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic.c b/tools/testing/selftests/kvm/lib/aarch64/gic.c index fff4fc27504d..55668631d546 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/gic.c +++ b/tools/testing/selftests/kvm/lib/aarch64/gic.c @@ -93,3 +93,69 @@ void gic_set_eoi(unsigned int intid) GUEST_ASSERT(gic_common_ops); gic_common_ops->gic_write_eoir(intid); } + +void gic_set_dir(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_write_dir(intid); +} + +void gic_set_eoi_split(bool split) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_set_eoi_split(split); +} + +void gic_set_priority_mask(uint64_t pmr) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_set_priority_mask(pmr); +} + +void gic_set_priority(unsigned int intid, unsigned int prio) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_set_priority(intid, prio); +} + +void gic_irq_set_active(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_irq_set_active(intid); +} + +void gic_irq_clear_active(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_irq_clear_active(intid); +} + +bool gic_irq_get_active(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + return gic_common_ops->gic_irq_get_active(intid); +} + +void gic_irq_set_pending(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_irq_set_pending(intid); +} + +void gic_irq_clear_pending(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_irq_clear_pending(intid); +} + +bool gic_irq_get_pending(unsigned int intid) +{ + GUEST_ASSERT(gic_common_ops); + return gic_common_ops->gic_irq_get_pending(intid); +} + +void gic_irq_set_config(unsigned int intid, bool is_edge) +{ + GUEST_ASSERT(gic_common_ops); + gic_common_ops->gic_irq_set_config(intid, is_edge); +} diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_private.h b/tools/testing/selftests/kvm/lib/aarch64/gic_private.h index d81d739433dc..75d07313c893 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/gic_private.h +++ b/tools/testing/selftests/kvm/lib/aarch64/gic_private.h @@ -14,6 +14,17 @@ struct gic_common_ops { void (*gic_irq_disable)(unsigned int intid); uint64_t (*gic_read_iar)(void); void (*gic_write_eoir)(uint32_t irq); + void (*gic_write_dir)(uint32_t irq); + void (*gic_set_eoi_split)(bool split); + void (*gic_set_priority_mask)(uint64_t mask); + void (*gic_set_priority)(uint32_t intid, uint32_t prio); + void (*gic_irq_set_active)(uint32_t intid); + void (*gic_irq_clear_active)(uint32_t intid); + bool (*gic_irq_get_active)(uint32_t intid); + void (*gic_irq_set_pending)(uint32_t intid); + void (*gic_irq_clear_pending)(uint32_t intid); + bool (*gic_irq_get_pending)(uint32_t intid); + void (*gic_irq_set_config)(uint32_t intid, bool is_edge); }; extern const struct gic_common_ops gicv3_ops; diff --git a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c index 00e944fd8148..00f613c0583c 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c +++ b/tools/testing/selftests/kvm/lib/aarch64/gic_v3.c @@ -90,6 +90,29 @@ static void gicv3_write_eoir(uint32_t irq) isb(); } +static void gicv3_write_dir(uint32_t irq) +{ + write_sysreg_s(irq, SYS_ICC_DIR_EL1); + isb(); +} + +static void gicv3_set_priority_mask(uint64_t mask) +{ + write_sysreg_s(mask, SYS_ICC_PMR_EL1); +} + +static void gicv3_set_eoi_split(bool split) +{ + uint32_t val; + + /* All other fields are read-only, so no need to read CTLR first. In + * fact, the kernel does the same. + */ + val = split ? (1U << 1) : 0; + write_sysreg_s(val, SYS_ICC_CTLR_EL1); + isb(); +} + uint32_t gicv3_reg_readl(uint32_t cpu_or_dist, uint64_t offset) { void *base = cpu_or_dist & DIST_BIT ? gicv3_data.dist_base @@ -174,26 +197,70 @@ static uint32_t gicv3_read_reg(uint32_t intid, uint64_t offset, return val; } -static void gicv3_irq_enable(unsigned int intid) +static void gicv3_set_priority(uint32_t intid, uint32_t prio) +{ + gicv3_write_reg(intid, GICD_IPRIORITYR, 32, 8, prio); +} + +/* Sets the intid to be level-sensitive or edge-triggered. */ +static void gicv3_irq_set_config(uint32_t intid, bool is_edge) +{ + uint32_t val; + + /* N/A for private interrupts. */ + GUEST_ASSERT(get_intid_range(intid) == SPI_RANGE); + val = is_edge ? 2 : 0; + gicv3_write_reg(intid, GICD_ICFGR, 32, 2, val); +} + +static void gicv3_irq_enable(uint32_t intid) { bool is_spi = get_intid_range(intid) == SPI_RANGE; - unsigned int val = 1; uint32_t cpu = guest_get_vcpuid(); - gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, val); + gicv3_write_reg(intid, GICD_ISENABLER, 32, 1, 1); gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } -static void gicv3_irq_disable(unsigned int intid) +static void gicv3_irq_disable(uint32_t intid) { bool is_spi = get_intid_range(intid) == SPI_RANGE; - uint32_t val = 1; uint32_t cpu = guest_get_vcpuid(); - gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, val); + gicv3_write_reg(intid, GICD_ICENABLER, 32, 1, 1); gicv3_wait_for_rwp(is_spi ? DIST_BIT : cpu); } +static void gicv3_irq_set_active(uint32_t intid) +{ + gicv3_write_reg(intid, GICD_ISACTIVER, 32, 1, 1); +} + +static void gicv3_irq_clear_active(uint32_t intid) +{ + gicv3_write_reg(intid, GICD_ICACTIVER, 32, 1, 1); +} + +static bool gicv3_irq_get_active(uint32_t intid) +{ + return gicv3_read_reg(intid, GICD_ISACTIVER, 32, 1); +} + +static void gicv3_irq_set_pending(uint32_t intid) +{ + gicv3_write_reg(intid, GICD_ISPENDR, 32, 1, 1); +} + +static void gicv3_irq_clear_pending(uint32_t intid) +{ + gicv3_write_reg(intid, GICD_ICPENDR, 32, 1, 1); +} + +static bool gicv3_irq_get_pending(uint32_t intid) +{ + return gicv3_read_reg(intid, GICD_ISPENDR, 32, 1); +} + static void gicv3_enable_redist(void *redist_base) { uint32_t val = readl(redist_base + GICR_WAKER); @@ -315,4 +382,15 @@ const struct gic_common_ops gicv3_ops = { .gic_irq_disable = gicv3_irq_disable, .gic_read_iar = gicv3_read_iar, .gic_write_eoir = gicv3_write_eoir, + .gic_write_dir = gicv3_write_dir, + .gic_set_priority_mask = gicv3_set_priority_mask, + .gic_set_eoi_split = gicv3_set_eoi_split, + .gic_set_priority = gicv3_set_priority, + .gic_irq_set_active = gicv3_irq_set_active, + .gic_irq_clear_active = gicv3_irq_clear_active, + .gic_irq_get_active = gicv3_irq_get_active, + .gic_irq_set_pending = gicv3_irq_set_pending, + .gic_irq_clear_pending = gicv3_irq_clear_pending, + .gic_irq_get_pending = gicv3_irq_get_pending, + .gic_irq_set_config = gicv3_irq_set_config, }; From 227895ed6d03b46fa619614a41a3b8e1074d6151 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:53 -0800 Subject: [PATCH 04/17] KVM: selftests: Add kvm_irq_line library function Add an architecture independent wrapper function for the KVM_IRQ_LINE ioctl. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-5-ricarkol@google.com --- .../testing/selftests/kvm/include/kvm_util.h | 2 ++ tools/testing/selftests/kvm/lib/kvm_util.c | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 6a1a37f30494..4ba8fd44cf95 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -240,6 +240,8 @@ int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, void *val, bool write); int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, void *val, bool write); +void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); +int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, uint64_t attr); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 8f2e0bb1ef96..9f7210c2d75e 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2086,6 +2086,27 @@ int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, return ret; } +/* + * IRQ related functions. + */ + +int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) +{ + struct kvm_irq_level irq_level = { + .irq = irq, + .level = level, + }; + + return _vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); +} + +void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) +{ + int ret = _kvm_irq_line(vm, irq, level); + + TEST_ASSERT(ret >= 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", ret, errno); +} + /* * VM Dump * From e95def3a904dea467309bbe382a9032d301ba9cd Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:54 -0800 Subject: [PATCH 05/17] KVM: selftests: aarch64: Add vGIC library functions to deal with vIRQ state 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 Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-6-ricarkol@google.com --- .../selftests/kvm/include/aarch64/gic.h | 10 ++ .../selftests/kvm/include/aarch64/vgic.h | 14 ++- .../testing/selftests/kvm/lib/aarch64/vgic.c | 93 +++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/aarch64/gic.h b/tools/testing/selftests/kvm/include/aarch64/gic.h index c932cf3d0771..b217ea17cac5 100644 --- a/tools/testing/selftests/kvm/include/aarch64/gic.h +++ b/tools/testing/selftests/kvm/include/aarch64/gic.h @@ -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); diff --git a/tools/testing/selftests/kvm/include/aarch64/vgic.h b/tools/testing/selftests/kvm/include/aarch64/vgic.h index 0ecfb253893c..ec8744bb2d4b 100644 --- a/tools/testing/selftests/kvm/include/aarch64/vgic.h +++ b/tools/testing/selftests/kvm/include/aarch64/vgic.h @@ -17,4 +17,16 @@ 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 diff --git a/tools/testing/selftests/kvm/lib/aarch64/vgic.c b/tools/testing/selftests/kvm/lib/aarch64/vgic.c index b9b271ff520d..a1f1f6c8e2e0 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/vgic.c +++ b/tools/testing/selftests/kvm/lib/aarch64/vgic.c @@ -5,11 +5,14 @@ #include #include +#include #include #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); +} From 50b020cdb7f72077e16133f1d88c9359cf415a53 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:55 -0800 Subject: [PATCH 06/17] KVM: selftests: aarch64: Add vgic_irq to test userspace IRQ injection Add a new KVM selftest, vgic_irq, for testing userspace IRQ injection. This particular test injects an SPI using KVM_IRQ_LINE on GICv3 and verifies that the IRQ is handled in the guest. The next commits will add more types of IRQs and different modes. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-7-ricarkol@google.com --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../testing/selftests/kvm/aarch64/vgic_irq.c | 244 ++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 tools/testing/selftests/kvm/aarch64/vgic_irq.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 3763105029fb..f75d4c433fc3 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -4,6 +4,7 @@ /aarch64/get-reg-list /aarch64/psci_cpu_on_test /aarch64/vgic_init +/aarch64/vgic_irq /s390x/memop /s390x/resets /s390x/sync_regs_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index c4e34717826a..758b5d0e352e 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -94,6 +94,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test TEST_GEN_PROGS_aarch64 += aarch64/vgic_init +TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq TEST_GEN_PROGS_aarch64 += demand_paging_test TEST_GEN_PROGS_aarch64 += dirty_log_test TEST_GEN_PROGS_aarch64 += dirty_log_perf_test diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c new file mode 100644 index 000000000000..e13e87427038 --- /dev/null +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vgic_irq.c - Test userspace injection of IRQs + * + * This test validates the injection of IRQs from userspace using various + * methods (e.g., KVM_IRQ_LINE) and modes (e.g., EOI). The guest "asks" the + * host to inject a specific intid via a GUEST_SYNC call, and then checks that + * it received it. + */ + +#include +#include +#include + +#include "processor.h" +#include "test_util.h" +#include "kvm_util.h" +#include "gic.h" +#include "gic_v3.h" +#include "vgic.h" + +#define GICD_BASE_GPA 0x08000000ULL +#define GICR_BASE_GPA 0x080A0000ULL +#define VCPU_ID 0 + +/* + * KVM implements 32 priority levels: + * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8 + * + * Note that these macros will still be correct in the case that KVM implements + * more priority levels. Also note that 32 is the minimum for GICv3 and GICv2. + */ +#define KVM_NUM_PRIOS 32 +#define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */ +#define LOWEST_PRIO (KVM_NUM_PRIOS - 1) +#define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */ +#define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1) +#define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */ + +static void *dist = (void *)GICD_BASE_GPA; +static void *redist = (void *)GICR_BASE_GPA; + +/* + * The kvm_inject_* utilities are used by the guest to ask the host to inject + * interrupts (e.g., using the KVM_IRQ_LINE ioctl). + */ + +typedef enum { + KVM_INJECT_EDGE_IRQ_LINE = 1, +} kvm_inject_cmd; + +struct kvm_inject_args { + kvm_inject_cmd cmd; + uint32_t intid; +}; + +/* Used on the guest side to perform the hypercall. */ +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid); + +/* Used on the host side to get the hypercall info. */ +static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, + struct kvm_inject_args *args); + +/* Shared between the guest main thread and the IRQ handlers. */ +volatile uint64_t irq_handled; +volatile uint32_t irqnr_received[MAX_SPI + 1]; + +static void reset_stats(void) +{ + int i; + + irq_handled = 0; + for (i = 0; i <= MAX_SPI; i++) + irqnr_received[i] = 0; +} + +static uint64_t gic_read_ap1r0(void) +{ + uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1); + + dsb(sy); + return reg; +} + +static void guest_irq_handler(struct ex_regs *regs) +{ + uint32_t intid = gic_get_and_ack_irq(); + + if (intid == IAR_SPURIOUS) + return; + + GUEST_ASSERT(gic_irq_get_active(intid)); + + GUEST_ASSERT(!gic_irq_get_pending(intid)); + + GUEST_ASSERT(intid < MAX_SPI); + irqnr_received[intid] += 1; + irq_handled += 1; + + gic_set_eoi(intid); + GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); + + GUEST_ASSERT(!gic_irq_get_active(intid)); + GUEST_ASSERT(!gic_irq_get_pending(intid)); +} + +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid) +{ + struct kvm_inject_args args = { + .cmd = cmd, + .intid = intid, + }; + GUEST_SYNC(&args); +} + +#define GUEST_ASSERT_IAR_EMPTY() \ +do { \ + uint32_t _intid; \ + _intid = gic_get_and_ack_irq(); \ + GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ +} while (0) + +static void test_kvm_irq_line(uint32_t intid) +{ + reset_stats(); + + asm volatile("msr daifset, #2" : : : "memory"); + kvm_inject_call(KVM_INJECT_EDGE_IRQ_LINE, intid); + + while (irq_handled < 1) { + asm volatile("wfi\n" + "msr daifclr, #2\n" + /* handle IRQ */ + "msr daifset, #2\n" + : : : "memory"); + } + asm volatile("msr daifclr, #2" : : : "memory"); + + GUEST_ASSERT_EQ(irq_handled, 1); + GUEST_ASSERT_EQ(irqnr_received[intid], 1); + GUEST_ASSERT_IAR_EMPTY(); +} + +static void guest_code(void) +{ + uint32_t i; + uint32_t nr_irqs = 64; /* absolute minimum number of IRQs supported. */ + + gic_init(GIC_V3, 1, dist, redist); + + for (i = 0; i < nr_irqs; i++) { + gic_irq_enable(i); + gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); + } + + gic_set_priority_mask(CPU_PRIO_MASK); + + local_irq_enable(); + + test_kvm_irq_line(MIN_SPI); + + GUEST_DONE(); +} + +static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, + struct kvm_inject_args *inject_args) +{ + kvm_inject_cmd cmd = inject_args->cmd; + uint32_t intid = inject_args->intid; + + switch (cmd) { + case KVM_INJECT_EDGE_IRQ_LINE: + kvm_arm_irq_line(vm, intid, 1); + kvm_arm_irq_line(vm, intid, 0); + break; + default: + break; + } +} + +static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, + struct kvm_inject_args *args) +{ + struct kvm_inject_args *kvm_args_hva; + vm_vaddr_t kvm_args_gva; + + kvm_args_gva = uc->args[1]; + kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva); + memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args)); +} + + +static void test_vgic(void) +{ + struct ucall uc; + int gic_fd; + struct kvm_vm *vm; + struct kvm_inject_args inject_args; + + vm = vm_create_default(VCPU_ID, 0, guest_code); + ucall_init(vm, NULL); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vm, VCPU_ID); + + gic_fd = vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA); + + vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, + guest_irq_handler); + + while (1) { + vcpu_run(vm, VCPU_ID); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_SYNC: + kvm_inject_get_call(vm, &uc, &inject_args); + run_guest_cmd(vm, gic_fd, &inject_args); + break; + case UCALL_ABORT: + TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", + (const char *)uc.args[0], + __FILE__, uc.args[1], uc.args[2], uc.args[3]); + break; + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } + +done: + close(gic_fd); + kvm_vm_free(vm); +} + +int main(int ac, char **av) +{ + /* Tell stdout not to buffer its content */ + setbuf(stdout, NULL); + + test_vgic(); + + return 0; +} From e1cb399eed1eda29568b17bdb75d16cee1fc3da4 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:56 -0800 Subject: [PATCH 07/17] KVM: selftests: aarch64: Abstract the injection functions in vgic_irq Build an abstraction around the injection functions, so the preparation and checking around the actual injection can be shared between tests. All functions are stored as pointers in arrays of kvm_inject_desc's which include the pointer and what kind of interrupts they can inject. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-8-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index e13e87427038..f5d76fef22f0 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -57,10 +57,28 @@ struct kvm_inject_args { /* Used on the guest side to perform the hypercall. */ static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid); +#define KVM_INJECT(cmd, intid) \ + kvm_inject_call(cmd, intid) + /* Used on the host side to get the hypercall info. */ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, struct kvm_inject_args *args); +struct kvm_inject_desc { + kvm_inject_cmd cmd; + /* can inject PPIs, PPIs, and/or SPIs. */ + bool sgi, ppi, spi; +}; + +static struct kvm_inject_desc inject_edge_fns[] = { + /* sgi ppi spi */ + { KVM_INJECT_EDGE_IRQ_LINE, false, false, true }, + { 0, }, +}; + +#define for_each_inject_fn(t, f) \ + for ((f) = (t); (f)->cmd; (f)++) + /* Shared between the guest main thread and the IRQ handlers. */ volatile uint64_t irq_handled; volatile uint32_t irqnr_received[MAX_SPI + 1]; @@ -120,12 +138,12 @@ do { \ GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ } while (0) -static void test_kvm_irq_line(uint32_t intid) +static void guest_inject(uint32_t intid, kvm_inject_cmd cmd) { reset_stats(); asm volatile("msr daifset, #2" : : : "memory"); - kvm_inject_call(KVM_INJECT_EDGE_IRQ_LINE, intid); + KVM_INJECT(cmd, intid); while (irq_handled < 1) { asm volatile("wfi\n" @@ -141,10 +159,23 @@ static void test_kvm_irq_line(uint32_t intid) GUEST_ASSERT_IAR_EMPTY(); } +static void test_injection(struct kvm_inject_desc *f) +{ + if (f->sgi) + guest_inject(MIN_SGI, f->cmd); + + if (f->ppi) + guest_inject(MIN_PPI, f->cmd); + + if (f->spi) + guest_inject(MIN_SPI, f->cmd); +} + static void guest_code(void) { uint32_t i; uint32_t nr_irqs = 64; /* absolute minimum number of IRQs supported. */ + struct kvm_inject_desc *f; gic_init(GIC_V3, 1, dist, redist); @@ -157,7 +188,9 @@ static void guest_code(void) local_irq_enable(); - test_kvm_irq_line(MIN_SPI); + /* Start the tests. */ + for_each_inject_fn(inject_edge_fns, f) + test_injection(f); GUEST_DONE(); } From e5410ee2806d74a749fa39ca6fdb73be2f88611f Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:57 -0800 Subject: [PATCH 08/17] KVM: selftests: aarch64: Cmdline arg to set number of IRQs in vgic_irq test Add the ability to specify the number of vIRQs exposed by KVM (arg defaults to 64). Then extend the KVM_IRQ_LINE test by injecting all available SPIs at once (specified by the nr-irqs arg). As a bonus, inject all SGIs at once as well. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-9-ricarkol@google.com --- .../selftests/kvm/aarch64/arch_timer.c | 2 +- .../testing/selftests/kvm/aarch64/vgic_irq.c | 151 ++++++++++++++---- .../selftests/kvm/include/aarch64/vgic.h | 2 +- .../testing/selftests/kvm/lib/aarch64/vgic.c | 9 +- 4 files changed, 128 insertions(+), 36 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c index bf6a45b0b8dc..9ad38bd360a4 100644 --- a/tools/testing/selftests/kvm/aarch64/arch_timer.c +++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c @@ -382,7 +382,7 @@ static struct kvm_vm *test_vm_create(void) ucall_init(vm, NULL); test_init_timer_irq(vm); - vgic_v3_setup(vm, nr_vcpus, GICD_BASE_GPA, GICR_BASE_GPA); + vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA); /* Make all the test's cmdline args visible to the guest */ sync_global_to_guest(vm, test_args); diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index f5d76fef22f0..0b89a29dfe79 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -23,6 +23,14 @@ #define GICR_BASE_GPA 0x080A0000ULL #define VCPU_ID 0 +/* + * Stores the user specified args; it's passed to the guest and to every test + * function. + */ +struct test_args { + uint32_t nr_irqs; /* number of KVM supported IRQs. */ +}; + /* * KVM implements 32 priority levels: * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8 @@ -51,14 +59,18 @@ typedef enum { struct kvm_inject_args { kvm_inject_cmd cmd; - uint32_t intid; + uint32_t first_intid; + uint32_t num; }; /* Used on the guest side to perform the hypercall. */ -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid); +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num); #define KVM_INJECT(cmd, intid) \ - kvm_inject_call(cmd, intid) + kvm_inject_call(cmd, intid, 1) + +#define KVM_INJECT_MULTI(cmd, intid, num) \ + kvm_inject_call(cmd, intid, num) /* Used on the host side to get the hypercall info. */ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, @@ -122,11 +134,12 @@ static void guest_irq_handler(struct ex_regs *regs) GUEST_ASSERT(!gic_irq_get_pending(intid)); } -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid) +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num) { struct kvm_inject_args args = { .cmd = cmd, - .intid = intid, + .first_intid = first_intid, + .num = num, }; GUEST_SYNC(&args); } @@ -138,14 +151,30 @@ do { \ GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ } while (0) -static void guest_inject(uint32_t intid, kvm_inject_cmd cmd) +static void reset_priorities(struct test_args *args) { + int i; + + for (i = 0; i < args->nr_irqs; i++) + gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); +} + +static void guest_inject(struct test_args *args, + uint32_t first_intid, uint32_t num, + kvm_inject_cmd cmd) +{ + uint32_t i; + reset_stats(); - asm volatile("msr daifset, #2" : : : "memory"); - KVM_INJECT(cmd, intid); + /* Cycle over all priorities to make things more interesting. */ + for (i = first_intid; i < num + first_intid; i++) + gic_set_priority(i, (i % (KVM_NUM_PRIOS - 1)) << 3); - while (irq_handled < 1) { + asm volatile("msr daifset, #2" : : : "memory"); + KVM_INJECT_MULTI(cmd, first_intid, num); + + while (irq_handled < num) { asm volatile("wfi\n" "msr daifclr, #2\n" /* handle IRQ */ @@ -154,57 +183,72 @@ static void guest_inject(uint32_t intid, kvm_inject_cmd cmd) } asm volatile("msr daifclr, #2" : : : "memory"); - GUEST_ASSERT_EQ(irq_handled, 1); - GUEST_ASSERT_EQ(irqnr_received[intid], 1); + GUEST_ASSERT_EQ(irq_handled, num); + for (i = first_intid; i < num + first_intid; i++) + GUEST_ASSERT_EQ(irqnr_received[i], 1); GUEST_ASSERT_IAR_EMPTY(); + + reset_priorities(args); } -static void test_injection(struct kvm_inject_desc *f) +static void test_injection(struct test_args *args, struct kvm_inject_desc *f) { - if (f->sgi) - guest_inject(MIN_SGI, f->cmd); + uint32_t nr_irqs = args->nr_irqs; + + if (f->sgi) { + guest_inject(args, MIN_SGI, 1, f->cmd); + guest_inject(args, 0, 16, f->cmd); + } if (f->ppi) - guest_inject(MIN_PPI, f->cmd); + guest_inject(args, MIN_PPI, 1, f->cmd); - if (f->spi) - guest_inject(MIN_SPI, f->cmd); + if (f->spi) { + guest_inject(args, MIN_SPI, 1, f->cmd); + guest_inject(args, nr_irqs - 1, 1, f->cmd); + guest_inject(args, MIN_SPI, nr_irqs - MIN_SPI, f->cmd); + } } -static void guest_code(void) +static void guest_code(struct test_args args) { - uint32_t i; - uint32_t nr_irqs = 64; /* absolute minimum number of IRQs supported. */ + uint32_t i, nr_irqs = args.nr_irqs; struct kvm_inject_desc *f; gic_init(GIC_V3, 1, dist, redist); - for (i = 0; i < nr_irqs; i++) { + for (i = 0; i < nr_irqs; i++) gic_irq_enable(i); - gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); - } + reset_priorities(&args); gic_set_priority_mask(CPU_PRIO_MASK); local_irq_enable(); /* Start the tests. */ for_each_inject_fn(inject_edge_fns, f) - test_injection(f); + test_injection(&args, f); GUEST_DONE(); } static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, - struct kvm_inject_args *inject_args) + struct kvm_inject_args *inject_args, + struct test_args *test_args) { kvm_inject_cmd cmd = inject_args->cmd; - uint32_t intid = inject_args->intid; + uint32_t intid = inject_args->first_intid; + uint32_t num = inject_args->num; + uint32_t i; + + assert(intid < UINT_MAX - num); switch (cmd) { case KVM_INJECT_EDGE_IRQ_LINE: - kvm_arm_irq_line(vm, intid, 1); - kvm_arm_irq_line(vm, intid, 0); + for (i = intid; i < intid + num; i++) + kvm_arm_irq_line(vm, i, 1); + for (i = intid; i < intid + num; i++) + kvm_arm_irq_line(vm, i, 0); break; default: break; @@ -222,21 +266,35 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args)); } +static void print_args(struct test_args *args) +{ + printf("nr-irqs=%d\n", args->nr_irqs); +} -static void test_vgic(void) +static void test_vgic(uint32_t nr_irqs) { struct ucall uc; int gic_fd; struct kvm_vm *vm; struct kvm_inject_args inject_args; + struct test_args args = { + .nr_irqs = nr_irqs, + }; + + print_args(&args); + vm = vm_create_default(VCPU_ID, 0, guest_code); ucall_init(vm, NULL); vm_init_descriptor_tables(vm); vcpu_init_descriptor_tables(vm, VCPU_ID); - gic_fd = vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA); + /* Setup the guest args page (so it gets the args). */ + vcpu_args_set(vm, 0, 1, args); + + gic_fd = vgic_v3_setup(vm, 1, nr_irqs, + GICD_BASE_GPA, GICR_BASE_GPA); vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler); @@ -247,7 +305,7 @@ static void test_vgic(void) switch (get_ucall(vm, VCPU_ID, &uc)) { case UCALL_SYNC: kvm_inject_get_call(vm, &uc, &inject_args); - run_guest_cmd(vm, gic_fd, &inject_args); + run_guest_cmd(vm, gic_fd, &inject_args, &args); break; case UCALL_ABORT: TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", @@ -266,12 +324,39 @@ static void test_vgic(void) kvm_vm_free(vm); } -int main(int ac, char **av) +static void help(const char *name) { + printf( + "\n" + "usage: %s [-n num_irqs]\n", name); + printf(" -n: specify the number of IRQs to configure the vgic with.\n"); + puts(""); + exit(1); +} + +int main(int argc, char **argv) +{ + uint32_t nr_irqs = 64; + int opt; + /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - test_vgic(); + while ((opt = getopt(argc, argv, "hg:n:")) != -1) { + switch (opt) { + case 'n': + nr_irqs = atoi(optarg); + if (nr_irqs > 1024 || nr_irqs % 32) + help(argv[0]); + break; + case 'h': + default: + help(argv[0]); + break; + } + } + + test_vgic(nr_irqs); return 0; } diff --git a/tools/testing/selftests/kvm/include/aarch64/vgic.h b/tools/testing/selftests/kvm/include/aarch64/vgic.h index ec8744bb2d4b..ce6f0383c1a1 100644 --- a/tools/testing/selftests/kvm/include/aarch64/vgic.h +++ b/tools/testing/selftests/kvm/include/aarch64/vgic.h @@ -14,7 +14,7 @@ ((uint64_t)(flags) << 12) | \ index) -int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, +int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs, uint64_t gicd_base_gpa, uint64_t gicr_base_gpa); #define VGIC_MAX_RESERVED 1023 diff --git a/tools/testing/selftests/kvm/lib/aarch64/vgic.c b/tools/testing/selftests/kvm/lib/aarch64/vgic.c index a1f1f6c8e2e0..84206d7c92b4 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/vgic.c +++ b/tools/testing/selftests/kvm/lib/aarch64/vgic.c @@ -31,7 +31,7 @@ * redistributor regions of the guest. Since it depends on the number of * vCPUs for the VM, it must be called after all the vCPUs have been created. */ -int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, +int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs, uint64_t gicd_base_gpa, uint64_t gicr_base_gpa) { int gic_fd; @@ -53,6 +53,13 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, /* Distributor setup */ gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); + + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, &nr_irqs, true); + + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true); nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); From 8a35b2877d9a15fa885cea744f1e578e035856fe Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:58 -0800 Subject: [PATCH 09/17] KVM: selftests: aarch64: Cmdline arg to set EOI mode in vgic_irq Add a new cmdline arg to set the EOI mode for all vgic_irq tests. This specifies whether a write to EOIR will deactivate IRQs or not. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-10-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 0b89a29dfe79..3e18fa224280 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -29,6 +29,7 @@ */ struct test_args { uint32_t nr_irqs; /* number of KVM supported IRQs. */ + bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ }; /* @@ -112,7 +113,7 @@ static uint64_t gic_read_ap1r0(void) return reg; } -static void guest_irq_handler(struct ex_regs *regs) +static void guest_irq_generic_handler(bool eoi_split) { uint32_t intid = gic_get_and_ack_irq(); @@ -129,6 +130,8 @@ static void guest_irq_handler(struct ex_regs *regs) gic_set_eoi(intid); GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); + if (eoi_split) + gic_set_dir(intid); GUEST_ASSERT(!gic_irq_get_active(intid)); GUEST_ASSERT(!gic_irq_get_pending(intid)); @@ -151,6 +154,24 @@ do { \ GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ } while (0) +#define CAT_HELPER(a, b) a ## b +#define CAT(a, b) CAT_HELPER(a, b) +#define PREFIX guest_irq_handler_ +#define GUEST_IRQ_HANDLER_NAME(split) CAT(PREFIX, split) +#define GENERATE_GUEST_IRQ_HANDLER(split) \ +static void CAT(PREFIX, split)(struct ex_regs *regs) \ +{ \ + guest_irq_generic_handler(split); \ +} + +GENERATE_GUEST_IRQ_HANDLER(0); +GENERATE_GUEST_IRQ_HANDLER(1); + +static void (*guest_irq_handlers[2])(struct ex_regs *) = { + GUEST_IRQ_HANDLER_NAME(0), + GUEST_IRQ_HANDLER_NAME(1), +}; + static void reset_priorities(struct test_args *args) { int i; @@ -220,6 +241,8 @@ static void guest_code(struct test_args args) for (i = 0; i < nr_irqs; i++) gic_irq_enable(i); + gic_set_eoi_split(args.eoi_split); + reset_priorities(&args); gic_set_priority_mask(CPU_PRIO_MASK); @@ -268,10 +291,11 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, static void print_args(struct test_args *args) { - printf("nr-irqs=%d\n", args->nr_irqs); + printf("nr-irqs=%d eoi-split=%d\n", + args->nr_irqs, args->eoi_split); } -static void test_vgic(uint32_t nr_irqs) +static void test_vgic(uint32_t nr_irqs, bool eoi_split) { struct ucall uc; int gic_fd; @@ -280,6 +304,7 @@ static void test_vgic(uint32_t nr_irqs) struct test_args args = { .nr_irqs = nr_irqs, + .eoi_split = eoi_split, }; print_args(&args); @@ -297,7 +322,7 @@ static void test_vgic(uint32_t nr_irqs) GICD_BASE_GPA, GICR_BASE_GPA); vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, - guest_irq_handler); + guest_irq_handlers[args.eoi_split]); while (1) { vcpu_run(vm, VCPU_ID); @@ -328,8 +353,11 @@ static void help(const char *name) { printf( "\n" - "usage: %s [-n num_irqs]\n", name); - printf(" -n: specify the number of IRQs to configure the vgic with.\n"); + "usage: %s [-n num_irqs] [-e eoi_split]\n", name); + printf(" -n: specify the number of IRQs to configure the vgic with. " + "It has to be a multiple of 32 and between 64 and 1024.\n"); + printf(" -e: if 1 then EOI is split into a write to DIR on top " + "of writing EOI.\n"); puts(""); exit(1); } @@ -337,18 +365,24 @@ static void help(const char *name) int main(int argc, char **argv) { uint32_t nr_irqs = 64; + bool default_args = true; int opt; + bool eoi_split = false; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - while ((opt = getopt(argc, argv, "hg:n:")) != -1) { + while ((opt = getopt(argc, argv, "hn:e:")) != -1) { switch (opt) { case 'n': nr_irqs = atoi(optarg); if (nr_irqs > 1024 || nr_irqs % 32) help(argv[0]); break; + case 'e': + eoi_split = (bool)atoi(optarg); + default_args = false; + break; case 'h': default: help(argv[0]); @@ -356,7 +390,15 @@ int main(int argc, char **argv) } } - test_vgic(nr_irqs); + /* If the user just specified nr_irqs and/or gic_version, then run all + * combinations. + */ + if (default_args) { + test_vgic(nr_irqs, false /* eoi_split */); + test_vgic(nr_irqs, true /* eoi_split */); + } else { + test_vgic(nr_irqs, eoi_split); + } return 0; } From 0ad3ff4a6adc4922808ef8b2f91880c25195f509 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:38:59 -0800 Subject: [PATCH 10/17] KVM: selftests: aarch64: Add preemption tests in vgic_irq Add tests for IRQ preemption (having more than one activated IRQ at the same time). This test injects multiple concurrent IRQs and handles them without handling the actual exceptions. This is done by masking interrupts for the whole test. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-11-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 3e18fa224280..b9080aa75a14 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -41,6 +41,7 @@ struct test_args { */ #define KVM_NUM_PRIOS 32 #define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */ +#define KVM_PRIO_STEPS (1 << KVM_PRIO_SHIFT) /* 8 */ #define LOWEST_PRIO (KVM_NUM_PRIOS - 1) #define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */ #define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1) @@ -212,6 +213,74 @@ static void guest_inject(struct test_args *args, reset_priorities(args); } +/* + * Polls the IAR until it's not a spurious interrupt. + * + * This function should only be used in test_inject_preemption (with IRQs + * masked). + */ +static uint32_t wait_for_and_activate_irq(void) +{ + uint32_t intid; + + do { + asm volatile("wfi" : : : "memory"); + intid = gic_get_and_ack_irq(); + } while (intid == IAR_SPURIOUS); + + return intid; +} + +/* + * Inject multiple concurrent IRQs (num IRQs starting at first_intid) and + * handle them without handling the actual exceptions. This is done by masking + * interrupts for the whole test. + */ +static void test_inject_preemption(struct test_args *args, + uint32_t first_intid, int num, + kvm_inject_cmd cmd) +{ + uint32_t intid, prio, step = KVM_PRIO_STEPS; + int i; + + /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs + * in descending order, so intid+1 can preempt intid. + */ + for (i = 0, prio = (num - 1) * step; i < num; i++, prio -= step) { + GUEST_ASSERT(prio >= 0); + intid = i + first_intid; + gic_set_priority(intid, prio); + } + + local_irq_disable(); + + for (i = 0; i < num; i++) { + uint32_t tmp; + intid = i + first_intid; + kvm_inject_call(cmd, intid, 1); + /* Each successive IRQ will preempt the previous one. */ + tmp = wait_for_and_activate_irq(); + GUEST_ASSERT_EQ(tmp, intid); + } + + /* finish handling the IRQs starting with the highest priority one. */ + for (i = 0; i < num; i++) { + intid = num - i - 1 + first_intid; + gic_set_eoi(intid); + if (args->eoi_split) + gic_set_dir(intid); + } + + local_irq_enable(); + + for (i = 0; i < num; i++) + GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); + GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); + GUEST_ASSERT_IAR_EMPTY(); + + reset_priorities(args); +} + static void test_injection(struct test_args *args, struct kvm_inject_desc *f) { uint32_t nr_irqs = args->nr_irqs; @@ -231,6 +300,24 @@ static void test_injection(struct test_args *args, struct kvm_inject_desc *f) } } +static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) +{ + /* + * Test up to 4 levels of preemption. The reason is that KVM doesn't + * currently implement the ability to have more than the number-of-LRs + * number of concurrently active IRQs. The number of LRs implemented is + * IMPLEMENTATION DEFINED, however, it seems that most implement 4. + */ + if (f->sgi) + test_inject_preemption(args, MIN_SGI, 4, f->cmd); + + if (f->ppi) + test_inject_preemption(args, MIN_PPI, 4, f->cmd); + + if (f->spi) + test_inject_preemption(args, MIN_SPI, 4, f->cmd); +} + static void guest_code(struct test_args args) { uint32_t i, nr_irqs = args.nr_irqs; @@ -249,8 +336,10 @@ static void guest_code(struct test_args args) local_irq_enable(); /* Start the tests. */ - for_each_inject_fn(inject_edge_fns, f) + for_each_inject_fn(inject_edge_fns, f) { test_injection(&args, f); + test_preemption(&args, f); + } GUEST_DONE(); } From 92f2cc4aa7964d4d13681eeb38582bb989b01b98 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:00 -0800 Subject: [PATCH 11/17] KVM: selftests: aarch64: Level-sensitive interrupts tests in vgic_irq Add a cmdline arg for using level-sensitive interrupts (vs the default edge-triggered). Then move the handler into a generic handler function that takes the type of interrupt (level vs. edge) as an arg. When handling line-sensitive interrupts it sets the line to low after acknowledging the IRQ. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-12-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 118 +++++++++++++----- 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index b9080aa75a14..a20d225367a1 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -30,6 +30,7 @@ struct test_args { uint32_t nr_irqs; /* number of KVM supported IRQs. */ bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ + bool level_sensitive; /* 1 is level, 0 is edge */ }; /* @@ -57,27 +58,31 @@ static void *redist = (void *)GICR_BASE_GPA; typedef enum { KVM_INJECT_EDGE_IRQ_LINE = 1, + KVM_SET_IRQ_LINE, + KVM_SET_IRQ_LINE_HIGH, } kvm_inject_cmd; struct kvm_inject_args { kvm_inject_cmd cmd; uint32_t first_intid; uint32_t num; + int level; }; /* Used on the guest side to perform the hypercall. */ -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num); - -#define KVM_INJECT(cmd, intid) \ - kvm_inject_call(cmd, intid, 1) - -#define KVM_INJECT_MULTI(cmd, intid, num) \ - kvm_inject_call(cmd, intid, num) +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, + uint32_t num, int level); /* Used on the host side to get the hypercall info. */ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, struct kvm_inject_args *args); +#define KVM_INJECT(cmd, intid) \ + kvm_inject_call(cmd, intid, 1, -1 /* not used */) + +#define KVM_INJECT_MULTI(cmd, intid, num) \ + kvm_inject_call(cmd, intid, num, -1 /* not used */) + struct kvm_inject_desc { kvm_inject_cmd cmd; /* can inject PPIs, PPIs, and/or SPIs. */ @@ -90,6 +95,12 @@ static struct kvm_inject_desc inject_edge_fns[] = { { 0, }, }; +static struct kvm_inject_desc inject_level_fns[] = { + /* sgi ppi spi */ + { KVM_SET_IRQ_LINE_HIGH, false, true, true }, + { 0, }, +}; + #define for_each_inject_fn(t, f) \ for ((f) = (t); (f)->cmd; (f)++) @@ -114,7 +125,9 @@ static uint64_t gic_read_ap1r0(void) return reg; } -static void guest_irq_generic_handler(bool eoi_split) +static void guest_set_irq_line(uint32_t intid, uint32_t level); + +static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) { uint32_t intid = gic_get_and_ack_irq(); @@ -123,7 +136,11 @@ static void guest_irq_generic_handler(bool eoi_split) GUEST_ASSERT(gic_irq_get_active(intid)); - GUEST_ASSERT(!gic_irq_get_pending(intid)); + if (!level_sensitive) + GUEST_ASSERT(!gic_irq_get_pending(intid)); + + if (level_sensitive) + guest_set_irq_line(intid, 0); GUEST_ASSERT(intid < MAX_SPI); irqnr_received[intid] += 1; @@ -138,12 +155,14 @@ static void guest_irq_generic_handler(bool eoi_split) GUEST_ASSERT(!gic_irq_get_pending(intid)); } -static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num) +static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, + uint32_t num, int level) { struct kvm_inject_args args = { .cmd = cmd, .first_intid = first_intid, .num = num, + .level = level, }; GUEST_SYNC(&args); } @@ -158,19 +177,21 @@ do { \ #define CAT_HELPER(a, b) a ## b #define CAT(a, b) CAT_HELPER(a, b) #define PREFIX guest_irq_handler_ -#define GUEST_IRQ_HANDLER_NAME(split) CAT(PREFIX, split) -#define GENERATE_GUEST_IRQ_HANDLER(split) \ -static void CAT(PREFIX, split)(struct ex_regs *regs) \ +#define GUEST_IRQ_HANDLER_NAME(split, lev) CAT(PREFIX, CAT(split, lev)) +#define GENERATE_GUEST_IRQ_HANDLER(split, lev) \ +static void CAT(PREFIX, CAT(split, lev))(struct ex_regs *regs) \ { \ - guest_irq_generic_handler(split); \ + guest_irq_generic_handler(split, lev); \ } -GENERATE_GUEST_IRQ_HANDLER(0); -GENERATE_GUEST_IRQ_HANDLER(1); +GENERATE_GUEST_IRQ_HANDLER(0, 0); +GENERATE_GUEST_IRQ_HANDLER(0, 1); +GENERATE_GUEST_IRQ_HANDLER(1, 0); +GENERATE_GUEST_IRQ_HANDLER(1, 1); -static void (*guest_irq_handlers[2])(struct ex_regs *) = { - GUEST_IRQ_HANDLER_NAME(0), - GUEST_IRQ_HANDLER_NAME(1), +static void (*guest_irq_handlers[2][2])(struct ex_regs *) = { + {GUEST_IRQ_HANDLER_NAME(0, 0), GUEST_IRQ_HANDLER_NAME(0, 1),}, + {GUEST_IRQ_HANDLER_NAME(1, 0), GUEST_IRQ_HANDLER_NAME(1, 1),}, }; static void reset_priorities(struct test_args *args) @@ -181,6 +202,11 @@ static void reset_priorities(struct test_args *args) gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); } +static void guest_set_irq_line(uint32_t intid, uint32_t level) +{ + kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level); +} + static void guest_inject(struct test_args *args, uint32_t first_intid, uint32_t num, kvm_inject_cmd cmd) @@ -257,10 +283,12 @@ static void test_inject_preemption(struct test_args *args, for (i = 0; i < num; i++) { uint32_t tmp; intid = i + first_intid; - kvm_inject_call(cmd, intid, 1); + KVM_INJECT(cmd, intid); /* Each successive IRQ will preempt the previous one. */ tmp = wait_for_and_activate_irq(); GUEST_ASSERT_EQ(tmp, intid); + if (args->level_sensitive) + guest_set_irq_line(intid, 0); } /* finish handling the IRQs starting with the highest priority one. */ @@ -321,22 +349,29 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) static void guest_code(struct test_args args) { uint32_t i, nr_irqs = args.nr_irqs; - struct kvm_inject_desc *f; + bool level_sensitive = args.level_sensitive; + struct kvm_inject_desc *f, *inject_fns; gic_init(GIC_V3, 1, dist, redist); for (i = 0; i < nr_irqs; i++) gic_irq_enable(i); + for (i = MIN_SPI; i < nr_irqs; i++) + gic_irq_set_config(i, !args.level_sensitive); + gic_set_eoi_split(args.eoi_split); reset_priorities(&args); gic_set_priority_mask(CPU_PRIO_MASK); + inject_fns = level_sensitive ? inject_level_fns + : inject_edge_fns; + local_irq_enable(); /* Start the tests. */ - for_each_inject_fn(inject_edge_fns, f) { + for_each_inject_fn(inject_fns, f) { test_injection(&args, f); test_preemption(&args, f); } @@ -351,6 +386,7 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, kvm_inject_cmd cmd = inject_args->cmd; uint32_t intid = inject_args->first_intid; uint32_t num = inject_args->num; + int level = inject_args->level; uint32_t i; assert(intid < UINT_MAX - num); @@ -362,6 +398,14 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, for (i = intid; i < intid + num; i++) kvm_arm_irq_line(vm, i, 0); break; + case KVM_SET_IRQ_LINE: + for (i = intid; i < intid + num; i++) + kvm_arm_irq_line(vm, i, level); + break; + case KVM_SET_IRQ_LINE_HIGH: + for (i = intid; i < intid + num; i++) + kvm_arm_irq_line(vm, i, 1); + break; default: break; } @@ -380,11 +424,12 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, static void print_args(struct test_args *args) { - printf("nr-irqs=%d eoi-split=%d\n", - args->nr_irqs, args->eoi_split); + printf("nr-irqs=%d level-sensitive=%d eoi-split=%d\n", + args->nr_irqs, args->level_sensitive, + args->eoi_split); } -static void test_vgic(uint32_t nr_irqs, bool eoi_split) +static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) { struct ucall uc; int gic_fd; @@ -393,6 +438,7 @@ static void test_vgic(uint32_t nr_irqs, bool eoi_split) struct test_args args = { .nr_irqs = nr_irqs, + .level_sensitive = level_sensitive, .eoi_split = eoi_split, }; @@ -411,7 +457,7 @@ static void test_vgic(uint32_t nr_irqs, bool eoi_split) GICD_BASE_GPA, GICR_BASE_GPA); vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, - guest_irq_handlers[args.eoi_split]); + guest_irq_handlers[args.eoi_split][args.level_sensitive]); while (1) { vcpu_run(vm, VCPU_ID); @@ -442,11 +488,12 @@ static void help(const char *name) { printf( "\n" - "usage: %s [-n num_irqs] [-e eoi_split]\n", name); - printf(" -n: specify the number of IRQs to configure the vgic with. " + "usage: %s [-n num_irqs] [-e eoi_split] [-l level_sensitive]\n", name); + printf(" -n: specify number of IRQs to setup the vgic with. " "It has to be a multiple of 32 and between 64 and 1024.\n"); printf(" -e: if 1 then EOI is split into a write to DIR on top " "of writing EOI.\n"); + printf(" -l: specify whether the IRQs are level-sensitive (1) or not (0)."); puts(""); exit(1); } @@ -455,13 +502,14 @@ int main(int argc, char **argv) { uint32_t nr_irqs = 64; bool default_args = true; + bool level_sensitive = false; int opt; bool eoi_split = false; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - while ((opt = getopt(argc, argv, "hn:e:")) != -1) { + while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) { switch (opt) { case 'n': nr_irqs = atoi(optarg); @@ -472,6 +520,10 @@ int main(int argc, char **argv) eoi_split = (bool)atoi(optarg); default_args = false; break; + case 'l': + level_sensitive = (bool)atoi(optarg); + default_args = false; + break; case 'h': default: help(argv[0]); @@ -483,10 +535,12 @@ int main(int argc, char **argv) * combinations. */ if (default_args) { - test_vgic(nr_irqs, false /* eoi_split */); - test_vgic(nr_irqs, true /* eoi_split */); + test_vgic(nr_irqs, false /* level */, false /* eoi_split */); + test_vgic(nr_irqs, false /* level */, true /* eoi_split */); + test_vgic(nr_irqs, true /* level */, false /* eoi_split */); + test_vgic(nr_irqs, true /* level */, true /* eoi_split */); } else { - test_vgic(nr_irqs, eoi_split); + test_vgic(nr_irqs, level_sensitive, eoi_split); } return 0; From 6830fa915912587a7aa304bade01b366cf0b9214 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:01 -0800 Subject: [PATCH 12/17] KVM: selftests: aarch64: Add tests for LEVEL_INFO in vgic_irq Add injection tests for the LEVEL_INFO ioctl (level-sensitive specific) into vgic_irq. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-13-ricarkol@google.com --- tools/testing/selftests/kvm/aarch64/vgic_irq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index a20d225367a1..bc1b6fd684fc 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -60,6 +60,7 @@ typedef enum { KVM_INJECT_EDGE_IRQ_LINE = 1, KVM_SET_IRQ_LINE, KVM_SET_IRQ_LINE_HIGH, + KVM_SET_LEVEL_INFO_HIGH, } kvm_inject_cmd; struct kvm_inject_args { @@ -98,6 +99,7 @@ static struct kvm_inject_desc inject_edge_fns[] = { static struct kvm_inject_desc inject_level_fns[] = { /* sgi ppi spi */ { KVM_SET_IRQ_LINE_HIGH, false, true, true }, + { KVM_SET_LEVEL_INFO_HIGH, false, true, true }, { 0, }, }; @@ -406,6 +408,10 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, for (i = intid; i < intid + num; i++) kvm_arm_irq_line(vm, i, 1); break; + case KVM_SET_LEVEL_INFO_HIGH: + for (i = intid; i < intid + num; i++) + kvm_irq_set_level_info(gic_fd, i, 1); + break; default: break; } From 90f50acac9ee9f81192098c22b2cbf2491a40263 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:02 -0800 Subject: [PATCH 13/17] KVM: selftests: aarch64: Add test_inject_fail to vgic_irq Add tests for failed injections to vgic_irq. This tests that KVM can handle bogus IRQ numbers. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-14-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 122 +++++++++++++++--- .../testing/selftests/kvm/lib/aarch64/vgic.c | 7 +- 2 files changed, 109 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index bc1b6fd684fc..9f1674b3a45c 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -68,21 +68,28 @@ struct kvm_inject_args { uint32_t first_intid; uint32_t num; int level; + bool expect_failure; }; /* Used on the guest side to perform the hypercall. */ static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, - uint32_t num, int level); + uint32_t num, int level, bool expect_failure); /* Used on the host side to get the hypercall info. */ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, struct kvm_inject_args *args); -#define KVM_INJECT(cmd, intid) \ - kvm_inject_call(cmd, intid, 1, -1 /* not used */) +#define _KVM_INJECT_MULTI(cmd, intid, num, expect_failure) \ + kvm_inject_call(cmd, intid, num, -1 /* not used */, expect_failure) #define KVM_INJECT_MULTI(cmd, intid, num) \ - kvm_inject_call(cmd, intid, num, -1 /* not used */) + _KVM_INJECT_MULTI(cmd, intid, num, false) + +#define _KVM_INJECT(cmd, intid, expect_failure) \ + _KVM_INJECT_MULTI(cmd, intid, 1, expect_failure) + +#define KVM_INJECT(cmd, intid) \ + _KVM_INJECT_MULTI(cmd, intid, 1, false) struct kvm_inject_desc { kvm_inject_cmd cmd; @@ -158,13 +165,14 @@ static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) } static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, - uint32_t num, int level) + uint32_t num, int level, bool expect_failure) { struct kvm_inject_args args = { .cmd = cmd, .first_intid = first_intid, .num = num, .level = level, + .expect_failure = expect_failure, }; GUEST_SYNC(&args); } @@ -206,7 +214,19 @@ static void reset_priorities(struct test_args *args) static void guest_set_irq_line(uint32_t intid, uint32_t level) { - kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level); + kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level, false); +} + +static void test_inject_fail(struct test_args *args, + uint32_t intid, kvm_inject_cmd cmd) +{ + reset_stats(); + + _KVM_INJECT(cmd, intid, true); + /* no IRQ to handle on entry */ + + GUEST_ASSERT_EQ(irq_handled, 0); + GUEST_ASSERT_IAR_EMPTY(); } static void guest_inject(struct test_args *args, @@ -330,6 +350,16 @@ static void test_injection(struct test_args *args, struct kvm_inject_desc *f) } } +static void test_injection_failure(struct test_args *args, + struct kvm_inject_desc *f) +{ + uint32_t bad_intid[] = { args->nr_irqs, 1020, 1024, 1120, 5120, ~0U, }; + int i; + + for (i = 0; i < ARRAY_SIZE(bad_intid); i++) + test_inject_fail(args, bad_intid[i], f->cmd); +} + static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) { /* @@ -376,11 +406,61 @@ static void guest_code(struct test_args args) for_each_inject_fn(inject_fns, f) { test_injection(&args, f); test_preemption(&args, f); + test_injection_failure(&args, f); } GUEST_DONE(); } +static void kvm_irq_line_check(struct kvm_vm *vm, uint32_t intid, int level, + struct test_args *test_args, bool expect_failure) +{ + int ret; + + if (!expect_failure) { + kvm_arm_irq_line(vm, intid, level); + } else { + /* The interface doesn't allow larger intid's. */ + if (intid > KVM_ARM_IRQ_NUM_MASK) + return; + + ret = _kvm_arm_irq_line(vm, intid, level); + TEST_ASSERT(ret != 0 && errno == EINVAL, + "Bad intid %i did not cause KVM_IRQ_LINE " + "error: rc: %i errno: %i", intid, ret, errno); + } +} + +void kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, + bool expect_failure) +{ + if (!expect_failure) { + kvm_irq_set_level_info(gic_fd, intid, level); + } else { + int ret = _kvm_irq_set_level_info(gic_fd, intid, level); + /* + * The kernel silently fails for invalid SPIs and SGIs (which + * are not level-sensitive). It only checks for intid to not + * spill over 1U << 10 (the max reserved SPI). Also, callers + * are supposed to mask the intid with 0x3ff (1023). + */ + if (intid > VGIC_MAX_RESERVED) + TEST_ASSERT(ret != 0 && errno == EINVAL, + "Bad intid %i did not cause VGIC_GRP_LEVEL_INFO " + "error: rc: %i errno: %i", intid, ret, errno); + else + TEST_ASSERT(!ret, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO " + "for intid %i failed, rc: %i errno: %i", + intid, ret, errno); + } +} + +/* handles the valid case: intid=0xffffffff num=1 */ +#define for_each_intid(first, num, tmp, i) \ + for ((tmp) = (i) = (first); \ + (tmp) < (uint64_t)(first) + (uint64_t)(num); \ + (tmp)++, (i)++) + static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, struct kvm_inject_args *inject_args, struct test_args *test_args) @@ -389,28 +469,36 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, uint32_t intid = inject_args->first_intid; uint32_t num = inject_args->num; int level = inject_args->level; + bool expect_failure = inject_args->expect_failure; + uint64_t tmp; uint32_t i; - assert(intid < UINT_MAX - num); + /* handles the valid case: intid=0xffffffff num=1 */ + assert(intid < UINT_MAX - num || num == 1); switch (cmd) { case KVM_INJECT_EDGE_IRQ_LINE: - for (i = intid; i < intid + num; i++) - kvm_arm_irq_line(vm, i, 1); - for (i = intid; i < intid + num; i++) - kvm_arm_irq_line(vm, i, 0); + for_each_intid(intid, num, tmp, i) + kvm_irq_line_check(vm, i, 1, test_args, + expect_failure); + for_each_intid(intid, num, tmp, i) + kvm_irq_line_check(vm, i, 0, test_args, + expect_failure); break; case KVM_SET_IRQ_LINE: - for (i = intid; i < intid + num; i++) - kvm_arm_irq_line(vm, i, level); + for_each_intid(intid, num, tmp, i) + kvm_irq_line_check(vm, i, level, test_args, + expect_failure); break; case KVM_SET_IRQ_LINE_HIGH: - for (i = intid; i < intid + num; i++) - kvm_arm_irq_line(vm, i, 1); + for_each_intid(intid, num, tmp, i) + kvm_irq_line_check(vm, i, 1, test_args, + expect_failure); break; case KVM_SET_LEVEL_INFO_HIGH: - for (i = intid; i < intid + num; i++) - kvm_irq_set_level_info(gic_fd, i, 1); + for_each_intid(intid, num, tmp, i) + kvm_irq_set_level_info_check(gic_fd, i, 1, + expect_failure); break; default: break; diff --git a/tools/testing/selftests/kvm/lib/aarch64/vgic.c b/tools/testing/selftests/kvm/lib/aarch64/vgic.c index 84206d7c92b4..b3a0fca0d780 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/vgic.c +++ b/tools/testing/selftests/kvm/lib/aarch64/vgic.c @@ -110,12 +110,13 @@ int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) { uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK; + TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself " + "doesn't allow injecting SGIs. There's no mask for it."); + 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."); + irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT; return _kvm_irq_line(vm, irq, level); } From 88209c104e9b3e95502c0e924fb1cd8bd5a01d82 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:03 -0800 Subject: [PATCH 14/17] KVM: selftests: Add IRQ GSI routing library functions Add an architecture independent wrapper function for creating and writing IRQ GSI routing tables. Also add a function to add irqchip entries. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-15-ricarkol@google.com --- .../testing/selftests/kvm/include/kvm_util.h | 8 +++ tools/testing/selftests/kvm/lib/kvm_util.c | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 4ba8fd44cf95..db36e6c49983 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -252,6 +252,14 @@ int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, uint64_t attr, void *val, bool write); +#define KVM_MAX_IRQ_ROUTES 4096 + +struct kvm_irq_routing *kvm_gsi_routing_create(void); +void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, + uint32_t gsi, uint32_t pin); +int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); +void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); + const char *exit_reason_str(unsigned int exit_reason); void virt_pgd_alloc(struct kvm_vm *vm); diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 9f7210c2d75e..68b56e7e6808 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -2107,6 +2107,57 @@ void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) TEST_ASSERT(ret >= 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", ret, errno); } +struct kvm_irq_routing *kvm_gsi_routing_create(void) +{ + struct kvm_irq_routing *routing; + size_t size; + + size = sizeof(struct kvm_irq_routing); + /* Allocate space for the max number of entries: this wastes 196 KBs. */ + size += KVM_MAX_IRQ_ROUTES * sizeof(struct kvm_irq_routing_entry); + routing = calloc(1, size); + assert(routing); + + return routing; +} + +void kvm_gsi_routing_irqchip_add(struct kvm_irq_routing *routing, + uint32_t gsi, uint32_t pin) +{ + int i; + + assert(routing); + assert(routing->nr < KVM_MAX_IRQ_ROUTES); + + i = routing->nr; + routing->entries[i].gsi = gsi; + routing->entries[i].type = KVM_IRQ_ROUTING_IRQCHIP; + routing->entries[i].flags = 0; + routing->entries[i].u.irqchip.irqchip = 0; + routing->entries[i].u.irqchip.pin = pin; + routing->nr++; +} + +int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) +{ + int ret; + + assert(routing); + ret = ioctl(vm_get_fd(vm), KVM_SET_GSI_ROUTING, routing); + free(routing); + + return ret; +} + +void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) +{ + int ret; + + ret = _kvm_gsi_routing_write(vm, routing); + TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING failed, rc: %i errno: %i", + ret, errno); +} + /* * VM Dump * From 6a5a47188caca7be4bbe28cdb31d5df09868ed5c Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:04 -0800 Subject: [PATCH 15/17] KVM: selftests: aarch64: Add tests for IRQFD in vgic_irq Add injection tests for the KVM_IRQFD ioctl into vgic_irq. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-16-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 101 +++++++++++++++++- .../selftests/kvm/include/aarch64/vgic.h | 2 + 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 9f1674b3a45c..121113f24ed3 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "processor.h" @@ -31,6 +32,8 @@ struct test_args { uint32_t nr_irqs; /* number of KVM supported IRQs. */ bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ bool level_sensitive; /* 1 is level, 0 is edge */ + int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */ + bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */ }; /* @@ -61,6 +64,7 @@ typedef enum { KVM_SET_IRQ_LINE, KVM_SET_IRQ_LINE_HIGH, KVM_SET_LEVEL_INFO_HIGH, + KVM_INJECT_IRQFD, } kvm_inject_cmd; struct kvm_inject_args { @@ -100,6 +104,7 @@ struct kvm_inject_desc { static struct kvm_inject_desc inject_edge_fns[] = { /* sgi ppi spi */ { KVM_INJECT_EDGE_IRQ_LINE, false, false, true }, + { KVM_INJECT_IRQFD, false, false, true }, { 0, }, }; @@ -107,12 +112,17 @@ static struct kvm_inject_desc inject_level_fns[] = { /* sgi ppi spi */ { KVM_SET_IRQ_LINE_HIGH, false, true, true }, { KVM_SET_LEVEL_INFO_HIGH, false, true, true }, + { KVM_INJECT_IRQFD, false, false, true }, { 0, }, }; #define for_each_inject_fn(t, f) \ for ((f) = (t); (f)->cmd; (f)++) +#define for_each_supported_inject_fn(args, t, f) \ + for_each_inject_fn(t, f) \ + if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD) + /* Shared between the guest main thread and the IRQ handlers. */ volatile uint64_t irq_handled; volatile uint32_t irqnr_received[MAX_SPI + 1]; @@ -403,7 +413,7 @@ static void guest_code(struct test_args args) local_irq_enable(); /* Start the tests. */ - for_each_inject_fn(inject_fns, f) { + for_each_supported_inject_fn(&args, inject_fns, f) { test_injection(&args, f); test_preemption(&args, f); test_injection_failure(&args, f); @@ -455,6 +465,88 @@ void kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, } } +static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, + uint32_t intid, uint32_t num, uint32_t kvm_max_routes, + bool expect_failure) +{ + struct kvm_irq_routing *routing; + int ret; + uint64_t i; + + assert(num <= kvm_max_routes && kvm_max_routes <= KVM_MAX_IRQ_ROUTES); + + routing = kvm_gsi_routing_create(); + for (i = intid; i < (uint64_t)intid + num; i++) + kvm_gsi_routing_irqchip_add(routing, i - MIN_SPI, i - MIN_SPI); + + if (!expect_failure) { + kvm_gsi_routing_write(vm, routing); + } else { + ret = _kvm_gsi_routing_write(vm, routing); + /* The kernel only checks for KVM_IRQCHIP_NUM_PINS. */ + if (intid >= KVM_IRQCHIP_NUM_PINS) + TEST_ASSERT(ret != 0 && errno == EINVAL, + "Bad intid %u did not cause KVM_SET_GSI_ROUTING " + "error: rc: %i errno: %i", intid, ret, errno); + else + TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING " + "for intid %i failed, rc: %i errno: %i", + intid, ret, errno); + } +} + +static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, + uint32_t intid, uint32_t num, uint32_t kvm_max_routes, + bool expect_failure) +{ + int fd[MAX_SPI]; + uint64_t val; + int ret, f; + uint64_t i; + + /* + * There is no way to try injecting an SGI or PPI as the interface + * starts counting from the first SPI (above the private ones), so just + * exit. + */ + if (INTID_IS_SGI(intid) || INTID_IS_PPI(intid)) + return; + + kvm_set_gsi_routing_irqchip_check(vm, intid, num, + kvm_max_routes, expect_failure); + + /* + * If expect_failure, then just to inject anyway. These + * will silently fail. And in any case, the guest will check + * that no actual interrupt was injected for those cases. + */ + + for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { + fd[f] = eventfd(0, 0); + TEST_ASSERT(fd[f] != -1, + "eventfd failed, errno: %i\n", errno); + } + + for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { + struct kvm_irqfd irqfd = { + .fd = fd[f], + .gsi = i - MIN_SPI, + }; + assert(i <= (uint64_t)UINT_MAX); + vm_ioctl(vm, KVM_IRQFD, &irqfd); + } + + for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { + val = 1; + ret = write(fd[f], &val, sizeof(uint64_t)); + TEST_ASSERT(ret == sizeof(uint64_t), + "Write to KVM_IRQFD failed with ret: %d\n", ret); + } + + for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) + close(fd[f]); +} + /* handles the valid case: intid=0xffffffff num=1 */ #define for_each_intid(first, num, tmp, i) \ for ((tmp) = (i) = (first); \ @@ -500,6 +592,11 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, kvm_irq_set_level_info_check(gic_fd, i, 1, expect_failure); break; + case KVM_INJECT_IRQFD: + kvm_routing_and_irqfd_check(vm, intid, num, + test_args->kvm_max_routes, + expect_failure); + break; default: break; } @@ -534,6 +631,8 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) .nr_irqs = nr_irqs, .level_sensitive = level_sensitive, .eoi_split = eoi_split, + .kvm_max_routes = kvm_check_cap(KVM_CAP_IRQ_ROUTING), + .kvm_supports_irqfd = kvm_check_cap(KVM_CAP_IRQFD), }; print_args(&args); diff --git a/tools/testing/selftests/kvm/include/aarch64/vgic.h b/tools/testing/selftests/kvm/include/aarch64/vgic.h index ce6f0383c1a1..4442081221a0 100644 --- a/tools/testing/selftests/kvm/include/aarch64/vgic.h +++ b/tools/testing/selftests/kvm/include/aarch64/vgic.h @@ -29,4 +29,6 @@ int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); 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); +#define KVM_IRQCHIP_NUM_PINS (1020 - 32) + #endif // SELFTEST_KVM_VGIC_H From bebd8f3f869361e0249efe423ba76a0d991ce3e6 Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:05 -0800 Subject: [PATCH 16/17] KVM: selftests: aarch64: Add ISPENDR write tests in vgic_irq Add injection tests that use writing into the ISPENDR register (to mark IRQs as pending). This is typically used by migration code. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-17-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 121113f24ed3..ab39f0bf18e7 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -65,6 +65,7 @@ typedef enum { KVM_SET_IRQ_LINE_HIGH, KVM_SET_LEVEL_INFO_HIGH, KVM_INJECT_IRQFD, + KVM_WRITE_ISPENDR, } kvm_inject_cmd; struct kvm_inject_args { @@ -105,6 +106,7 @@ static struct kvm_inject_desc inject_edge_fns[] = { /* sgi ppi spi */ { KVM_INJECT_EDGE_IRQ_LINE, false, false, true }, { KVM_INJECT_IRQFD, false, false, true }, + { KVM_WRITE_ISPENDR, true, false, true }, { 0, }, }; @@ -113,6 +115,7 @@ static struct kvm_inject_desc inject_level_fns[] = { { KVM_SET_IRQ_LINE_HIGH, false, true, true }, { KVM_SET_LEVEL_INFO_HIGH, false, true, true }, { KVM_INJECT_IRQFD, false, false, true }, + { KVM_WRITE_ISPENDR, false, true, true }, { 0, }, }; @@ -495,6 +498,20 @@ static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, } } +static void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, + uint32_t vcpu, bool expect_failure) +{ + /* + * Ignore this when expecting failure as invalid intids will lead to + * either trying to inject SGIs when we configured the test to be + * level_sensitive (or the reverse), or inject large intids which + * will lead to writing above the ISPENDR register space (and we + * don't want to do that either). + */ + if (!expect_failure) + kvm_irq_write_ispendr(gic_fd, intid, vcpu); +} + static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, uint32_t intid, uint32_t num, uint32_t kvm_max_routes, bool expect_failure) @@ -597,6 +614,11 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, test_args->kvm_max_routes, expect_failure); break; + case KVM_WRITE_ISPENDR: + for (i = intid; i < intid + num; i++) + kvm_irq_write_ispendr_check(gic_fd, i, + VCPU_ID, expect_failure); + break; default: break; } From 728fcc46d2c2292d1ac73f3491b8f4332066fdad Mon Sep 17 00:00:00 2001 From: Ricardo Koller Date: Mon, 8 Nov 2021 18:39:06 -0800 Subject: [PATCH 17/17] KVM: selftests: aarch64: Add test for restoring active IRQs Add a test that restores multiple IRQs in active state, it does it by writing into ISACTIVER from the guest and using KVM ioctls. This test tries to emulate what would happen during a live migration: restore active IRQs. Signed-off-by: Ricardo Koller Acked-by: Andrew Jones Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20211109023906.1091208-18-ricarkol@google.com --- .../testing/selftests/kvm/aarch64/vgic_irq.c | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index ab39f0bf18e7..e6c7d7f8fbd1 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -66,6 +66,7 @@ typedef enum { KVM_SET_LEVEL_INFO_HIGH, KVM_INJECT_IRQFD, KVM_WRITE_ISPENDR, + KVM_WRITE_ISACTIVER, } kvm_inject_cmd; struct kvm_inject_args { @@ -96,6 +97,9 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, #define KVM_INJECT(cmd, intid) \ _KVM_INJECT_MULTI(cmd, intid, 1, false) +#define KVM_ACTIVATE(cmd, intid) \ + kvm_inject_call(cmd, intid, 1, 1, false); + struct kvm_inject_desc { kvm_inject_cmd cmd; /* can inject PPIs, PPIs, and/or SPIs. */ @@ -119,6 +123,12 @@ static struct kvm_inject_desc inject_level_fns[] = { { 0, }, }; +static struct kvm_inject_desc set_active_fns[] = { + /* sgi ppi spi */ + { KVM_WRITE_ISACTIVER, true, true, true }, + { 0, }, +}; + #define for_each_inject_fn(t, f) \ for ((f) = (t); (f)->cmd; (f)++) @@ -126,6 +136,9 @@ static struct kvm_inject_desc inject_level_fns[] = { for_each_inject_fn(t, f) \ if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD) +#define for_each_supported_activate_fn(args, t, f) \ + for_each_supported_inject_fn((args), (t), (f)) + /* Shared between the guest main thread and the IRQ handlers. */ volatile uint64_t irq_handled; volatile uint32_t irqnr_received[MAX_SPI + 1]; @@ -147,6 +160,12 @@ static uint64_t gic_read_ap1r0(void) return reg; } +static void gic_write_ap1r0(uint64_t val) +{ + write_sysreg_s(val, SYS_ICV_AP1R0_EL1); + isb(); +} + static void guest_set_irq_line(uint32_t intid, uint32_t level); static void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) @@ -274,6 +293,55 @@ static void guest_inject(struct test_args *args, reset_priorities(args); } +/* + * Restore the active state of multiple concurrent IRQs (given by + * concurrent_irqs). This does what a live-migration would do on the + * destination side assuming there are some active IRQs that were not + * deactivated yet. + */ +static void guest_restore_active(struct test_args *args, + uint32_t first_intid, uint32_t num, + kvm_inject_cmd cmd) +{ + uint32_t prio, intid, ap1r; + int i; + + /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs + * in descending order, so intid+1 can preempt intid. + */ + for (i = 0, prio = (num - 1) * 8; i < num; i++, prio -= 8) { + GUEST_ASSERT(prio >= 0); + intid = i + first_intid; + gic_set_priority(intid, prio); + } + + /* In a real migration, KVM would restore all GIC state before running + * guest code. + */ + for (i = 0; i < num; i++) { + intid = i + first_intid; + KVM_ACTIVATE(cmd, intid); + ap1r = gic_read_ap1r0(); + ap1r |= 1U << i; + gic_write_ap1r0(ap1r); + } + + /* This is where the "migration" would occur. */ + + /* finish handling the IRQs starting with the highest priority one. */ + for (i = 0; i < num; i++) { + intid = num - i - 1 + first_intid; + gic_set_eoi(intid); + if (args->eoi_split) + gic_set_dir(intid); + } + + for (i = 0; i < num; i++) + GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); + GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); + GUEST_ASSERT_IAR_EMPTY(); +} + /* * Polls the IAR until it's not a spurious interrupt. * @@ -391,6 +459,19 @@ static void test_preemption(struct test_args *args, struct kvm_inject_desc *f) test_inject_preemption(args, MIN_SPI, 4, f->cmd); } +static void test_restore_active(struct test_args *args, struct kvm_inject_desc *f) +{ + /* Test up to 4 active IRQs. Same reason as in test_preemption. */ + if (f->sgi) + guest_restore_active(args, MIN_SGI, 4, f->cmd); + + if (f->ppi) + guest_restore_active(args, MIN_PPI, 4, f->cmd); + + if (f->spi) + guest_restore_active(args, MIN_SPI, 4, f->cmd); +} + static void guest_code(struct test_args args) { uint32_t i, nr_irqs = args.nr_irqs; @@ -422,6 +503,12 @@ static void guest_code(struct test_args args) test_injection_failure(&args, f); } + /* Restore the active state of IRQs. This would happen when live + * migrating IRQs in the middle of being handled. + */ + for_each_supported_activate_fn(&args, set_active_fns, f) + test_restore_active(&args, f); + GUEST_DONE(); } @@ -619,6 +706,10 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, kvm_irq_write_ispendr_check(gic_fd, i, VCPU_ID, expect_failure); break; + case KVM_WRITE_ISACTIVER: + for (i = intid; i < intid + num; i++) + kvm_irq_write_isactiver(gic_fd, i, VCPU_ID); + break; default: break; }