Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/kvm/get-reg-list.c
38189 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Check for KVM_GET_REG_LIST regressions.
4
*
5
* Copyright (C) 2020, Red Hat, Inc.
6
*
7
* When attempting to migrate from a host with an older kernel to a host
8
* with a newer kernel we allow the newer kernel on the destination to
9
* list new registers with get-reg-list. We assume they'll be unused, at
10
* least until the guest reboots, and so they're relatively harmless.
11
* However, if the destination host with the newer kernel is missing
12
* registers which the source host with the older kernel has, then that's
13
* a regression in get-reg-list. This test checks for that regression by
14
* checking the current list against a blessed list. We should never have
15
* missing registers, but if new ones appear then they can probably be
16
* added to the blessed list. A completely new blessed list can be created
17
* by running the test with the --list command line argument.
18
*
19
* The blessed list should be created from the oldest possible kernel.
20
*/
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <sys/types.h>
26
#include <sys/wait.h>
27
#include "kvm_util.h"
28
#include "test_util.h"
29
#include "processor.h"
30
31
static struct kvm_reg_list *reg_list;
32
static __u64 *blessed_reg, blessed_n;
33
34
extern struct vcpu_reg_list *vcpu_configs[];
35
extern int vcpu_configs_n;
36
37
#define for_each_reg(i) \
38
for ((i) = 0; (i) < reg_list->n; ++(i))
39
40
#define for_each_reg_filtered(i) \
41
for_each_reg(i) \
42
if (!filter_reg(reg_list->reg[i]))
43
44
#define for_each_missing_reg(i) \
45
for ((i) = 0; (i) < blessed_n; ++(i)) \
46
if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \
47
if (check_supported_reg(vcpu, blessed_reg[i]))
48
49
#define for_each_new_reg(i) \
50
for_each_reg_filtered(i) \
51
if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
52
53
#define for_each_present_blessed_reg(i) \
54
for_each_reg(i) \
55
if (find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
56
57
static const char *config_name(struct vcpu_reg_list *c)
58
{
59
struct vcpu_reg_sublist *s;
60
int len = 0;
61
62
if (c->name)
63
return c->name;
64
65
for_each_sublist(c, s)
66
len += strlen(s->name) + 1;
67
68
c->name = malloc(len);
69
70
len = 0;
71
for_each_sublist(c, s) {
72
if (!strcmp(s->name, "base"))
73
continue;
74
if (len)
75
c->name[len++] = '+';
76
strcpy(c->name + len, s->name);
77
len += strlen(s->name);
78
}
79
c->name[len] = '\0';
80
81
return c->name;
82
}
83
84
bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)
85
{
86
return true;
87
}
88
89
bool __weak filter_reg(__u64 reg)
90
{
91
return false;
92
}
93
94
static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)
95
{
96
int i;
97
98
for (i = 0; i < nr_regs; ++i)
99
if (reg == regs[i])
100
return true;
101
return false;
102
}
103
104
void __weak print_reg(const char *prefix, __u64 id)
105
{
106
printf("\t0x%llx,\n", id);
107
}
108
109
bool __weak check_reject_set(int err)
110
{
111
return true;
112
}
113
114
void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
115
{
116
}
117
118
#ifdef __aarch64__
119
static void prepare_vcpu_init(struct kvm_vm *vm, struct vcpu_reg_list *c,
120
struct kvm_vcpu_init *init)
121
{
122
struct vcpu_reg_sublist *s;
123
124
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, init);
125
126
for_each_sublist(c, s)
127
if (s->capability)
128
init->features[s->feature / 32] |= 1 << (s->feature % 32);
129
}
130
131
static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
132
{
133
struct kvm_vcpu_init init;
134
struct kvm_vcpu *vcpu;
135
136
prepare_vcpu_init(vm, c, &init);
137
vcpu = __vm_vcpu_add(vm, 0);
138
aarch64_vcpu_setup(vcpu, &init);
139
140
return vcpu;
141
}
142
#else
143
static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
144
{
145
return __vm_vcpu_add(vm, 0);
146
}
147
#endif
148
149
static void check_supported(struct vcpu_reg_list *c)
150
{
151
struct vcpu_reg_sublist *s;
152
153
for_each_sublist(c, s) {
154
if (!s->capability)
155
continue;
156
157
__TEST_REQUIRE(kvm_has_cap(s->capability),
158
"%s: %s not available, skipping tests",
159
config_name(c), s->name);
160
}
161
}
162
163
static bool print_list;
164
static bool print_filtered;
165
166
static void run_test(struct vcpu_reg_list *c)
167
{
168
int new_regs = 0, missing_regs = 0, i, n;
169
int failed_get = 0, failed_set = 0, failed_reject = 0;
170
int skipped_set = 0;
171
struct kvm_vcpu *vcpu;
172
struct kvm_vm *vm;
173
struct vcpu_reg_sublist *s;
174
175
check_supported(c);
176
177
vm = vm_create_barebones();
178
vcpu = vcpu_config_get_vcpu(c, vm);
179
finalize_vcpu(vcpu, c);
180
181
reg_list = vcpu_get_reg_list(vcpu);
182
183
if (print_list || print_filtered) {
184
putchar('\n');
185
for_each_reg(i) {
186
__u64 id = reg_list->reg[i];
187
if ((print_list && !filter_reg(id)) ||
188
(print_filtered && filter_reg(id)))
189
print_reg(config_name(c), id);
190
}
191
putchar('\n');
192
return;
193
}
194
195
for_each_sublist(c, s)
196
blessed_n += s->regs_n;
197
blessed_reg = calloc(blessed_n, sizeof(__u64));
198
199
n = 0;
200
for_each_sublist(c, s) {
201
for (i = 0; i < s->regs_n; ++i)
202
blessed_reg[n++] = s->regs[i];
203
}
204
205
/*
206
* We only test that we can get the register and then write back the
207
* same value. Some registers may allow other values to be written
208
* back, but others only allow some bits to be changed, and at least
209
* for ID registers set will fail if the value does not exactly match
210
* what was returned by get. If registers that allow other values to
211
* be written need to have the other values tested, then we should
212
* create a new set of tests for those in a new independent test
213
* executable.
214
*
215
* Only do the get/set tests on present, blessed list registers,
216
* since we don't know the capabilities of any new registers.
217
*/
218
for_each_present_blessed_reg(i) {
219
uint8_t addr[2048 / 8];
220
struct kvm_one_reg reg = {
221
.id = reg_list->reg[i],
222
.addr = (__u64)&addr,
223
};
224
bool reject_reg = false, skip_reg = false;
225
int ret;
226
227
ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);
228
if (ret) {
229
printf("%s: Failed to get ", config_name(c));
230
print_reg(config_name(c), reg.id);
231
putchar('\n');
232
++failed_get;
233
}
234
235
for_each_sublist(c, s) {
236
/* rejects_set registers are rejected for set operation */
237
if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {
238
reject_reg = true;
239
ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
240
if (ret != -1 || !check_reject_set(errno)) {
241
printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);
242
print_reg(config_name(c), reg.id);
243
putchar('\n');
244
++failed_reject;
245
}
246
break;
247
}
248
249
/* skips_set registers are skipped for set operation */
250
if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {
251
skip_reg = true;
252
++skipped_set;
253
break;
254
}
255
}
256
257
if (!reject_reg && !skip_reg) {
258
ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
259
if (ret) {
260
printf("%s: Failed to set ", config_name(c));
261
print_reg(config_name(c), reg.id);
262
putchar('\n');
263
++failed_set;
264
}
265
}
266
}
267
268
for_each_new_reg(i)
269
++new_regs;
270
271
for_each_missing_reg(i)
272
++missing_regs;
273
274
if (new_regs || missing_regs) {
275
n = 0;
276
for_each_reg_filtered(i)
277
++n;
278
279
printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);
280
printf("%s: Number registers: %5lld (includes %lld filtered registers)\n",
281
config_name(c), reg_list->n, reg_list->n - n);
282
}
283
284
if (new_regs) {
285
printf("\n%s: There are %d new registers.\n"
286
"Consider adding them to the blessed reg "
287
"list with the following lines:\n\n", config_name(c), new_regs);
288
for_each_new_reg(i)
289
print_reg(config_name(c), reg_list->reg[i]);
290
putchar('\n');
291
}
292
293
if (missing_regs) {
294
printf("\n%s: There are %d missing registers.\n"
295
"The following lines are missing registers:\n\n", config_name(c), missing_regs);
296
for_each_missing_reg(i)
297
print_reg(config_name(c), blessed_reg[i]);
298
putchar('\n');
299
}
300
301
TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
302
"%s: There are %d missing registers; %d registers failed get; "
303
"%d registers failed set; %d registers failed reject; %d registers skipped set",
304
config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);
305
306
pr_info("%s: PASS\n", config_name(c));
307
blessed_n = 0;
308
free(blessed_reg);
309
free(reg_list);
310
kvm_vm_free(vm);
311
}
312
313
static void help(void)
314
{
315
struct vcpu_reg_list *c;
316
int i;
317
318
printf(
319
"\n"
320
"usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"
321
" --config=<selection> Used to select a specific vcpu configuration for the test/listing\n"
322
" '<selection>' may be\n");
323
324
for (i = 0; i < vcpu_configs_n; ++i) {
325
c = vcpu_configs[i];
326
printf(
327
" '%s'\n", config_name(c));
328
}
329
330
printf(
331
"\n"
332
" --list Print the register list rather than test it (requires --config)\n"
333
" --list-filtered Print registers that would normally be filtered out (requires --config)\n"
334
"\n"
335
);
336
}
337
338
static struct vcpu_reg_list *parse_config(const char *config)
339
{
340
struct vcpu_reg_list *c = NULL;
341
int i;
342
343
if (config[8] != '=')
344
help(), exit(1);
345
346
for (i = 0; i < vcpu_configs_n; ++i) {
347
c = vcpu_configs[i];
348
if (strcmp(config_name(c), &config[9]) == 0)
349
break;
350
}
351
352
if (i == vcpu_configs_n)
353
help(), exit(1);
354
355
return c;
356
}
357
358
int main(int ac, char **av)
359
{
360
struct vcpu_reg_list *c, *sel = NULL;
361
int i, ret = 0;
362
pid_t pid;
363
364
for (i = 1; i < ac; ++i) {
365
if (strncmp(av[i], "--config", 8) == 0)
366
sel = parse_config(av[i]);
367
else if (strcmp(av[i], "--list") == 0)
368
print_list = true;
369
else if (strcmp(av[i], "--list-filtered") == 0)
370
print_filtered = true;
371
else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)
372
help(), exit(0);
373
else
374
help(), exit(1);
375
}
376
377
if (print_list || print_filtered) {
378
/*
379
* We only want to print the register list of a single config.
380
*/
381
if (!sel)
382
help(), exit(1);
383
}
384
385
for (i = 0; i < vcpu_configs_n; ++i) {
386
c = vcpu_configs[i];
387
if (sel && c != sel)
388
continue;
389
390
pid = fork();
391
392
if (!pid) {
393
run_test(c);
394
exit(0);
395
} else {
396
int wstatus;
397
pid_t wpid = wait(&wstatus);
398
TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");
399
if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)
400
ret = KSFT_FAIL;
401
}
402
}
403
404
return ret;
405
}
406
407