1 // SPDX-License-Identifier: GPL-2.0
3 * vgic_irq.c - Test userspace injection of IRQs
5 * This test validates the injection of IRQs from userspace using various
6 * methods (e.g., KVM_IRQ_LINE) and modes (e.g., EOI). The guest "asks" the
7 * host to inject a specific intid via a GUEST_SYNC call, and then checks that
12 #include <asm/kvm_para.h>
13 #include <linux/sizes.h>
15 #include "processor.h"
16 #include "test_util.h"
22 #define GICD_BASE_GPA 0x08000000ULL
23 #define GICR_BASE_GPA 0x080A0000ULL
27 * Stores the user specified args; it's passed to the guest and to every test
31 uint32_t nr_irqs; /* number of KVM supported IRQs. */
35 * KVM implements 32 priority levels:
36 * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8
38 * Note that these macros will still be correct in the case that KVM implements
39 * more priority levels. Also note that 32 is the minimum for GICv3 and GICv2.
41 #define KVM_NUM_PRIOS 32
42 #define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */
43 #define LOWEST_PRIO (KVM_NUM_PRIOS - 1)
44 #define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */
45 #define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1)
46 #define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */
48 static void *dist = (void *)GICD_BASE_GPA;
49 static void *redist = (void *)GICR_BASE_GPA;
52 * The kvm_inject_* utilities are used by the guest to ask the host to inject
53 * interrupts (e.g., using the KVM_IRQ_LINE ioctl).
57 KVM_INJECT_EDGE_IRQ_LINE = 1,
60 struct kvm_inject_args {
66 /* Used on the guest side to perform the hypercall. */
67 static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num);
69 #define KVM_INJECT(cmd, intid) \
70 kvm_inject_call(cmd, intid, 1)
72 #define KVM_INJECT_MULTI(cmd, intid, num) \
73 kvm_inject_call(cmd, intid, num)
75 /* Used on the host side to get the hypercall info. */
76 static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
77 struct kvm_inject_args *args);
79 struct kvm_inject_desc {
81 /* can inject PPIs, PPIs, and/or SPIs. */
85 static struct kvm_inject_desc inject_edge_fns[] = {
87 { KVM_INJECT_EDGE_IRQ_LINE, false, false, true },
91 #define for_each_inject_fn(t, f) \
92 for ((f) = (t); (f)->cmd; (f)++)
94 /* Shared between the guest main thread and the IRQ handlers. */
95 volatile uint64_t irq_handled;
96 volatile uint32_t irqnr_received[MAX_SPI + 1];
98 static void reset_stats(void)
103 for (i = 0; i <= MAX_SPI; i++)
104 irqnr_received[i] = 0;
107 static uint64_t gic_read_ap1r0(void)
109 uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1);
115 static void guest_irq_handler(struct ex_regs *regs)
117 uint32_t intid = gic_get_and_ack_irq();
119 if (intid == IAR_SPURIOUS)
122 GUEST_ASSERT(gic_irq_get_active(intid));
124 GUEST_ASSERT(!gic_irq_get_pending(intid));
126 GUEST_ASSERT(intid < MAX_SPI);
127 irqnr_received[intid] += 1;
131 GUEST_ASSERT_EQ(gic_read_ap1r0(), 0);
133 GUEST_ASSERT(!gic_irq_get_active(intid));
134 GUEST_ASSERT(!gic_irq_get_pending(intid));
137 static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num)
139 struct kvm_inject_args args = {
141 .first_intid = first_intid,
147 #define GUEST_ASSERT_IAR_EMPTY() \
150 _intid = gic_get_and_ack_irq(); \
151 GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \
154 static void reset_priorities(struct test_args *args)
158 for (i = 0; i < args->nr_irqs; i++)
159 gic_set_priority(i, IRQ_DEFAULT_PRIO_REG);
162 static void guest_inject(struct test_args *args,
163 uint32_t first_intid, uint32_t num,
170 /* Cycle over all priorities to make things more interesting. */
171 for (i = first_intid; i < num + first_intid; i++)
172 gic_set_priority(i, (i % (KVM_NUM_PRIOS - 1)) << 3);
174 asm volatile("msr daifset, #2" : : : "memory");
175 KVM_INJECT_MULTI(cmd, first_intid, num);
177 while (irq_handled < num) {
184 asm volatile("msr daifclr, #2" : : : "memory");
186 GUEST_ASSERT_EQ(irq_handled, num);
187 for (i = first_intid; i < num + first_intid; i++)
188 GUEST_ASSERT_EQ(irqnr_received[i], 1);
189 GUEST_ASSERT_IAR_EMPTY();
191 reset_priorities(args);
194 static void test_injection(struct test_args *args, struct kvm_inject_desc *f)
196 uint32_t nr_irqs = args->nr_irqs;
199 guest_inject(args, MIN_SGI, 1, f->cmd);
200 guest_inject(args, 0, 16, f->cmd);
204 guest_inject(args, MIN_PPI, 1, f->cmd);
207 guest_inject(args, MIN_SPI, 1, f->cmd);
208 guest_inject(args, nr_irqs - 1, 1, f->cmd);
209 guest_inject(args, MIN_SPI, nr_irqs - MIN_SPI, f->cmd);
213 static void guest_code(struct test_args args)
215 uint32_t i, nr_irqs = args.nr_irqs;
216 struct kvm_inject_desc *f;
218 gic_init(GIC_V3, 1, dist, redist);
220 for (i = 0; i < nr_irqs; i++)
223 reset_priorities(&args);
224 gic_set_priority_mask(CPU_PRIO_MASK);
228 /* Start the tests. */
229 for_each_inject_fn(inject_edge_fns, f)
230 test_injection(&args, f);
235 static void run_guest_cmd(struct kvm_vm *vm, int gic_fd,
236 struct kvm_inject_args *inject_args,
237 struct test_args *test_args)
239 kvm_inject_cmd cmd = inject_args->cmd;
240 uint32_t intid = inject_args->first_intid;
241 uint32_t num = inject_args->num;
244 assert(intid < UINT_MAX - num);
247 case KVM_INJECT_EDGE_IRQ_LINE:
248 for (i = intid; i < intid + num; i++)
249 kvm_arm_irq_line(vm, i, 1);
250 for (i = intid; i < intid + num; i++)
251 kvm_arm_irq_line(vm, i, 0);
258 static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
259 struct kvm_inject_args *args)
261 struct kvm_inject_args *kvm_args_hva;
262 vm_vaddr_t kvm_args_gva;
264 kvm_args_gva = uc->args[1];
265 kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva);
266 memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args));
269 static void print_args(struct test_args *args)
271 printf("nr-irqs=%d\n", args->nr_irqs);
274 static void test_vgic(uint32_t nr_irqs)
279 struct kvm_inject_args inject_args;
281 struct test_args args = {
287 vm = vm_create_default(VCPU_ID, 0, guest_code);
288 ucall_init(vm, NULL);
290 vm_init_descriptor_tables(vm);
291 vcpu_init_descriptor_tables(vm, VCPU_ID);
293 /* Setup the guest args page (so it gets the args). */
294 vcpu_args_set(vm, 0, 1, args);
296 gic_fd = vgic_v3_setup(vm, 1, nr_irqs,
297 GICD_BASE_GPA, GICR_BASE_GPA);
299 vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT,
303 vcpu_run(vm, VCPU_ID);
305 switch (get_ucall(vm, VCPU_ID, &uc)) {
307 kvm_inject_get_call(vm, &uc, &inject_args);
308 run_guest_cmd(vm, gic_fd, &inject_args, &args);
311 TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx",
312 (const char *)uc.args[0],
313 __FILE__, uc.args[1], uc.args[2], uc.args[3]);
318 TEST_FAIL("Unknown ucall %lu", uc.cmd);
327 static void help(const char *name)
331 "usage: %s [-n num_irqs]\n", name);
332 printf(" -n: specify the number of IRQs to configure the vgic with.\n");
337 int main(int argc, char **argv)
339 uint32_t nr_irqs = 64;
342 /* Tell stdout not to buffer its content */
343 setbuf(stdout, NULL);
345 while ((opt = getopt(argc, argv, "hg:n:")) != -1) {
348 nr_irqs = atoi(optarg);
349 if (nr_irqs > 1024 || nr_irqs % 32)