Skip to content

Commit 50b020c

Browse files
Ricardo KollerMarc Zyngier
authored andcommitted
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 <[email protected]> Acked-by: Andrew Jones <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent e95def3 commit 50b020c

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

tools/testing/selftests/kvm/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/aarch64/get-reg-list
55
/aarch64/psci_cpu_on_test
66
/aarch64/vgic_init
7+
/aarch64/vgic_irq
78
/s390x/memop
89
/s390x/resets
910
/s390x/sync_regs_test

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
9494
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
9595
TEST_GEN_PROGS_aarch64 += aarch64/psci_cpu_on_test
9696
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
97+
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
9798
TEST_GEN_PROGS_aarch64 += demand_paging_test
9899
TEST_GEN_PROGS_aarch64 += dirty_log_test
99100
TEST_GEN_PROGS_aarch64 += dirty_log_perf_test
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* vgic_irq.c - Test userspace injection of IRQs
4+
*
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
8+
* it received it.
9+
*/
10+
11+
#include <asm/kvm.h>
12+
#include <asm/kvm_para.h>
13+
#include <linux/sizes.h>
14+
15+
#include "processor.h"
16+
#include "test_util.h"
17+
#include "kvm_util.h"
18+
#include "gic.h"
19+
#include "gic_v3.h"
20+
#include "vgic.h"
21+
22+
#define GICD_BASE_GPA 0x08000000ULL
23+
#define GICR_BASE_GPA 0x080A0000ULL
24+
#define VCPU_ID 0
25+
26+
/*
27+
* KVM implements 32 priority levels:
28+
* 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8
29+
*
30+
* Note that these macros will still be correct in the case that KVM implements
31+
* more priority levels. Also note that 32 is the minimum for GICv3 and GICv2.
32+
*/
33+
#define KVM_NUM_PRIOS 32
34+
#define KVM_PRIO_SHIFT 3 /* steps of 8 = 1 << 3 */
35+
#define LOWEST_PRIO (KVM_NUM_PRIOS - 1)
36+
#define CPU_PRIO_MASK (LOWEST_PRIO << KVM_PRIO_SHIFT) /* 0xf8 */
37+
#define IRQ_DEFAULT_PRIO (LOWEST_PRIO - 1)
38+
#define IRQ_DEFAULT_PRIO_REG (IRQ_DEFAULT_PRIO << KVM_PRIO_SHIFT) /* 0xf0 */
39+
40+
static void *dist = (void *)GICD_BASE_GPA;
41+
static void *redist = (void *)GICR_BASE_GPA;
42+
43+
/*
44+
* The kvm_inject_* utilities are used by the guest to ask the host to inject
45+
* interrupts (e.g., using the KVM_IRQ_LINE ioctl).
46+
*/
47+
48+
typedef enum {
49+
KVM_INJECT_EDGE_IRQ_LINE = 1,
50+
} kvm_inject_cmd;
51+
52+
struct kvm_inject_args {
53+
kvm_inject_cmd cmd;
54+
uint32_t intid;
55+
};
56+
57+
/* Used on the guest side to perform the hypercall. */
58+
static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid);
59+
60+
/* Used on the host side to get the hypercall info. */
61+
static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
62+
struct kvm_inject_args *args);
63+
64+
/* Shared between the guest main thread and the IRQ handlers. */
65+
volatile uint64_t irq_handled;
66+
volatile uint32_t irqnr_received[MAX_SPI + 1];
67+
68+
static void reset_stats(void)
69+
{
70+
int i;
71+
72+
irq_handled = 0;
73+
for (i = 0; i <= MAX_SPI; i++)
74+
irqnr_received[i] = 0;
75+
}
76+
77+
static uint64_t gic_read_ap1r0(void)
78+
{
79+
uint64_t reg = read_sysreg_s(SYS_ICV_AP1R0_EL1);
80+
81+
dsb(sy);
82+
return reg;
83+
}
84+
85+
static void guest_irq_handler(struct ex_regs *regs)
86+
{
87+
uint32_t intid = gic_get_and_ack_irq();
88+
89+
if (intid == IAR_SPURIOUS)
90+
return;
91+
92+
GUEST_ASSERT(gic_irq_get_active(intid));
93+
94+
GUEST_ASSERT(!gic_irq_get_pending(intid));
95+
96+
GUEST_ASSERT(intid < MAX_SPI);
97+
irqnr_received[intid] += 1;
98+
irq_handled += 1;
99+
100+
gic_set_eoi(intid);
101+
GUEST_ASSERT_EQ(gic_read_ap1r0(), 0);
102+
103+
GUEST_ASSERT(!gic_irq_get_active(intid));
104+
GUEST_ASSERT(!gic_irq_get_pending(intid));
105+
}
106+
107+
static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid)
108+
{
109+
struct kvm_inject_args args = {
110+
.cmd = cmd,
111+
.intid = intid,
112+
};
113+
GUEST_SYNC(&args);
114+
}
115+
116+
#define GUEST_ASSERT_IAR_EMPTY() \
117+
do { \
118+
uint32_t _intid; \
119+
_intid = gic_get_and_ack_irq(); \
120+
GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \
121+
} while (0)
122+
123+
static void test_kvm_irq_line(uint32_t intid)
124+
{
125+
reset_stats();
126+
127+
asm volatile("msr daifset, #2" : : : "memory");
128+
kvm_inject_call(KVM_INJECT_EDGE_IRQ_LINE, intid);
129+
130+
while (irq_handled < 1) {
131+
asm volatile("wfi\n"
132+
"msr daifclr, #2\n"
133+
/* handle IRQ */
134+
"msr daifset, #2\n"
135+
: : : "memory");
136+
}
137+
asm volatile("msr daifclr, #2" : : : "memory");
138+
139+
GUEST_ASSERT_EQ(irq_handled, 1);
140+
GUEST_ASSERT_EQ(irqnr_received[intid], 1);
141+
GUEST_ASSERT_IAR_EMPTY();
142+
}
143+
144+
static void guest_code(void)
145+
{
146+
uint32_t i;
147+
uint32_t nr_irqs = 64; /* absolute minimum number of IRQs supported. */
148+
149+
gic_init(GIC_V3, 1, dist, redist);
150+
151+
for (i = 0; i < nr_irqs; i++) {
152+
gic_irq_enable(i);
153+
gic_set_priority(i, IRQ_DEFAULT_PRIO_REG);
154+
}
155+
156+
gic_set_priority_mask(CPU_PRIO_MASK);
157+
158+
local_irq_enable();
159+
160+
test_kvm_irq_line(MIN_SPI);
161+
162+
GUEST_DONE();
163+
}
164+
165+
static void run_guest_cmd(struct kvm_vm *vm, int gic_fd,
166+
struct kvm_inject_args *inject_args)
167+
{
168+
kvm_inject_cmd cmd = inject_args->cmd;
169+
uint32_t intid = inject_args->intid;
170+
171+
switch (cmd) {
172+
case KVM_INJECT_EDGE_IRQ_LINE:
173+
kvm_arm_irq_line(vm, intid, 1);
174+
kvm_arm_irq_line(vm, intid, 0);
175+
break;
176+
default:
177+
break;
178+
}
179+
}
180+
181+
static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
182+
struct kvm_inject_args *args)
183+
{
184+
struct kvm_inject_args *kvm_args_hva;
185+
vm_vaddr_t kvm_args_gva;
186+
187+
kvm_args_gva = uc->args[1];
188+
kvm_args_hva = (struct kvm_inject_args *)addr_gva2hva(vm, kvm_args_gva);
189+
memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args));
190+
}
191+
192+
193+
static void test_vgic(void)
194+
{
195+
struct ucall uc;
196+
int gic_fd;
197+
struct kvm_vm *vm;
198+
struct kvm_inject_args inject_args;
199+
200+
vm = vm_create_default(VCPU_ID, 0, guest_code);
201+
ucall_init(vm, NULL);
202+
203+
vm_init_descriptor_tables(vm);
204+
vcpu_init_descriptor_tables(vm, VCPU_ID);
205+
206+
gic_fd = vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA);
207+
208+
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT,
209+
guest_irq_handler);
210+
211+
while (1) {
212+
vcpu_run(vm, VCPU_ID);
213+
214+
switch (get_ucall(vm, VCPU_ID, &uc)) {
215+
case UCALL_SYNC:
216+
kvm_inject_get_call(vm, &uc, &inject_args);
217+
run_guest_cmd(vm, gic_fd, &inject_args);
218+
break;
219+
case UCALL_ABORT:
220+
TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx",
221+
(const char *)uc.args[0],
222+
__FILE__, uc.args[1], uc.args[2], uc.args[3]);
223+
break;
224+
case UCALL_DONE:
225+
goto done;
226+
default:
227+
TEST_FAIL("Unknown ucall %lu", uc.cmd);
228+
}
229+
}
230+
231+
done:
232+
close(gic_fd);
233+
kvm_vm_free(vm);
234+
}
235+
236+
int main(int ac, char **av)
237+
{
238+
/* Tell stdout not to buffer its content */
239+
setbuf(stdout, NULL);
240+
241+
test_vgic();
242+
243+
return 0;
244+
}

0 commit comments

Comments
 (0)