Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/kernel.h>
4#include <sys/syscall.h>
5#include <asm/kvm.h>
6#include <asm/kvm_para.h>
7
8#include <arm64/gic_v5.h>
9
10#include "test_util.h"
11#include "kvm_util.h"
12#include "processor.h"
13#include "vgic.h"
14
15#define NR_VCPUS 1
16
17struct vm_gic {
18 struct kvm_vm *vm;
19 int gic_fd;
20 u32 gic_dev_type;
21};
22
23static u64 max_phys_size;
24
25#define GUEST_CMD_IRQ_CDIA 10
26#define GUEST_CMD_IRQ_DIEOI 11
27#define GUEST_CMD_IS_AWAKE 12
28#define GUEST_CMD_IS_READY 13
29
30static void guest_irq_handler(struct ex_regs *regs)
31{
32 bool valid;
33 u32 hwirq;
34 u64 ia;
35 static int count;
36
37 /*
38 * We have pending interrupts. Should never actually enter WFI
39 * here!
40 */
41 wfi();
42 GUEST_SYNC(GUEST_CMD_IS_AWAKE);
43
44 ia = gicr_insn(CDIA);
45 valid = GICV5_GICR_CDIA_VALID(ia);
46
47 GUEST_SYNC(GUEST_CMD_IRQ_CDIA);
48
49 if (!valid)
50 return;
51
52 gsb_ack();
53 isb();
54
55 hwirq = FIELD_GET(GICV5_GICR_CDIA_INTID, ia);
56
57 gic_insn(hwirq, CDDI);
58 gic_insn(0, CDEOI);
59
60 GUEST_SYNC(GUEST_CMD_IRQ_DIEOI);
61
62 if (++count >= 2)
63 GUEST_DONE();
64
65 /* Ask for the next interrupt to be injected */
66 GUEST_SYNC(GUEST_CMD_IS_READY);
67}
68
69static void guest_code(void)
70{
71 local_irq_disable();
72
73 gicv5_cpu_enable_interrupts();
74 local_irq_enable();
75
76 /* Enable the SW_PPI (3) */
77 write_sysreg_s(BIT_ULL(3), SYS_ICC_PPI_ENABLER0_EL1);
78
79 /* Ask for the first interrupt to be injected */
80 GUEST_SYNC(GUEST_CMD_IS_READY);
81
82 /* Loop forever waiting for interrupts */
83 while (1);
84}
85
86
87/* we don't want to assert on run execution, hence that helper */
88static int run_vcpu(struct kvm_vcpu *vcpu)
89{
90 return __vcpu_run(vcpu) ? -errno : 0;
91}
92
93static void vm_gic_destroy(struct vm_gic *v)
94{
95 close(v->gic_fd);
96 kvm_vm_free(v->vm);
97}
98
99static void test_vgic_v5_ppis(u32 gic_dev_type)
100{
101 struct kvm_vcpu *vcpus[NR_VCPUS];
102 struct ucall uc;
103 u64 user_ppis[2];
104 struct vm_gic v;
105 int ret, i;
106
107 v.gic_dev_type = gic_dev_type;
108 v.vm = __vm_create(VM_SHAPE_DEFAULT, NR_VCPUS, 0);
109
110 v.gic_fd = kvm_create_device(v.vm, gic_dev_type);
111
112 for (i = 0; i < NR_VCPUS; i++)
113 vcpus[i] = vm_vcpu_add(v.vm, i, guest_code);
114
115 vm_init_descriptor_tables(v.vm);
116 vm_install_exception_handler(v.vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
117
118 for (i = 0; i < NR_VCPUS; i++)
119 vcpu_init_descriptor_tables(vcpus[i]);
120
121 kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
122 KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
123
124 /* Read out the PPIs that user space is allowed to drive. */
125 kvm_device_attr_get(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
126 KVM_DEV_ARM_VGIC_USERSPACE_PPIS, &user_ppis);
127
128 /* We should always be able to drive the SW_PPI. */
129 TEST_ASSERT(user_ppis[0] & BIT(GICV5_ARCH_PPI_SW_PPI),
130 "SW_PPI is not drivable by userspace");
131
132 while (1) {
133 ret = run_vcpu(vcpus[0]);
134
135 switch (get_ucall(vcpus[0], &uc)) {
136 case UCALL_SYNC:
137 /*
138 * The guest is ready for the next level change. Set
139 * high if ready, and lower if it has been consumed.
140 */
141 if (uc.args[1] == GUEST_CMD_IS_READY ||
142 uc.args[1] == GUEST_CMD_IRQ_DIEOI) {
143 u64 irq;
144 bool level = uc.args[1] == GUEST_CMD_IRQ_DIEOI ? 0 : 1;
145
146 irq = FIELD_PREP(KVM_ARM_IRQ_NUM_MASK, 3);
147 irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;
148
149 _kvm_irq_line(v.vm, irq, level);
150 } else if (uc.args[1] == GUEST_CMD_IS_AWAKE) {
151 pr_info("Guest skipping WFI due to pending IRQ\n");
152 } else if (uc.args[1] == GUEST_CMD_IRQ_CDIA) {
153 pr_info("Guest acknowledged IRQ\n");
154 }
155
156 continue;
157 case UCALL_ABORT:
158 REPORT_GUEST_ASSERT(uc);
159 break;
160 case UCALL_DONE:
161 goto done;
162 default:
163 TEST_FAIL("Unknown ucall %lu", uc.cmd);
164 }
165 }
166
167done:
168 TEST_ASSERT(ret == 0, "Failed to test GICv5 PPIs");
169
170 vm_gic_destroy(&v);
171}
172
173/*
174 * Returns 0 if it's possible to create GIC device of a given type (V5).
175 */
176int test_kvm_device(u32 gic_dev_type)
177{
178 struct kvm_vcpu *vcpus[NR_VCPUS];
179 struct vm_gic v;
180 int ret;
181
182 v.vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus);
183
184 /* try to create a non existing KVM device */
185 ret = __kvm_test_create_device(v.vm, 0);
186 TEST_ASSERT(ret && errno == ENODEV, "unsupported device");
187
188 /* trial mode */
189 ret = __kvm_test_create_device(v.vm, gic_dev_type);
190 if (ret)
191 return ret;
192 v.gic_fd = kvm_create_device(v.vm, gic_dev_type);
193
194 ret = __kvm_create_device(v.vm, gic_dev_type);
195 TEST_ASSERT(ret < 0 && errno == EEXIST, "create GIC device twice");
196
197 vm_gic_destroy(&v);
198
199 return 0;
200}
201
202void run_tests(u32 gic_dev_type)
203{
204 pr_info("Test VGICv5 PPIs\n");
205 test_vgic_v5_ppis(gic_dev_type);
206}
207
208int main(int ac, char **av)
209{
210 int ret;
211 int pa_bits;
212
213 test_disable_default_vgic();
214
215 pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits;
216 max_phys_size = 1ULL << pa_bits;
217
218 ret = test_kvm_device(KVM_DEV_TYPE_ARM_VGIC_V5);
219 if (ret) {
220 pr_info("No GICv5 support; Not running GIC_v5 tests.\n");
221 exit(KSFT_SKIP);
222 }
223
224 pr_info("Running VGIC_V5 tests.\n");
225 run_tests(KVM_DEV_TYPE_ARM_VGIC_V5);
226
227 return 0;
228}