File Index
Files and directories
Sign in
FILE

get-reg-list.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/kvm/get-reg-list.c C
  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 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
131static 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
143static 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
149static 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
163static bool print_list;
164static bool print_filtered;
165
166static 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
313static 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
338static 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
358int 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}
~/ Read-only file preview.