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
31static struct kvm_reg_list *reg_list;
32static __u64 *blessed_reg, blessed_n;
33
34extern struct vcpu_reg_list *vcpu_configs[];
35extern 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
57static 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
84bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)
85{
86 return true;
87}
88
89bool __weak filter_reg(__u64 reg)
90{
91 return false;
92}
93
94static 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
104void __weak print_reg(const char *prefix, __u64 id)
105{
106 printf("\t0x%llx,\n", id);
107}
108
109bool __weak check_reject_set(int err)
110{
111 return true;
112}
113
114void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
115{
116}
117
118#ifdef __aarch64__
119static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init)
120{
121 struct vcpu_reg_sublist *s;
122
123 for_each_sublist(c, s)
124 if (s->capability)
125 init->features[s->feature / 32] |= 1 << (s->feature % 32);
126}
127
128static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
129{
130 struct kvm_vcpu_init init = { .target = -1, };
131 struct kvm_vcpu *vcpu;
132
133 prepare_vcpu_init(c, &init);
134 vcpu = __vm_vcpu_add(vm, 0);
135 aarch64_vcpu_setup(vcpu, &init);
136
137 return vcpu;
138}
139#else
140static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
141{
142 return __vm_vcpu_add(vm, 0);
143}
144#endif
145
146static void check_supported(struct vcpu_reg_list *c)
147{
148 struct vcpu_reg_sublist *s;
149
150 for_each_sublist(c, s) {
151 if (!s->capability)
152 continue;
153
154 __TEST_REQUIRE(kvm_has_cap(s->capability),
155 "%s: %s not available, skipping tests",
156 config_name(c), s->name);
157 }
158}
159
160static bool print_list;
161static bool print_filtered;
162
163static void run_test(struct vcpu_reg_list *c)
164{
165 int new_regs = 0, missing_regs = 0, i, n;
166 int failed_get = 0, failed_set = 0, failed_reject = 0;
167 int skipped_set = 0;
168 struct kvm_vcpu *vcpu;
169 struct kvm_vm *vm;
170 struct vcpu_reg_sublist *s;
171
172 check_supported(c);
173
174 vm = vm_create_barebones();
175 vcpu = vcpu_config_get_vcpu(c, vm);
176 finalize_vcpu(vcpu, c);
177
178 reg_list = vcpu_get_reg_list(vcpu);
179
180 if (print_list || print_filtered) {
181 putchar('\n');
182 for_each_reg(i) {
183 __u64 id = reg_list->reg[i];
184 if ((print_list && !filter_reg(id)) ||
185 (print_filtered && filter_reg(id)))
186 print_reg(config_name(c), id);
187 }
188 putchar('\n');
189 return;
190 }
191
192 for_each_sublist(c, s)
193 blessed_n += s->regs_n;
194 blessed_reg = calloc(blessed_n, sizeof(__u64));
195
196 n = 0;
197 for_each_sublist(c, s) {
198 for (i = 0; i < s->regs_n; ++i)
199 blessed_reg[n++] = s->regs[i];
200 }
201
202 /*
203 * We only test that we can get the register and then write back the
204 * same value. Some registers may allow other values to be written
205 * back, but others only allow some bits to be changed, and at least
206 * for ID registers set will fail if the value does not exactly match
207 * what was returned by get. If registers that allow other values to
208 * be written need to have the other values tested, then we should
209 * create a new set of tests for those in a new independent test
210 * executable.
211 *
212 * Only do the get/set tests on present, blessed list registers,
213 * since we don't know the capabilities of any new registers.
214 */
215 for_each_present_blessed_reg(i) {
216 uint8_t addr[2048 / 8];
217 struct kvm_one_reg reg = {
218 .id = reg_list->reg[i],
219 .addr = (__u64)&addr,
220 };
221 bool reject_reg = false, skip_reg = false;
222 int ret;
223
224 ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);
225 if (ret) {
226 printf("%s: Failed to get ", config_name(c));
227 print_reg(config_name(c), reg.id);
228 putchar('\n');
229 ++failed_get;
230 }
231
232 for_each_sublist(c, s) {
233 /* rejects_set registers are rejected for set operation */
234 if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {
235 reject_reg = true;
236 ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
237 if (ret != -1 || !check_reject_set(errno)) {
238 printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);
239 print_reg(config_name(c), reg.id);
240 putchar('\n');
241 ++failed_reject;
242 }
243 break;
244 }
245
246 /* skips_set registers are skipped for set operation */
247 if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {
248 skip_reg = true;
249 ++skipped_set;
250 break;
251 }
252 }
253
254 if (!reject_reg && !skip_reg) {
255 ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
256 if (ret) {
257 printf("%s: Failed to set ", config_name(c));
258 print_reg(config_name(c), reg.id);
259 putchar('\n');
260 ++failed_set;
261 }
262 }
263 }
264
265 for_each_new_reg(i)
266 ++new_regs;
267
268 for_each_missing_reg(i)
269 ++missing_regs;
270
271 if (new_regs || missing_regs) {
272 n = 0;
273 for_each_reg_filtered(i)
274 ++n;
275
276 printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);
277 printf("%s: Number registers: %5lld (includes %lld filtered registers)\n",
278 config_name(c), reg_list->n, reg_list->n - n);
279 }
280
281 if (new_regs) {
282 printf("\n%s: There are %d new registers.\n"
283 "Consider adding them to the blessed reg "
284 "list with the following lines:\n\n", config_name(c), new_regs);
285 for_each_new_reg(i)
286 print_reg(config_name(c), reg_list->reg[i]);
287 putchar('\n');
288 }
289
290 if (missing_regs) {
291 printf("\n%s: There are %d missing registers.\n"
292 "The following lines are missing registers:\n\n", config_name(c), missing_regs);
293 for_each_missing_reg(i)
294 print_reg(config_name(c), blessed_reg[i]);
295 putchar('\n');
296 }
297
298 TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
299 "%s: There are %d missing registers; %d registers failed get; "
300 "%d registers failed set; %d registers failed reject; %d registers skipped set",
301 config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);
302
303 pr_info("%s: PASS\n", config_name(c));
304 blessed_n = 0;
305 free(blessed_reg);
306 free(reg_list);
307 kvm_vm_free(vm);
308}
309
310static void help(void)
311{
312 struct vcpu_reg_list *c;
313 int i;
314
315 printf(
316 "\n"
317 "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"
318 " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n"
319 " '<selection>' may be\n");
320
321 for (i = 0; i < vcpu_configs_n; ++i) {
322 c = vcpu_configs[i];
323 printf(
324 " '%s'\n", config_name(c));
325 }
326
327 printf(
328 "\n"
329 " --list Print the register list rather than test it (requires --config)\n"
330 " --list-filtered Print registers that would normally be filtered out (requires --config)\n"
331 "\n"
332 );
333}
334
335static struct vcpu_reg_list *parse_config(const char *config)
336{
337 struct vcpu_reg_list *c = NULL;
338 int i;
339
340 if (config[8] != '=')
341 help(), exit(1);
342
343 for (i = 0; i < vcpu_configs_n; ++i) {
344 c = vcpu_configs[i];
345 if (strcmp(config_name(c), &config[9]) == 0)
346 break;
347 }
348
349 if (i == vcpu_configs_n)
350 help(), exit(1);
351
352 return c;
353}
354
355int main(int ac, char **av)
356{
357 struct vcpu_reg_list *c, *sel = NULL;
358 int i, ret = 0;
359 pid_t pid;
360
361 for (i = 1; i < ac; ++i) {
362 if (strncmp(av[i], "--config", 8) == 0)
363 sel = parse_config(config: av[i]);
364 else if (strcmp(av[i], "--list") == 0)
365 print_list = true;
366 else if (strcmp(av[i], "--list-filtered") == 0)
367 print_filtered = true;
368 else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)
369 help(), exit(0);
370 else
371 help(), exit(1);
372 }
373
374 if (print_list || print_filtered) {
375 /*
376 * We only want to print the register list of a single config.
377 */
378 if (!sel)
379 help(), exit(1);
380 }
381
382 for (i = 0; i < vcpu_configs_n; ++i) {
383 c = vcpu_configs[i];
384 if (sel && c != sel)
385 continue;
386
387 pid = fork();
388
389 if (!pid) {
390 run_test(c);
391 exit(0);
392 } else {
393 int wstatus;
394 pid_t wpid = wait(&wstatus);
395 TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");
396 if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)
397 ret = KSFT_FAIL;
398 }
399 }
400
401 return ret;
402}
403

source code of linux/tools/testing/selftests/kvm/get-reg-list.c