From: David Woodhouse Test that GICv2 IGROUPR writability is consistently gated by the IIDR implementation revision for both guest and userspace paths: Default (no IIDR write): implementation_rev defaults to 3, groups writable from both guest and userspace. Rev 1: IGROUPR reads as zero (group 0), writes ignored from both guest and userspace. Rev 2: IGROUPR is writable from both guest and userspace. This test requires GICv2 emulation support (GICv3 with GICv2 compat CPU interface) and will be skipped on hardware without it. Signed-off-by: David Woodhouse --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/arm64/vgic_group_v2.c | 168 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 tools/testing/selftests/kvm/arm64/vgic_group_v2.c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index df729a70124f..878d7cb92555 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -178,6 +178,7 @@ TEST_GEN_PROGS_arm64 += arm64/vgic_init TEST_GEN_PROGS_arm64 += arm64/vgic_irq TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress TEST_GEN_PROGS_arm64 += arm64/vgic_group_iidr +TEST_GEN_PROGS_arm64 += arm64/vgic_group_v2 TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access TEST_GEN_PROGS_arm64 += arm64/no-vgic-v3 TEST_GEN_PROGS_arm64 += arm64/idreg-idst diff --git a/tools/testing/selftests/kvm/arm64/vgic_group_v2.c b/tools/testing/selftests/kvm/arm64/vgic_group_v2.c new file mode 100644 index 000000000000..6d4bad44bae7 --- /dev/null +++ b/tools/testing/selftests/kvm/arm64/vgic_group_v2.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * vgic_group_v2.c - Test GICv2 IGROUPR behaviour across IIDR revisions + * + * Validate that the GICD_IIDR implementation revision controls GICv2 + * IGROUPR writability for both guest and userspace: + * Default (no IIDR write): groups writable (implementation_rev defaults to 3) + * Rev 1: IGROUPR reads as zero (group 0), writes ignored + * Rev 2: IGROUPR is guest and userspace configurable + */ +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "gic.h" +#include "gic_v3.h" +#include "vgic.h" + +#define NR_IRQS 64 + +#define V2_DIST_BASE 0x8000000ULL +#define V2_CPU_BASE 0x8010000ULL +#define V2_DIST_GVA ((volatile void *)V2_DIST_BASE) + +#define SPI_IGROUPR (GICD_IGROUPR + (32 / 32) * 4) + +static uint64_t shared_rev; +static uint64_t guest_result; + +static void guest_code(void) +{ + uint32_t before, after; + + before = readl(V2_DIST_GVA + SPI_IGROUPR); + writel(0x5a5a5a5a, V2_DIST_GVA + SPI_IGROUPR); + after = readl(V2_DIST_GVA + SPI_IGROUPR); + + guest_result = ((uint64_t)before << 32) | after; + GUEST_DONE(); +} + +static int create_v2_gic(struct kvm_vm *vm) +{ + uint32_t nr_irqs = NR_IRQS; + uint64_t addr; + int gic_fd; + + gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V2); + if (gic_fd < 0) + return gic_fd; + + addr = V2_DIST_BASE; + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V2_ADDR_TYPE_DIST, &addr); + addr = V2_CPU_BASE; + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V2_ADDR_TYPE_CPU, &addr); + + virt_map(vm, V2_DIST_BASE, V2_DIST_BASE, + vm_calc_num_guest_pages(vm->mode, SZ_64K)); + virt_map(vm, V2_CPU_BASE, V2_CPU_BASE, + vm_calc_num_guest_pages(vm->mode, SZ_64K)); + + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, + 0, &nr_irqs); + return gic_fd; +} + +static void run_test(int set_iidr_rev) +{ + struct kvm_vcpu *vcpus[1]; + struct kvm_vm *vm; + struct ucall uc; + uint32_t before, after, igroupr, iidr; + int gic_fd; + bool expect_writable; + + if (set_iidr_rev >= 0) + pr_info("Testing GICv2 IIDR revision %d\n", set_iidr_rev); + else + pr_info("Testing GICv2 IIDR default (no write)\n"); + + test_disable_default_vgic(); + vm = vm_create_with_vcpus(1, guest_code, vcpus); + + gic_fd = create_v2_gic(vm); + TEST_REQUIRE(gic_fd >= 0); + + if (set_iidr_rev >= 0) { + kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_IIDR, &iidr); + iidr &= ~GICD_IIDR_REVISION_MASK; + iidr |= set_iidr_rev << GICD_IIDR_REVISION_SHIFT; + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + GICD_IIDR, &iidr); + } + + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); + + /* + * Default (no IIDR write) gets implementation_rev=3 from vgic_init(), + * so groups should be writable. Rev 1 = not writable. Rev 2+ = writable. + */ + expect_writable = (set_iidr_rev != 1); + + /* Test userspace IGROUPR write */ + igroupr = 0xa5a5a5a5; + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + SPI_IGROUPR, &igroupr); + igroupr = 0; + kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + SPI_IGROUPR, &igroupr); + + if (expect_writable) + TEST_ASSERT(igroupr == 0xa5a5a5a5, + "Userspace write should succeed: got 0x%08x", igroupr); + else + TEST_ASSERT(igroupr == 0x00000000, + "Userspace write should be ignored: got 0x%08x", igroupr); + + /* Reset IGROUPR to 0 via userspace for rev 2+ before guest test */ + if (expect_writable) { + igroupr = 0; + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, + SPI_IGROUPR, &igroupr); + } + + /* Test guest IGROUPR write */ + sync_global_to_guest(vm, guest_result); + vcpu_run(vcpus[0]); + + switch (get_ucall(vcpus[0], &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_DONE: + break; + default: + TEST_FAIL("Unexpected ucall %lu", uc.cmd); + } + + sync_global_from_guest(vm, guest_result); + before = guest_result >> 32; + after = guest_result & 0xffffffff; + + TEST_ASSERT(before == 0x00000000, + "Initial IGROUPR should be 0 (group 0): got 0x%08x", before); + + if (expect_writable) + TEST_ASSERT(after == 0x5a5a5a5a, + "Guest write should succeed: got 0x%08x", after); + else + TEST_ASSERT(after == 0x00000000, + "Guest write should be ignored: got 0x%08x", after); + + close(gic_fd); + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + run_test(-1); /* default */ + run_test(1); /* rev 1 */ + run_test(2); /* rev 2 */ + return 0; +} -- 2.51.0