162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vgic_irq.c - Test userspace injection of IRQs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This test validates the injection of IRQs from userspace using various 662306a36Sopenharmony_ci * methods (e.g., KVM_IRQ_LINE) and modes (e.g., EOI). The guest "asks" the 762306a36Sopenharmony_ci * host to inject a specific intid via a GUEST_SYNC call, and then checks that 862306a36Sopenharmony_ci * it received it. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <asm/kvm.h> 1162306a36Sopenharmony_ci#include <asm/kvm_para.h> 1262306a36Sopenharmony_ci#include <sys/eventfd.h> 1362306a36Sopenharmony_ci#include <linux/sizes.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "processor.h" 1662306a36Sopenharmony_ci#include "test_util.h" 1762306a36Sopenharmony_ci#include "kvm_util.h" 1862306a36Sopenharmony_ci#include "gic.h" 1962306a36Sopenharmony_ci#include "gic_v3.h" 2062306a36Sopenharmony_ci#include "vgic.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define GICD_BASE_GPA 0x08000000ULL 2362306a36Sopenharmony_ci#define GICR_BASE_GPA 0x080A0000ULL 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Stores the user specified args; it's passed to the guest and to every test 2762306a36Sopenharmony_ci * function. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistruct test_args { 3062306a36Sopenharmony_ci uint32_t nr_irqs; /* number of KVM supported IRQs. */ 3162306a36Sopenharmony_ci bool eoi_split; /* 1 is eoir+dir, 0 is eoir only */ 3262306a36Sopenharmony_ci bool level_sensitive; /* 1 is level, 0 is edge */ 3362306a36Sopenharmony_ci int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */ 3462306a36Sopenharmony_ci bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */ 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * KVM implements 32 priority levels: 3962306a36Sopenharmony_ci * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Note that these macros will still be correct in the case that KVM implements 4262306a36Sopenharmony_ci * more priority levels. Also note that 32 is the minimum for GICv3 and GICv2. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci#define KVM_NUM_PRIOS 32 4562306a36Sopenharmony_ci#define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */ 4662306a36Sopenharmony_ci#define KVM_PRIO_STEPS (1 << KVM_PRIO_SHIFT) /* 8 */ 4762306a36Sopenharmony_ci#define LOWEST_PRIO (KVM_NUM_PRIOS - 1) 4862306a36Sopenharmony_ci#define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */ 4962306a36Sopenharmony_ci#define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1) 5062306a36Sopenharmony_ci#define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void *dist = (void *)GICD_BASE_GPA; 5362306a36Sopenharmony_cistatic void *redist = (void *)GICR_BASE_GPA; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * The kvm_inject_* utilities are used by the guest to ask the host to inject 5762306a36Sopenharmony_ci * interrupts (e.g., using the KVM_IRQ_LINE ioctl). 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_citypedef enum { 6162306a36Sopenharmony_ci KVM_INJECT_EDGE_IRQ_LINE = 1, 6262306a36Sopenharmony_ci KVM_SET_IRQ_LINE, 6362306a36Sopenharmony_ci KVM_SET_IRQ_LINE_HIGH, 6462306a36Sopenharmony_ci KVM_SET_LEVEL_INFO_HIGH, 6562306a36Sopenharmony_ci KVM_INJECT_IRQFD, 6662306a36Sopenharmony_ci KVM_WRITE_ISPENDR, 6762306a36Sopenharmony_ci KVM_WRITE_ISACTIVER, 6862306a36Sopenharmony_ci} kvm_inject_cmd; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct kvm_inject_args { 7162306a36Sopenharmony_ci kvm_inject_cmd cmd; 7262306a36Sopenharmony_ci uint32_t first_intid; 7362306a36Sopenharmony_ci uint32_t num; 7462306a36Sopenharmony_ci int level; 7562306a36Sopenharmony_ci bool expect_failure; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Used on the guest side to perform the hypercall. */ 7962306a36Sopenharmony_cistatic void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, 8062306a36Sopenharmony_ci uint32_t num, int level, bool expect_failure); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Used on the host side to get the hypercall info. */ 8362306a36Sopenharmony_cistatic void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, 8462306a36Sopenharmony_ci struct kvm_inject_args *args); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define _KVM_INJECT_MULTI(cmd, intid, num, expect_failure) \ 8762306a36Sopenharmony_ci kvm_inject_call(cmd, intid, num, -1 /* not used */, expect_failure) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define KVM_INJECT_MULTI(cmd, intid, num) \ 9062306a36Sopenharmony_ci _KVM_INJECT_MULTI(cmd, intid, num, false) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define _KVM_INJECT(cmd, intid, expect_failure) \ 9362306a36Sopenharmony_ci _KVM_INJECT_MULTI(cmd, intid, 1, expect_failure) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define KVM_INJECT(cmd, intid) \ 9662306a36Sopenharmony_ci _KVM_INJECT_MULTI(cmd, intid, 1, false) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define KVM_ACTIVATE(cmd, intid) \ 9962306a36Sopenharmony_ci kvm_inject_call(cmd, intid, 1, 1, false); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct kvm_inject_desc { 10262306a36Sopenharmony_ci kvm_inject_cmd cmd; 10362306a36Sopenharmony_ci /* can inject PPIs, PPIs, and/or SPIs. */ 10462306a36Sopenharmony_ci bool sgi, ppi, spi; 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct kvm_inject_desc inject_edge_fns[] = { 10862306a36Sopenharmony_ci /* sgi ppi spi */ 10962306a36Sopenharmony_ci { KVM_INJECT_EDGE_IRQ_LINE, false, false, true }, 11062306a36Sopenharmony_ci { KVM_INJECT_IRQFD, false, false, true }, 11162306a36Sopenharmony_ci { KVM_WRITE_ISPENDR, true, false, true }, 11262306a36Sopenharmony_ci { 0, }, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct kvm_inject_desc inject_level_fns[] = { 11662306a36Sopenharmony_ci /* sgi ppi spi */ 11762306a36Sopenharmony_ci { KVM_SET_IRQ_LINE_HIGH, false, true, true }, 11862306a36Sopenharmony_ci { KVM_SET_LEVEL_INFO_HIGH, false, true, true }, 11962306a36Sopenharmony_ci { KVM_INJECT_IRQFD, false, false, true }, 12062306a36Sopenharmony_ci { KVM_WRITE_ISPENDR, false, true, true }, 12162306a36Sopenharmony_ci { 0, }, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct kvm_inject_desc set_active_fns[] = { 12562306a36Sopenharmony_ci /* sgi ppi spi */ 12662306a36Sopenharmony_ci { KVM_WRITE_ISACTIVER, true, true, true }, 12762306a36Sopenharmony_ci { 0, }, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define for_each_inject_fn(t, f) \ 13162306a36Sopenharmony_ci for ((f) = (t); (f)->cmd; (f)++) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define for_each_supported_inject_fn(args, t, f) \ 13462306a36Sopenharmony_ci for_each_inject_fn(t, f) \ 13562306a36Sopenharmony_ci if ((args)->kvm_supports_irqfd || (f)->cmd != KVM_INJECT_IRQFD) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define for_each_supported_activate_fn(args, t, f) \ 13862306a36Sopenharmony_ci for_each_supported_inject_fn((args), (t), (f)) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Shared between the guest main thread and the IRQ handlers. */ 14162306a36Sopenharmony_civolatile uint64_t irq_handled; 14262306a36Sopenharmony_civolatile uint32_t irqnr_received[MAX_SPI + 1]; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void reset_stats(void) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci irq_handled = 0; 14962306a36Sopenharmony_ci for (i = 0; i <= MAX_SPI; i++) 15062306a36Sopenharmony_ci irqnr_received[i] = 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic uint64_t gic_read_ap1r0(void) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dsb(sy); 15862306a36Sopenharmony_ci return reg; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void gic_write_ap1r0(uint64_t val) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci write_sysreg_s(val, SYS_ICV_AP1R0_EL1); 16462306a36Sopenharmony_ci isb(); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void guest_set_irq_line(uint32_t intid, uint32_t level); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void guest_irq_generic_handler(bool eoi_split, bool level_sensitive) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci uint32_t intid = gic_get_and_ack_irq(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (intid == IAR_SPURIOUS) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci GUEST_ASSERT(gic_irq_get_active(intid)); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!level_sensitive) 17962306a36Sopenharmony_ci GUEST_ASSERT(!gic_irq_get_pending(intid)); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (level_sensitive) 18262306a36Sopenharmony_ci guest_set_irq_line(intid, 0); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci GUEST_ASSERT(intid < MAX_SPI); 18562306a36Sopenharmony_ci irqnr_received[intid] += 1; 18662306a36Sopenharmony_ci irq_handled += 1; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci gic_set_eoi(intid); 18962306a36Sopenharmony_ci GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); 19062306a36Sopenharmony_ci if (eoi_split) 19162306a36Sopenharmony_ci gic_set_dir(intid); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci GUEST_ASSERT(!gic_irq_get_active(intid)); 19462306a36Sopenharmony_ci GUEST_ASSERT(!gic_irq_get_pending(intid)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, 19862306a36Sopenharmony_ci uint32_t num, int level, bool expect_failure) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct kvm_inject_args args = { 20162306a36Sopenharmony_ci .cmd = cmd, 20262306a36Sopenharmony_ci .first_intid = first_intid, 20362306a36Sopenharmony_ci .num = num, 20462306a36Sopenharmony_ci .level = level, 20562306a36Sopenharmony_ci .expect_failure = expect_failure, 20662306a36Sopenharmony_ci }; 20762306a36Sopenharmony_ci GUEST_SYNC(&args); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#define GUEST_ASSERT_IAR_EMPTY() \ 21162306a36Sopenharmony_cido { \ 21262306a36Sopenharmony_ci uint32_t _intid; \ 21362306a36Sopenharmony_ci _intid = gic_get_and_ack_irq(); \ 21462306a36Sopenharmony_ci GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ 21562306a36Sopenharmony_ci} while (0) 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci#define CAT_HELPER(a, b) a ## b 21862306a36Sopenharmony_ci#define CAT(a, b) CAT_HELPER(a, b) 21962306a36Sopenharmony_ci#define PREFIX guest_irq_handler_ 22062306a36Sopenharmony_ci#define GUEST_IRQ_HANDLER_NAME(split, lev) CAT(PREFIX, CAT(split, lev)) 22162306a36Sopenharmony_ci#define GENERATE_GUEST_IRQ_HANDLER(split, lev) \ 22262306a36Sopenharmony_cistatic void CAT(PREFIX, CAT(split, lev))(struct ex_regs *regs) \ 22362306a36Sopenharmony_ci{ \ 22462306a36Sopenharmony_ci guest_irq_generic_handler(split, lev); \ 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciGENERATE_GUEST_IRQ_HANDLER(0, 0); 22862306a36Sopenharmony_ciGENERATE_GUEST_IRQ_HANDLER(0, 1); 22962306a36Sopenharmony_ciGENERATE_GUEST_IRQ_HANDLER(1, 0); 23062306a36Sopenharmony_ciGENERATE_GUEST_IRQ_HANDLER(1, 1); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void (*guest_irq_handlers[2][2])(struct ex_regs *) = { 23362306a36Sopenharmony_ci {GUEST_IRQ_HANDLER_NAME(0, 0), GUEST_IRQ_HANDLER_NAME(0, 1),}, 23462306a36Sopenharmony_ci {GUEST_IRQ_HANDLER_NAME(1, 0), GUEST_IRQ_HANDLER_NAME(1, 1),}, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void reset_priorities(struct test_args *args) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int i; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (i = 0; i < args->nr_irqs; i++) 24262306a36Sopenharmony_ci gic_set_priority(i, IRQ_DEFAULT_PRIO_REG); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void guest_set_irq_line(uint32_t intid, uint32_t level) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci kvm_inject_call(KVM_SET_IRQ_LINE, intid, 1, level, false); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void test_inject_fail(struct test_args *args, 25162306a36Sopenharmony_ci uint32_t intid, kvm_inject_cmd cmd) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci reset_stats(); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci _KVM_INJECT(cmd, intid, true); 25662306a36Sopenharmony_ci /* no IRQ to handle on entry */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci GUEST_ASSERT_EQ(irq_handled, 0); 25962306a36Sopenharmony_ci GUEST_ASSERT_IAR_EMPTY(); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void guest_inject(struct test_args *args, 26362306a36Sopenharmony_ci uint32_t first_intid, uint32_t num, 26462306a36Sopenharmony_ci kvm_inject_cmd cmd) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci uint32_t i; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci reset_stats(); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Cycle over all priorities to make things more interesting. */ 27162306a36Sopenharmony_ci for (i = first_intid; i < num + first_intid; i++) 27262306a36Sopenharmony_ci gic_set_priority(i, (i % (KVM_NUM_PRIOS - 1)) << 3); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci asm volatile("msr daifset, #2" : : : "memory"); 27562306a36Sopenharmony_ci KVM_INJECT_MULTI(cmd, first_intid, num); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci while (irq_handled < num) { 27862306a36Sopenharmony_ci asm volatile("wfi\n" 27962306a36Sopenharmony_ci "msr daifclr, #2\n" 28062306a36Sopenharmony_ci /* handle IRQ */ 28162306a36Sopenharmony_ci "msr daifset, #2\n" 28262306a36Sopenharmony_ci : : : "memory"); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci asm volatile("msr daifclr, #2" : : : "memory"); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci GUEST_ASSERT_EQ(irq_handled, num); 28762306a36Sopenharmony_ci for (i = first_intid; i < num + first_intid; i++) 28862306a36Sopenharmony_ci GUEST_ASSERT_EQ(irqnr_received[i], 1); 28962306a36Sopenharmony_ci GUEST_ASSERT_IAR_EMPTY(); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci reset_priorities(args); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * Restore the active state of multiple concurrent IRQs (given by 29662306a36Sopenharmony_ci * concurrent_irqs). This does what a live-migration would do on the 29762306a36Sopenharmony_ci * destination side assuming there are some active IRQs that were not 29862306a36Sopenharmony_ci * deactivated yet. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic void guest_restore_active(struct test_args *args, 30162306a36Sopenharmony_ci uint32_t first_intid, uint32_t num, 30262306a36Sopenharmony_ci kvm_inject_cmd cmd) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci uint32_t prio, intid, ap1r; 30562306a36Sopenharmony_ci int i; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs 30962306a36Sopenharmony_ci * in descending order, so intid+1 can preempt intid. 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci for (i = 0, prio = (num - 1) * 8; i < num; i++, prio -= 8) { 31262306a36Sopenharmony_ci GUEST_ASSERT(prio >= 0); 31362306a36Sopenharmony_ci intid = i + first_intid; 31462306a36Sopenharmony_ci gic_set_priority(intid, prio); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * In a real migration, KVM would restore all GIC state before running 31962306a36Sopenharmony_ci * guest code. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci for (i = 0; i < num; i++) { 32262306a36Sopenharmony_ci intid = i + first_intid; 32362306a36Sopenharmony_ci KVM_ACTIVATE(cmd, intid); 32462306a36Sopenharmony_ci ap1r = gic_read_ap1r0(); 32562306a36Sopenharmony_ci ap1r |= 1U << i; 32662306a36Sopenharmony_ci gic_write_ap1r0(ap1r); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* This is where the "migration" would occur. */ 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* finish handling the IRQs starting with the highest priority one. */ 33262306a36Sopenharmony_ci for (i = 0; i < num; i++) { 33362306a36Sopenharmony_ci intid = num - i - 1 + first_intid; 33462306a36Sopenharmony_ci gic_set_eoi(intid); 33562306a36Sopenharmony_ci if (args->eoi_split) 33662306a36Sopenharmony_ci gic_set_dir(intid); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < num; i++) 34062306a36Sopenharmony_ci GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); 34162306a36Sopenharmony_ci GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); 34262306a36Sopenharmony_ci GUEST_ASSERT_IAR_EMPTY(); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Polls the IAR until it's not a spurious interrupt. 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * This function should only be used in test_inject_preemption (with IRQs 34962306a36Sopenharmony_ci * masked). 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic uint32_t wait_for_and_activate_irq(void) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci uint32_t intid; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci do { 35662306a36Sopenharmony_ci asm volatile("wfi" : : : "memory"); 35762306a36Sopenharmony_ci intid = gic_get_and_ack_irq(); 35862306a36Sopenharmony_ci } while (intid == IAR_SPURIOUS); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return intid; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Inject multiple concurrent IRQs (num IRQs starting at first_intid) and 36562306a36Sopenharmony_ci * handle them without handling the actual exceptions. This is done by masking 36662306a36Sopenharmony_ci * interrupts for the whole test. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic void test_inject_preemption(struct test_args *args, 36962306a36Sopenharmony_ci uint32_t first_intid, int num, 37062306a36Sopenharmony_ci kvm_inject_cmd cmd) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci uint32_t intid, prio, step = KVM_PRIO_STEPS; 37362306a36Sopenharmony_ci int i; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Set the priorities of the first (KVM_NUM_PRIOS - 1) IRQs 37662306a36Sopenharmony_ci * in descending order, so intid+1 can preempt intid. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci for (i = 0, prio = (num - 1) * step; i < num; i++, prio -= step) { 37962306a36Sopenharmony_ci GUEST_ASSERT(prio >= 0); 38062306a36Sopenharmony_ci intid = i + first_intid; 38162306a36Sopenharmony_ci gic_set_priority(intid, prio); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci local_irq_disable(); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for (i = 0; i < num; i++) { 38762306a36Sopenharmony_ci uint32_t tmp; 38862306a36Sopenharmony_ci intid = i + first_intid; 38962306a36Sopenharmony_ci KVM_INJECT(cmd, intid); 39062306a36Sopenharmony_ci /* Each successive IRQ will preempt the previous one. */ 39162306a36Sopenharmony_ci tmp = wait_for_and_activate_irq(); 39262306a36Sopenharmony_ci GUEST_ASSERT_EQ(tmp, intid); 39362306a36Sopenharmony_ci if (args->level_sensitive) 39462306a36Sopenharmony_ci guest_set_irq_line(intid, 0); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* finish handling the IRQs starting with the highest priority one. */ 39862306a36Sopenharmony_ci for (i = 0; i < num; i++) { 39962306a36Sopenharmony_ci intid = num - i - 1 + first_intid; 40062306a36Sopenharmony_ci gic_set_eoi(intid); 40162306a36Sopenharmony_ci if (args->eoi_split) 40262306a36Sopenharmony_ci gic_set_dir(intid); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci local_irq_enable(); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (i = 0; i < num; i++) 40862306a36Sopenharmony_ci GUEST_ASSERT(!gic_irq_get_active(i + first_intid)); 40962306a36Sopenharmony_ci GUEST_ASSERT_EQ(gic_read_ap1r0(), 0); 41062306a36Sopenharmony_ci GUEST_ASSERT_IAR_EMPTY(); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci reset_priorities(args); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void test_injection(struct test_args *args, struct kvm_inject_desc *f) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci uint32_t nr_irqs = args->nr_irqs; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (f->sgi) { 42062306a36Sopenharmony_ci guest_inject(args, MIN_SGI, 1, f->cmd); 42162306a36Sopenharmony_ci guest_inject(args, 0, 16, f->cmd); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (f->ppi) 42562306a36Sopenharmony_ci guest_inject(args, MIN_PPI, 1, f->cmd); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (f->spi) { 42862306a36Sopenharmony_ci guest_inject(args, MIN_SPI, 1, f->cmd); 42962306a36Sopenharmony_ci guest_inject(args, nr_irqs - 1, 1, f->cmd); 43062306a36Sopenharmony_ci guest_inject(args, MIN_SPI, nr_irqs - MIN_SPI, f->cmd); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void test_injection_failure(struct test_args *args, 43562306a36Sopenharmony_ci struct kvm_inject_desc *f) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci uint32_t bad_intid[] = { args->nr_irqs, 1020, 1024, 1120, 5120, ~0U, }; 43862306a36Sopenharmony_ci int i; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bad_intid); i++) 44162306a36Sopenharmony_ci test_inject_fail(args, bad_intid[i], f->cmd); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void test_preemption(struct test_args *args, struct kvm_inject_desc *f) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * Test up to 4 levels of preemption. The reason is that KVM doesn't 44862306a36Sopenharmony_ci * currently implement the ability to have more than the number-of-LRs 44962306a36Sopenharmony_ci * number of concurrently active IRQs. The number of LRs implemented is 45062306a36Sopenharmony_ci * IMPLEMENTATION DEFINED, however, it seems that most implement 4. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci if (f->sgi) 45362306a36Sopenharmony_ci test_inject_preemption(args, MIN_SGI, 4, f->cmd); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (f->ppi) 45662306a36Sopenharmony_ci test_inject_preemption(args, MIN_PPI, 4, f->cmd); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (f->spi) 45962306a36Sopenharmony_ci test_inject_preemption(args, MIN_SPI, 4, f->cmd); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void test_restore_active(struct test_args *args, struct kvm_inject_desc *f) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci /* Test up to 4 active IRQs. Same reason as in test_preemption. */ 46562306a36Sopenharmony_ci if (f->sgi) 46662306a36Sopenharmony_ci guest_restore_active(args, MIN_SGI, 4, f->cmd); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (f->ppi) 46962306a36Sopenharmony_ci guest_restore_active(args, MIN_PPI, 4, f->cmd); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (f->spi) 47262306a36Sopenharmony_ci guest_restore_active(args, MIN_SPI, 4, f->cmd); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void guest_code(struct test_args *args) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci uint32_t i, nr_irqs = args->nr_irqs; 47862306a36Sopenharmony_ci bool level_sensitive = args->level_sensitive; 47962306a36Sopenharmony_ci struct kvm_inject_desc *f, *inject_fns; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci gic_init(GIC_V3, 1, dist, redist); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 48462306a36Sopenharmony_ci gic_irq_enable(i); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci for (i = MIN_SPI; i < nr_irqs; i++) 48762306a36Sopenharmony_ci gic_irq_set_config(i, !level_sensitive); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci gic_set_eoi_split(args->eoi_split); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci reset_priorities(args); 49262306a36Sopenharmony_ci gic_set_priority_mask(CPU_PRIO_MASK); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci inject_fns = level_sensitive ? inject_level_fns 49562306a36Sopenharmony_ci : inject_edge_fns; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci local_irq_enable(); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* Start the tests. */ 50062306a36Sopenharmony_ci for_each_supported_inject_fn(args, inject_fns, f) { 50162306a36Sopenharmony_ci test_injection(args, f); 50262306a36Sopenharmony_ci test_preemption(args, f); 50362306a36Sopenharmony_ci test_injection_failure(args, f); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * Restore the active state of IRQs. This would happen when live 50862306a36Sopenharmony_ci * migrating IRQs in the middle of being handled. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci for_each_supported_activate_fn(args, set_active_fns, f) 51162306a36Sopenharmony_ci test_restore_active(args, f); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci GUEST_DONE(); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void kvm_irq_line_check(struct kvm_vm *vm, uint32_t intid, int level, 51762306a36Sopenharmony_ci struct test_args *test_args, bool expect_failure) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!expect_failure) { 52262306a36Sopenharmony_ci kvm_arm_irq_line(vm, intid, level); 52362306a36Sopenharmony_ci } else { 52462306a36Sopenharmony_ci /* The interface doesn't allow larger intid's. */ 52562306a36Sopenharmony_ci if (intid > KVM_ARM_IRQ_NUM_MASK) 52662306a36Sopenharmony_ci return; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = _kvm_arm_irq_line(vm, intid, level); 52962306a36Sopenharmony_ci TEST_ASSERT(ret != 0 && errno == EINVAL, 53062306a36Sopenharmony_ci "Bad intid %i did not cause KVM_IRQ_LINE " 53162306a36Sopenharmony_ci "error: rc: %i errno: %i", intid, ret, errno); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_civoid kvm_irq_set_level_info_check(int gic_fd, uint32_t intid, int level, 53662306a36Sopenharmony_ci bool expect_failure) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci if (!expect_failure) { 53962306a36Sopenharmony_ci kvm_irq_set_level_info(gic_fd, intid, level); 54062306a36Sopenharmony_ci } else { 54162306a36Sopenharmony_ci int ret = _kvm_irq_set_level_info(gic_fd, intid, level); 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * The kernel silently fails for invalid SPIs and SGIs (which 54462306a36Sopenharmony_ci * are not level-sensitive). It only checks for intid to not 54562306a36Sopenharmony_ci * spill over 1U << 10 (the max reserved SPI). Also, callers 54662306a36Sopenharmony_ci * are supposed to mask the intid with 0x3ff (1023). 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (intid > VGIC_MAX_RESERVED) 54962306a36Sopenharmony_ci TEST_ASSERT(ret != 0 && errno == EINVAL, 55062306a36Sopenharmony_ci "Bad intid %i did not cause VGIC_GRP_LEVEL_INFO " 55162306a36Sopenharmony_ci "error: rc: %i errno: %i", intid, ret, errno); 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci TEST_ASSERT(!ret, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO " 55462306a36Sopenharmony_ci "for intid %i failed, rc: %i errno: %i", 55562306a36Sopenharmony_ci intid, ret, errno); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, 56062306a36Sopenharmony_ci uint32_t intid, uint32_t num, uint32_t kvm_max_routes, 56162306a36Sopenharmony_ci bool expect_failure) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct kvm_irq_routing *routing; 56462306a36Sopenharmony_ci int ret; 56562306a36Sopenharmony_ci uint64_t i; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci assert(num <= kvm_max_routes && kvm_max_routes <= KVM_MAX_IRQ_ROUTES); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci routing = kvm_gsi_routing_create(); 57062306a36Sopenharmony_ci for (i = intid; i < (uint64_t)intid + num; i++) 57162306a36Sopenharmony_ci kvm_gsi_routing_irqchip_add(routing, i - MIN_SPI, i - MIN_SPI); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!expect_failure) { 57462306a36Sopenharmony_ci kvm_gsi_routing_write(vm, routing); 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci ret = _kvm_gsi_routing_write(vm, routing); 57762306a36Sopenharmony_ci /* The kernel only checks e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS */ 57862306a36Sopenharmony_ci if (((uint64_t)intid + num - 1 - MIN_SPI) >= KVM_IRQCHIP_NUM_PINS) 57962306a36Sopenharmony_ci TEST_ASSERT(ret != 0 && errno == EINVAL, 58062306a36Sopenharmony_ci "Bad intid %u did not cause KVM_SET_GSI_ROUTING " 58162306a36Sopenharmony_ci "error: rc: %i errno: %i", intid, ret, errno); 58262306a36Sopenharmony_ci else 58362306a36Sopenharmony_ci TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING " 58462306a36Sopenharmony_ci "for intid %i failed, rc: %i errno: %i", 58562306a36Sopenharmony_ci intid, ret, errno); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, 59062306a36Sopenharmony_ci struct kvm_vcpu *vcpu, 59162306a36Sopenharmony_ci bool expect_failure) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * Ignore this when expecting failure as invalid intids will lead to 59562306a36Sopenharmony_ci * either trying to inject SGIs when we configured the test to be 59662306a36Sopenharmony_ci * level_sensitive (or the reverse), or inject large intids which 59762306a36Sopenharmony_ci * will lead to writing above the ISPENDR register space (and we 59862306a36Sopenharmony_ci * don't want to do that either). 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci if (!expect_failure) 60162306a36Sopenharmony_ci kvm_irq_write_ispendr(gic_fd, intid, vcpu); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void kvm_routing_and_irqfd_check(struct kvm_vm *vm, 60562306a36Sopenharmony_ci uint32_t intid, uint32_t num, uint32_t kvm_max_routes, 60662306a36Sopenharmony_ci bool expect_failure) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci int fd[MAX_SPI]; 60962306a36Sopenharmony_ci uint64_t val; 61062306a36Sopenharmony_ci int ret, f; 61162306a36Sopenharmony_ci uint64_t i; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * There is no way to try injecting an SGI or PPI as the interface 61562306a36Sopenharmony_ci * starts counting from the first SPI (above the private ones), so just 61662306a36Sopenharmony_ci * exit. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci if (INTID_IS_SGI(intid) || INTID_IS_PPI(intid)) 61962306a36Sopenharmony_ci return; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci kvm_set_gsi_routing_irqchip_check(vm, intid, num, 62262306a36Sopenharmony_ci kvm_max_routes, expect_failure); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * If expect_failure, then just to inject anyway. These 62662306a36Sopenharmony_ci * will silently fail. And in any case, the guest will check 62762306a36Sopenharmony_ci * that no actual interrupt was injected for those cases. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { 63162306a36Sopenharmony_ci fd[f] = eventfd(0, 0); 63262306a36Sopenharmony_ci TEST_ASSERT(fd[f] != -1, __KVM_SYSCALL_ERROR("eventfd()", fd[f])); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { 63662306a36Sopenharmony_ci struct kvm_irqfd irqfd = { 63762306a36Sopenharmony_ci .fd = fd[f], 63862306a36Sopenharmony_ci .gsi = i - MIN_SPI, 63962306a36Sopenharmony_ci }; 64062306a36Sopenharmony_ci assert(i <= (uint64_t)UINT_MAX); 64162306a36Sopenharmony_ci vm_ioctl(vm, KVM_IRQFD, &irqfd); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { 64562306a36Sopenharmony_ci val = 1; 64662306a36Sopenharmony_ci ret = write(fd[f], &val, sizeof(uint64_t)); 64762306a36Sopenharmony_ci TEST_ASSERT(ret == sizeof(uint64_t), 64862306a36Sopenharmony_ci __KVM_SYSCALL_ERROR("write()", ret)); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) 65262306a36Sopenharmony_ci close(fd[f]); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/* handles the valid case: intid=0xffffffff num=1 */ 65662306a36Sopenharmony_ci#define for_each_intid(first, num, tmp, i) \ 65762306a36Sopenharmony_ci for ((tmp) = (i) = (first); \ 65862306a36Sopenharmony_ci (tmp) < (uint64_t)(first) + (uint64_t)(num); \ 65962306a36Sopenharmony_ci (tmp)++, (i)++) 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic void run_guest_cmd(struct kvm_vcpu *vcpu, int gic_fd, 66262306a36Sopenharmony_ci struct kvm_inject_args *inject_args, 66362306a36Sopenharmony_ci struct test_args *test_args) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci kvm_inject_cmd cmd = inject_args->cmd; 66662306a36Sopenharmony_ci uint32_t intid = inject_args->first_intid; 66762306a36Sopenharmony_ci uint32_t num = inject_args->num; 66862306a36Sopenharmony_ci int level = inject_args->level; 66962306a36Sopenharmony_ci bool expect_failure = inject_args->expect_failure; 67062306a36Sopenharmony_ci struct kvm_vm *vm = vcpu->vm; 67162306a36Sopenharmony_ci uint64_t tmp; 67262306a36Sopenharmony_ci uint32_t i; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* handles the valid case: intid=0xffffffff num=1 */ 67562306a36Sopenharmony_ci assert(intid < UINT_MAX - num || num == 1); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci switch (cmd) { 67862306a36Sopenharmony_ci case KVM_INJECT_EDGE_IRQ_LINE: 67962306a36Sopenharmony_ci for_each_intid(intid, num, tmp, i) 68062306a36Sopenharmony_ci kvm_irq_line_check(vm, i, 1, test_args, 68162306a36Sopenharmony_ci expect_failure); 68262306a36Sopenharmony_ci for_each_intid(intid, num, tmp, i) 68362306a36Sopenharmony_ci kvm_irq_line_check(vm, i, 0, test_args, 68462306a36Sopenharmony_ci expect_failure); 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci case KVM_SET_IRQ_LINE: 68762306a36Sopenharmony_ci for_each_intid(intid, num, tmp, i) 68862306a36Sopenharmony_ci kvm_irq_line_check(vm, i, level, test_args, 68962306a36Sopenharmony_ci expect_failure); 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci case KVM_SET_IRQ_LINE_HIGH: 69262306a36Sopenharmony_ci for_each_intid(intid, num, tmp, i) 69362306a36Sopenharmony_ci kvm_irq_line_check(vm, i, 1, test_args, 69462306a36Sopenharmony_ci expect_failure); 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci case KVM_SET_LEVEL_INFO_HIGH: 69762306a36Sopenharmony_ci for_each_intid(intid, num, tmp, i) 69862306a36Sopenharmony_ci kvm_irq_set_level_info_check(gic_fd, i, 1, 69962306a36Sopenharmony_ci expect_failure); 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case KVM_INJECT_IRQFD: 70262306a36Sopenharmony_ci kvm_routing_and_irqfd_check(vm, intid, num, 70362306a36Sopenharmony_ci test_args->kvm_max_routes, 70462306a36Sopenharmony_ci expect_failure); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case KVM_WRITE_ISPENDR: 70762306a36Sopenharmony_ci for (i = intid; i < intid + num; i++) 70862306a36Sopenharmony_ci kvm_irq_write_ispendr_check(gic_fd, i, vcpu, 70962306a36Sopenharmony_ci expect_failure); 71062306a36Sopenharmony_ci break; 71162306a36Sopenharmony_ci case KVM_WRITE_ISACTIVER: 71262306a36Sopenharmony_ci for (i = intid; i < intid + num; i++) 71362306a36Sopenharmony_ci kvm_irq_write_isactiver(gic_fd, i, vcpu); 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci default: 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, 72162306a36Sopenharmony_ci struct kvm_inject_args *args) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct kvm_inject_args *kvm_args_hva; 72462306a36Sopenharmony_ci vm_vaddr_t kvm_args_gva; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci kvm_args_gva = uc->args[1]; 72762306a36Sopenharmony_ci kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva); 72862306a36Sopenharmony_ci memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args)); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic void print_args(struct test_args *args) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci printf("nr-irqs=%d level-sensitive=%d eoi-split=%d\n", 73462306a36Sopenharmony_ci args->nr_irqs, args->level_sensitive, 73562306a36Sopenharmony_ci args->eoi_split); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct ucall uc; 74162306a36Sopenharmony_ci int gic_fd; 74262306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 74362306a36Sopenharmony_ci struct kvm_vm *vm; 74462306a36Sopenharmony_ci struct kvm_inject_args inject_args; 74562306a36Sopenharmony_ci vm_vaddr_t args_gva; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci struct test_args args = { 74862306a36Sopenharmony_ci .nr_irqs = nr_irqs, 74962306a36Sopenharmony_ci .level_sensitive = level_sensitive, 75062306a36Sopenharmony_ci .eoi_split = eoi_split, 75162306a36Sopenharmony_ci .kvm_max_routes = kvm_check_cap(KVM_CAP_IRQ_ROUTING), 75262306a36Sopenharmony_ci .kvm_supports_irqfd = kvm_check_cap(KVM_CAP_IRQFD), 75362306a36Sopenharmony_ci }; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci print_args(&args); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci vm = vm_create_with_one_vcpu(&vcpu, guest_code); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci vm_init_descriptor_tables(vm); 76062306a36Sopenharmony_ci vcpu_init_descriptor_tables(vcpu); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Setup the guest args page (so it gets the args). */ 76362306a36Sopenharmony_ci args_gva = vm_vaddr_alloc_page(vm); 76462306a36Sopenharmony_ci memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args)); 76562306a36Sopenharmony_ci vcpu_args_set(vcpu, 1, args_gva); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci gic_fd = vgic_v3_setup(vm, 1, nr_irqs, 76862306a36Sopenharmony_ci GICD_BASE_GPA, GICR_BASE_GPA); 76962306a36Sopenharmony_ci __TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3, skipping"); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, 77262306a36Sopenharmony_ci guest_irq_handlers[args.eoi_split][args.level_sensitive]); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci while (1) { 77562306a36Sopenharmony_ci vcpu_run(vcpu); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci switch (get_ucall(vcpu, &uc)) { 77862306a36Sopenharmony_ci case UCALL_SYNC: 77962306a36Sopenharmony_ci kvm_inject_get_call(vm, &uc, &inject_args); 78062306a36Sopenharmony_ci run_guest_cmd(vcpu, gic_fd, &inject_args, &args); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case UCALL_ABORT: 78362306a36Sopenharmony_ci REPORT_GUEST_ASSERT(uc); 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case UCALL_DONE: 78662306a36Sopenharmony_ci goto done; 78762306a36Sopenharmony_ci default: 78862306a36Sopenharmony_ci TEST_FAIL("Unknown ucall %lu", uc.cmd); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cidone: 79362306a36Sopenharmony_ci close(gic_fd); 79462306a36Sopenharmony_ci kvm_vm_free(vm); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void help(const char *name) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci printf( 80062306a36Sopenharmony_ci "\n" 80162306a36Sopenharmony_ci "usage: %s [-n num_irqs] [-e eoi_split] [-l level_sensitive]\n", name); 80262306a36Sopenharmony_ci printf(" -n: specify number of IRQs to setup the vgic with. " 80362306a36Sopenharmony_ci "It has to be a multiple of 32 and between 64 and 1024.\n"); 80462306a36Sopenharmony_ci printf(" -e: if 1 then EOI is split into a write to DIR on top " 80562306a36Sopenharmony_ci "of writing EOI.\n"); 80662306a36Sopenharmony_ci printf(" -l: specify whether the IRQs are level-sensitive (1) or not (0)."); 80762306a36Sopenharmony_ci puts(""); 80862306a36Sopenharmony_ci exit(1); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciint main(int argc, char **argv) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci uint32_t nr_irqs = 64; 81462306a36Sopenharmony_ci bool default_args = true; 81562306a36Sopenharmony_ci bool level_sensitive = false; 81662306a36Sopenharmony_ci int opt; 81762306a36Sopenharmony_ci bool eoi_split = false; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) { 82062306a36Sopenharmony_ci switch (opt) { 82162306a36Sopenharmony_ci case 'n': 82262306a36Sopenharmony_ci nr_irqs = atoi_non_negative("Number of IRQs", optarg); 82362306a36Sopenharmony_ci if (nr_irqs > 1024 || nr_irqs % 32) 82462306a36Sopenharmony_ci help(argv[0]); 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci case 'e': 82762306a36Sopenharmony_ci eoi_split = (bool)atoi_paranoid(optarg); 82862306a36Sopenharmony_ci default_args = false; 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci case 'l': 83162306a36Sopenharmony_ci level_sensitive = (bool)atoi_paranoid(optarg); 83262306a36Sopenharmony_ci default_args = false; 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case 'h': 83562306a36Sopenharmony_ci default: 83662306a36Sopenharmony_ci help(argv[0]); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * If the user just specified nr_irqs and/or gic_version, then run all 84362306a36Sopenharmony_ci * combinations. 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci if (default_args) { 84662306a36Sopenharmony_ci test_vgic(nr_irqs, false /* level */, false /* eoi_split */); 84762306a36Sopenharmony_ci test_vgic(nr_irqs, false /* level */, true /* eoi_split */); 84862306a36Sopenharmony_ci test_vgic(nr_irqs, true /* level */, false /* eoi_split */); 84962306a36Sopenharmony_ci test_vgic(nr_irqs, true /* level */, true /* eoi_split */); 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci test_vgic(nr_irqs, level_sensitive, eoi_split); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 856