1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
// SPDX-License-Identifier: GPL-2.0
// Check that, on a GICv3-capable system (GICv3 native, or GICv5 with
// FEAT_GCIE_LEGACY), not configuring GICv3 correctly results in all
// of the sysregs generating an UNDEF exception. Do the same for GICv5
// on a GICv5 host.
#include <test_util.h>
#include <kvm_util.h>
#include <processor.h>
#include <arm64/gic_v5.h>
static volatile bool handled;
#define __check_sr_read(r) \
({ \
u64 val; \
\
handled = false; \
dsb(sy); \
val = read_sysreg_s(SYS_ ## r); \
val; \
})
#define __check_sr_write(r) \
do { \
handled = false; \
dsb(sy); \
write_sysreg_s(0, SYS_ ## r); \
isb(); \
} while (0)
#define __check_gicv5_gicr_op(r) \
({ \
u64 val; \
\
handled = false; \
dsb(sy); \
val = read_sysreg_s(GICV5_OP_GICR_ ## r); \
val; \
})
#define __check_gicv5_gic_op(r) \
do { \
handled = false; \
dsb(sy); \
write_sysreg_s(0, GICV5_OP_GIC_ ## r); \
isb(); \
} while (0)
/* Fatal checks */
#define check_sr_read(r) \
do { \
__check_sr_read(r); \
__GUEST_ASSERT(handled, #r " no read trap"); \
} while (0)
#define check_sr_write(r) \
do { \
__check_sr_write(r); \
__GUEST_ASSERT(handled, #r " no write trap"); \
} while (0)
#define check_sr_rw(r) \
do { \
check_sr_read(r); \
check_sr_write(r); \
} while (0)
#define check_gicv5_gicr_op(r) \
do { \
__check_gicv5_gicr_op(r); \
__GUEST_ASSERT(handled, #r " no read trap"); \
} while (0)
#define check_gicv5_gic_op(r) \
do { \
__check_gicv5_gic_op(r); \
__GUEST_ASSERT(handled, #r " no write trap"); \
} while (0)
static void guest_code_gicv3(void)
{
u64 val;
/*
* Check that we advertise that ID_AA64PFR0_EL1.GIC == 0, having
* hidden the feature at runtime without any other userspace action.
*/
__GUEST_ASSERT(FIELD_GET(ID_AA64PFR0_EL1_GIC,
read_sysreg(id_aa64pfr0_el1)) == 0,
"GICv3 wrongly advertised");
/*
* Access all GICv3 registers, and fail if we don't get an UNDEF.
* Note that we happily access all the APxRn registers without
* checking their existence, as all we want to see is a failure.
*/
check_sr_rw(ICC_PMR_EL1);
check_sr_read(ICC_IAR0_EL1);
check_sr_write(ICC_EOIR0_EL1);
check_sr_rw(ICC_HPPIR0_EL1);
check_sr_rw(ICC_BPR0_EL1);
check_sr_rw(ICC_AP0R0_EL1);
check_sr_rw(ICC_AP0R1_EL1);
check_sr_rw(ICC_AP0R2_EL1);
check_sr_rw(ICC_AP0R3_EL1);
check_sr_rw(ICC_AP1R0_EL1);
check_sr_rw(ICC_AP1R1_EL1);
check_sr_rw(ICC_AP1R2_EL1);
check_sr_rw(ICC_AP1R3_EL1);
check_sr_write(ICC_DIR_EL1);
check_sr_read(ICC_RPR_EL1);
check_sr_write(ICC_SGI1R_EL1);
check_sr_write(ICC_ASGI1R_EL1);
check_sr_write(ICC_SGI0R_EL1);
check_sr_read(ICC_IAR1_EL1);
check_sr_write(ICC_EOIR1_EL1);
check_sr_rw(ICC_HPPIR1_EL1);
check_sr_rw(ICC_BPR1_EL1);
check_sr_rw(ICC_CTLR_EL1);
check_sr_rw(ICC_IGRPEN0_EL1);
check_sr_rw(ICC_IGRPEN1_EL1);
/*
* ICC_SRE_EL1 may not be trappable, as ICC_SRE_EL2.Enable can
* be RAO/WI. Engage in non-fatal accesses, starting with a
* write of 0 to try and disable SRE, and let's see if it
* sticks.
*/
__check_sr_write(ICC_SRE_EL1);
if (!handled)
GUEST_PRINTF("ICC_SRE_EL1 write not trapping (OK)\n");
val = __check_sr_read(ICC_SRE_EL1);
if (!handled) {
__GUEST_ASSERT((val & BIT(0)),
"ICC_SRE_EL1 not trapped but ICC_SRE_EL1.SRE not set\n");
GUEST_PRINTF("ICC_SRE_EL1 read not trapping (OK)\n");
}
GUEST_DONE();
}
static void guest_code_gicv5(void)
{
/*
* Check that we advertise that ID_AA64PFR2_EL1.GCIE == 0, having
* hidden the feature at runtime without any other userspace action.
*/
__GUEST_ASSERT(FIELD_GET(ID_AA64PFR2_EL1_GCIE,
read_sysreg_s(SYS_ID_AA64PFR2_EL1)) == 0,
"GICv5 wrongly advertised");
/*
* Try all GICv5 instructions, and fail if we don't get an UNDEF.
*/
check_gicv5_gic_op(CDAFF);
check_gicv5_gic_op(CDDI);
check_gicv5_gic_op(CDDIS);
check_gicv5_gic_op(CDEOI);
check_gicv5_gic_op(CDHM);
check_gicv5_gic_op(CDPEND);
check_gicv5_gic_op(CDPRI);
check_gicv5_gic_op(CDRCFG);
check_gicv5_gicr_op(CDIA);
check_gicv5_gicr_op(CDNMIA);
/* Check General System Register acccesses */
check_sr_rw(ICC_APR_EL1);
check_sr_rw(ICC_CR0_EL1);
check_sr_read(ICC_HPPIR_EL1);
check_sr_read(ICC_IAFFIDR_EL1);
check_sr_rw(ICC_ICSR_EL1);
check_sr_read(ICC_IDR0_EL1);
check_sr_rw(ICC_PCR_EL1);
/* Check PPI System Register accessess */
check_sr_rw(ICC_PPI_CACTIVER0_EL1);
check_sr_rw(ICC_PPI_CACTIVER1_EL1);
check_sr_rw(ICC_PPI_SACTIVER0_EL1);
check_sr_rw(ICC_PPI_SACTIVER1_EL1);
check_sr_rw(ICC_PPI_CPENDR0_EL1);
check_sr_rw(ICC_PPI_CPENDR1_EL1);
check_sr_rw(ICC_PPI_SPENDR0_EL1);
check_sr_rw(ICC_PPI_SPENDR1_EL1);
check_sr_rw(ICC_PPI_ENABLER0_EL1);
check_sr_rw(ICC_PPI_ENABLER1_EL1);
check_sr_read(ICC_PPI_HMR0_EL1);
check_sr_read(ICC_PPI_HMR1_EL1);
check_sr_rw(ICC_PPI_PRIORITYR0_EL1);
check_sr_rw(ICC_PPI_PRIORITYR1_EL1);
check_sr_rw(ICC_PPI_PRIORITYR2_EL1);
check_sr_rw(ICC_PPI_PRIORITYR3_EL1);
check_sr_rw(ICC_PPI_PRIORITYR4_EL1);
check_sr_rw(ICC_PPI_PRIORITYR5_EL1);
check_sr_rw(ICC_PPI_PRIORITYR6_EL1);
check_sr_rw(ICC_PPI_PRIORITYR7_EL1);
check_sr_rw(ICC_PPI_PRIORITYR8_EL1);
check_sr_rw(ICC_PPI_PRIORITYR9_EL1);
check_sr_rw(ICC_PPI_PRIORITYR10_EL1);
check_sr_rw(ICC_PPI_PRIORITYR11_EL1);
check_sr_rw(ICC_PPI_PRIORITYR12_EL1);
check_sr_rw(ICC_PPI_PRIORITYR13_EL1);
check_sr_rw(ICC_PPI_PRIORITYR14_EL1);
check_sr_rw(ICC_PPI_PRIORITYR15_EL1);
GUEST_DONE();
}
static void guest_undef_handler(struct ex_regs *regs)
{
/* Success, we've gracefully exploded! */
handled = true;
regs->pc += 4;
}
static void test_run_vcpu(struct kvm_vcpu *vcpu)
{
struct ucall uc;
do {
vcpu_run(vcpu);
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);
break;
case UCALL_PRINTF:
printf("%s", uc.buffer);
break;
case UCALL_DONE:
break;
default:
TEST_FAIL("Unknown ucall %lu", uc.cmd);
}
} while (uc.cmd != UCALL_DONE);
}
static void test_guest_no_vgic(void *guest_code)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
/* Create a VM without a GIC */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
vm_init_descriptor_tables(vm);
vcpu_init_descriptor_tables(vcpu);
vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT,
ESR_ELx_EC_UNKNOWN, guest_undef_handler);
test_run_vcpu(vcpu);
kvm_vm_free(vm);
}
int main(int argc, char *argv[])
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
bool has_v3, has_v5;
u64 pfr;
test_disable_default_vgic();
vm = vm_create_with_one_vcpu(&vcpu, NULL);
pfr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR0_EL1));
has_v3 = !!FIELD_GET(ID_AA64PFR0_EL1_GIC, pfr);
pfr = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64PFR2_EL1));
has_v5 = !!FIELD_GET(ID_AA64PFR2_EL1_GCIE, pfr);
kvm_vm_free(vm);
__TEST_REQUIRE(has_v3 || has_v5,
"Neither GICv3 nor GICv5 supported.");
if (has_v3) {
pr_info("Testing no-vgic-v3\n");
test_guest_no_vgic(guest_code_gicv3);
} else {
pr_info("No GICv3 support: skipping no-vgic-v3 test\n");
}
if (has_v5) {
pr_info("Testing no-vgic-v5\n");
test_guest_no_vgic(guest_code_gicv5);
} else {
pr_info("No GICv5 support: skipping no-vgic-v5 test\n");
}
return 0;
}
|