xref: /linux/tools/testing/selftests/kvm/get-reg-list.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
117da79e0SAndrew Jones // SPDX-License-Identifier: GPL-2.0
217da79e0SAndrew Jones /*
317da79e0SAndrew Jones  * Check for KVM_GET_REG_LIST regressions.
417da79e0SAndrew Jones  *
517da79e0SAndrew Jones  * Copyright (C) 2020, Red Hat, Inc.
617da79e0SAndrew Jones  *
717da79e0SAndrew Jones  * When attempting to migrate from a host with an older kernel to a host
817da79e0SAndrew Jones  * with a newer kernel we allow the newer kernel on the destination to
917da79e0SAndrew Jones  * list new registers with get-reg-list. We assume they'll be unused, at
1017da79e0SAndrew Jones  * least until the guest reboots, and so they're relatively harmless.
1117da79e0SAndrew Jones  * However, if the destination host with the newer kernel is missing
1217da79e0SAndrew Jones  * registers which the source host with the older kernel has, then that's
1317da79e0SAndrew Jones  * a regression in get-reg-list. This test checks for that regression by
1417da79e0SAndrew Jones  * checking the current list against a blessed list. We should never have
1517da79e0SAndrew Jones  * missing registers, but if new ones appear then they can probably be
1617da79e0SAndrew Jones  * added to the blessed list. A completely new blessed list can be created
1717da79e0SAndrew Jones  * by running the test with the --list command line argument.
1817da79e0SAndrew Jones  *
1917da79e0SAndrew Jones  * The blessed list should be created from the oldest possible kernel.
2017da79e0SAndrew Jones  */
2117da79e0SAndrew Jones #include <stdio.h>
2217da79e0SAndrew Jones #include <stdlib.h>
2317da79e0SAndrew Jones #include <string.h>
2417da79e0SAndrew Jones #include <unistd.h>
2517da79e0SAndrew Jones #include <sys/types.h>
2617da79e0SAndrew Jones #include <sys/wait.h>
2717da79e0SAndrew Jones #include "kvm_util.h"
2817da79e0SAndrew Jones #include "test_util.h"
2917da79e0SAndrew Jones #include "processor.h"
3017da79e0SAndrew Jones 
3117da79e0SAndrew Jones static struct kvm_reg_list *reg_list;
3217da79e0SAndrew Jones static __u64 *blessed_reg, blessed_n;
3317da79e0SAndrew Jones 
3417da79e0SAndrew Jones extern struct vcpu_reg_list *vcpu_configs[];
3517da79e0SAndrew Jones extern int vcpu_configs_n;
3617da79e0SAndrew Jones 
3717da79e0SAndrew Jones #define for_each_reg(i)								\
3817da79e0SAndrew Jones 	for ((i) = 0; (i) < reg_list->n; ++(i))
3917da79e0SAndrew Jones 
4017da79e0SAndrew Jones #define for_each_reg_filtered(i)						\
4117da79e0SAndrew Jones 	for_each_reg(i)								\
4217da79e0SAndrew Jones 		if (!filter_reg(reg_list->reg[i]))
4317da79e0SAndrew Jones 
4417da79e0SAndrew Jones #define for_each_missing_reg(i)							\
4517da79e0SAndrew Jones 	for ((i) = 0; (i) < blessed_n; ++(i))					\
4617da79e0SAndrew Jones 		if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i]))	\
4717da79e0SAndrew Jones 			if (check_supported_reg(vcpu, blessed_reg[i]))
4817da79e0SAndrew Jones 
4917da79e0SAndrew Jones #define for_each_new_reg(i)							\
5017da79e0SAndrew Jones 	for_each_reg_filtered(i)						\
5117da79e0SAndrew Jones 		if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
5217da79e0SAndrew Jones 
53c4746771SHaibo Xu #define for_each_present_blessed_reg(i)						\
54c4746771SHaibo Xu 	for_each_reg(i)								\
55c4746771SHaibo Xu 		if (find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
56c4746771SHaibo Xu 
config_name(struct vcpu_reg_list * c)5717da79e0SAndrew Jones static const char *config_name(struct vcpu_reg_list *c)
5817da79e0SAndrew Jones {
5917da79e0SAndrew Jones 	struct vcpu_reg_sublist *s;
6017da79e0SAndrew Jones 	int len = 0;
6117da79e0SAndrew Jones 
6217da79e0SAndrew Jones 	if (c->name)
6317da79e0SAndrew Jones 		return c->name;
6417da79e0SAndrew Jones 
6517da79e0SAndrew Jones 	for_each_sublist(c, s)
6617da79e0SAndrew Jones 		len += strlen(s->name) + 1;
6717da79e0SAndrew Jones 
6817da79e0SAndrew Jones 	c->name = malloc(len);
6917da79e0SAndrew Jones 
7017da79e0SAndrew Jones 	len = 0;
7117da79e0SAndrew Jones 	for_each_sublist(c, s) {
7217da79e0SAndrew Jones 		if (!strcmp(s->name, "base"))
7317da79e0SAndrew Jones 			continue;
74e39120abSPaolo Bonzini 		if (len)
75e39120abSPaolo Bonzini 			c->name[len++] = '+';
76e39120abSPaolo Bonzini 		strcpy(c->name + len, s->name);
77e39120abSPaolo Bonzini 		len += strlen(s->name);
7817da79e0SAndrew Jones 	}
79e39120abSPaolo Bonzini 	c->name[len] = '\0';
8017da79e0SAndrew Jones 
8117da79e0SAndrew Jones 	return c->name;
8217da79e0SAndrew Jones }
8317da79e0SAndrew Jones 
check_supported_reg(struct kvm_vcpu * vcpu,__u64 reg)8417da79e0SAndrew Jones bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)
8517da79e0SAndrew Jones {
8617da79e0SAndrew Jones 	return true;
8717da79e0SAndrew Jones }
8817da79e0SAndrew Jones 
filter_reg(__u64 reg)8917da79e0SAndrew Jones bool __weak filter_reg(__u64 reg)
9017da79e0SAndrew Jones {
9117da79e0SAndrew Jones 	return false;
9217da79e0SAndrew Jones }
9317da79e0SAndrew Jones 
find_reg(__u64 regs[],__u64 nr_regs,__u64 reg)9417da79e0SAndrew Jones static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)
9517da79e0SAndrew Jones {
9617da79e0SAndrew Jones 	int i;
9717da79e0SAndrew Jones 
9817da79e0SAndrew Jones 	for (i = 0; i < nr_regs; ++i)
9917da79e0SAndrew Jones 		if (reg == regs[i])
10017da79e0SAndrew Jones 			return true;
10117da79e0SAndrew Jones 	return false;
10217da79e0SAndrew Jones }
10317da79e0SAndrew Jones 
print_reg(const char * prefix,__u64 id)10417da79e0SAndrew Jones void __weak print_reg(const char *prefix, __u64 id)
10517da79e0SAndrew Jones {
10617da79e0SAndrew Jones 	printf("\t0x%llx,\n", id);
10717da79e0SAndrew Jones }
10817da79e0SAndrew Jones 
check_reject_set(int err)10990a6bcbcSHaibo Xu bool __weak check_reject_set(int err)
11090a6bcbcSHaibo Xu {
11190a6bcbcSHaibo Xu 	return true;
11290a6bcbcSHaibo Xu }
11390a6bcbcSHaibo Xu 
finalize_vcpu(struct kvm_vcpu * vcpu,struct vcpu_reg_list * c)114e8566033SHaibo Xu void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
115e8566033SHaibo Xu {
116e8566033SHaibo Xu }
117e8566033SHaibo Xu 
118be4c5806SAndrew Jones #ifdef __aarch64__
prepare_vcpu_init(struct vcpu_reg_list * c,struct kvm_vcpu_init * init)11917da79e0SAndrew Jones static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init)
12017da79e0SAndrew Jones {
12117da79e0SAndrew Jones 	struct vcpu_reg_sublist *s;
12217da79e0SAndrew Jones 
12317da79e0SAndrew Jones 	for_each_sublist(c, s)
12417da79e0SAndrew Jones 		if (s->capability)
12517da79e0SAndrew Jones 			init->features[s->feature / 32] |= 1 << (s->feature % 32);
12617da79e0SAndrew Jones }
12717da79e0SAndrew Jones 
vcpu_config_get_vcpu(struct vcpu_reg_list * c,struct kvm_vm * vm)128be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
129be4c5806SAndrew Jones {
130be4c5806SAndrew Jones 	struct kvm_vcpu_init init = { .target = -1, };
131be4c5806SAndrew Jones 	struct kvm_vcpu *vcpu;
132be4c5806SAndrew Jones 
133be4c5806SAndrew Jones 	prepare_vcpu_init(c, &init);
134be4c5806SAndrew Jones 	vcpu = __vm_vcpu_add(vm, 0);
135be4c5806SAndrew Jones 	aarch64_vcpu_setup(vcpu, &init);
136be4c5806SAndrew Jones 
137be4c5806SAndrew Jones 	return vcpu;
138be4c5806SAndrew Jones }
139be4c5806SAndrew Jones #else
vcpu_config_get_vcpu(struct vcpu_reg_list * c,struct kvm_vm * vm)140be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
141be4c5806SAndrew Jones {
142be4c5806SAndrew Jones 	return __vm_vcpu_add(vm, 0);
143be4c5806SAndrew Jones }
144be4c5806SAndrew Jones #endif
145be4c5806SAndrew Jones 
check_supported(struct vcpu_reg_list * c)14617da79e0SAndrew Jones static void check_supported(struct vcpu_reg_list *c)
14717da79e0SAndrew Jones {
14817da79e0SAndrew Jones 	struct vcpu_reg_sublist *s;
14917da79e0SAndrew Jones 
15017da79e0SAndrew Jones 	for_each_sublist(c, s) {
15117da79e0SAndrew Jones 		if (!s->capability)
15217da79e0SAndrew Jones 			continue;
15317da79e0SAndrew Jones 
15417da79e0SAndrew Jones 		__TEST_REQUIRE(kvm_has_cap(s->capability),
155*250e138dSAndrew Jones 			       "%s: %s not available, skipping tests",
15617da79e0SAndrew Jones 			       config_name(c), s->name);
15717da79e0SAndrew Jones 	}
15817da79e0SAndrew Jones }
15917da79e0SAndrew Jones 
16017da79e0SAndrew Jones static bool print_list;
16117da79e0SAndrew Jones static bool print_filtered;
16217da79e0SAndrew Jones 
run_test(struct vcpu_reg_list * c)16317da79e0SAndrew Jones static void run_test(struct vcpu_reg_list *c)
16417da79e0SAndrew Jones {
16517da79e0SAndrew Jones 	int new_regs = 0, missing_regs = 0, i, n;
16617da79e0SAndrew Jones 	int failed_get = 0, failed_set = 0, failed_reject = 0;
167cbc0daa6SHaibo Xu 	int skipped_set = 0;
16817da79e0SAndrew Jones 	struct kvm_vcpu *vcpu;
16917da79e0SAndrew Jones 	struct kvm_vm *vm;
17017da79e0SAndrew Jones 	struct vcpu_reg_sublist *s;
17117da79e0SAndrew Jones 
17217da79e0SAndrew Jones 	check_supported(c);
17317da79e0SAndrew Jones 
17417da79e0SAndrew Jones 	vm = vm_create_barebones();
175be4c5806SAndrew Jones 	vcpu = vcpu_config_get_vcpu(c, vm);
176e8566033SHaibo Xu 	finalize_vcpu(vcpu, c);
17717da79e0SAndrew Jones 
17817da79e0SAndrew Jones 	reg_list = vcpu_get_reg_list(vcpu);
17917da79e0SAndrew Jones 
18017da79e0SAndrew Jones 	if (print_list || print_filtered) {
18117da79e0SAndrew Jones 		putchar('\n');
18217da79e0SAndrew Jones 		for_each_reg(i) {
18317da79e0SAndrew Jones 			__u64 id = reg_list->reg[i];
18417da79e0SAndrew Jones 			if ((print_list && !filter_reg(id)) ||
18517da79e0SAndrew Jones 			    (print_filtered && filter_reg(id)))
18617da79e0SAndrew Jones 				print_reg(config_name(c), id);
18717da79e0SAndrew Jones 		}
18817da79e0SAndrew Jones 		putchar('\n');
18917da79e0SAndrew Jones 		return;
19017da79e0SAndrew Jones 	}
19117da79e0SAndrew Jones 
192c4746771SHaibo Xu 	for_each_sublist(c, s)
193c4746771SHaibo Xu 		blessed_n += s->regs_n;
194c4746771SHaibo Xu 	blessed_reg = calloc(blessed_n, sizeof(__u64));
195c4746771SHaibo Xu 
196c4746771SHaibo Xu 	n = 0;
197c4746771SHaibo Xu 	for_each_sublist(c, s) {
198c4746771SHaibo Xu 		for (i = 0; i < s->regs_n; ++i)
199c4746771SHaibo Xu 			blessed_reg[n++] = s->regs[i];
200c4746771SHaibo Xu 	}
201c4746771SHaibo Xu 
20217da79e0SAndrew Jones 	/*
20317da79e0SAndrew Jones 	 * We only test that we can get the register and then write back the
20417da79e0SAndrew Jones 	 * same value. Some registers may allow other values to be written
20517da79e0SAndrew Jones 	 * back, but others only allow some bits to be changed, and at least
20617da79e0SAndrew Jones 	 * for ID registers set will fail if the value does not exactly match
20717da79e0SAndrew Jones 	 * what was returned by get. If registers that allow other values to
20817da79e0SAndrew Jones 	 * be written need to have the other values tested, then we should
20917da79e0SAndrew Jones 	 * create a new set of tests for those in a new independent test
21017da79e0SAndrew Jones 	 * executable.
211c4746771SHaibo Xu 	 *
212c4746771SHaibo Xu 	 * Only do the get/set tests on present, blessed list registers,
213c4746771SHaibo Xu 	 * since we don't know the capabilities of any new registers.
21417da79e0SAndrew Jones 	 */
215c4746771SHaibo Xu 	for_each_present_blessed_reg(i) {
21617da79e0SAndrew Jones 		uint8_t addr[2048 / 8];
21717da79e0SAndrew Jones 		struct kvm_one_reg reg = {
21817da79e0SAndrew Jones 			.id = reg_list->reg[i],
21917da79e0SAndrew Jones 			.addr = (__u64)&addr,
22017da79e0SAndrew Jones 		};
221cbc0daa6SHaibo Xu 		bool reject_reg = false, skip_reg = false;
22217da79e0SAndrew Jones 		int ret;
22317da79e0SAndrew Jones 
22417da79e0SAndrew Jones 		ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);
22517da79e0SAndrew Jones 		if (ret) {
22617da79e0SAndrew Jones 			printf("%s: Failed to get ", config_name(c));
22717da79e0SAndrew Jones 			print_reg(config_name(c), reg.id);
22817da79e0SAndrew Jones 			putchar('\n');
22917da79e0SAndrew Jones 			++failed_get;
23017da79e0SAndrew Jones 		}
23117da79e0SAndrew Jones 
23217da79e0SAndrew Jones 		for_each_sublist(c, s) {
233cbc0daa6SHaibo Xu 			/* rejects_set registers are rejected for set operation */
23417da79e0SAndrew Jones 			if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {
23517da79e0SAndrew Jones 				reject_reg = true;
23617da79e0SAndrew Jones 				ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
23790a6bcbcSHaibo Xu 				if (ret != -1 || !check_reject_set(errno)) {
23817da79e0SAndrew Jones 					printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);
23917da79e0SAndrew Jones 					print_reg(config_name(c), reg.id);
24017da79e0SAndrew Jones 					putchar('\n');
24117da79e0SAndrew Jones 					++failed_reject;
24217da79e0SAndrew Jones 				}
24317da79e0SAndrew Jones 				break;
24417da79e0SAndrew Jones 			}
245cbc0daa6SHaibo Xu 
246cbc0daa6SHaibo Xu 			/* skips_set registers are skipped for set operation */
247cbc0daa6SHaibo Xu 			if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {
248cbc0daa6SHaibo Xu 				skip_reg = true;
249cbc0daa6SHaibo Xu 				++skipped_set;
250cbc0daa6SHaibo Xu 				break;
251cbc0daa6SHaibo Xu 			}
25217da79e0SAndrew Jones 		}
25317da79e0SAndrew Jones 
254cbc0daa6SHaibo Xu 		if (!reject_reg && !skip_reg) {
25517da79e0SAndrew Jones 			ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
25617da79e0SAndrew Jones 			if (ret) {
25717da79e0SAndrew Jones 				printf("%s: Failed to set ", config_name(c));
25817da79e0SAndrew Jones 				print_reg(config_name(c), reg.id);
25917da79e0SAndrew Jones 				putchar('\n');
26017da79e0SAndrew Jones 				++failed_set;
26117da79e0SAndrew Jones 			}
26217da79e0SAndrew Jones 		}
26317da79e0SAndrew Jones 	}
26417da79e0SAndrew Jones 
26517da79e0SAndrew Jones 	for_each_new_reg(i)
26617da79e0SAndrew Jones 		++new_regs;
26717da79e0SAndrew Jones 
26817da79e0SAndrew Jones 	for_each_missing_reg(i)
26917da79e0SAndrew Jones 		++missing_regs;
27017da79e0SAndrew Jones 
27117da79e0SAndrew Jones 	if (new_regs || missing_regs) {
27217da79e0SAndrew Jones 		n = 0;
27317da79e0SAndrew Jones 		for_each_reg_filtered(i)
27417da79e0SAndrew Jones 			++n;
27517da79e0SAndrew Jones 
27617da79e0SAndrew Jones 		printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);
27717da79e0SAndrew Jones 		printf("%s: Number registers:         %5lld (includes %lld filtered registers)\n",
27817da79e0SAndrew Jones 		       config_name(c), reg_list->n, reg_list->n - n);
27917da79e0SAndrew Jones 	}
28017da79e0SAndrew Jones 
28117da79e0SAndrew Jones 	if (new_regs) {
28217da79e0SAndrew Jones 		printf("\n%s: There are %d new registers.\n"
28317da79e0SAndrew Jones 		       "Consider adding them to the blessed reg "
28417da79e0SAndrew Jones 		       "list with the following lines:\n\n", config_name(c), new_regs);
28517da79e0SAndrew Jones 		for_each_new_reg(i)
28617da79e0SAndrew Jones 			print_reg(config_name(c), reg_list->reg[i]);
28717da79e0SAndrew Jones 		putchar('\n');
28817da79e0SAndrew Jones 	}
28917da79e0SAndrew Jones 
29017da79e0SAndrew Jones 	if (missing_regs) {
29117da79e0SAndrew Jones 		printf("\n%s: There are %d missing registers.\n"
29217da79e0SAndrew Jones 		       "The following lines are missing registers:\n\n", config_name(c), missing_regs);
29317da79e0SAndrew Jones 		for_each_missing_reg(i)
29417da79e0SAndrew Jones 			print_reg(config_name(c), blessed_reg[i]);
29517da79e0SAndrew Jones 		putchar('\n');
29617da79e0SAndrew Jones 	}
29717da79e0SAndrew Jones 
29817da79e0SAndrew Jones 	TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
299cbc0daa6SHaibo Xu 		    "%s: There are %d missing registers; %d registers failed get; "
300cbc0daa6SHaibo Xu 		    "%d registers failed set; %d registers failed reject; %d registers skipped set",
301cbc0daa6SHaibo Xu 		    config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);
30217da79e0SAndrew Jones 
30317da79e0SAndrew Jones 	pr_info("%s: PASS\n", config_name(c));
30417da79e0SAndrew Jones 	blessed_n = 0;
30517da79e0SAndrew Jones 	free(blessed_reg);
30617da79e0SAndrew Jones 	free(reg_list);
30717da79e0SAndrew Jones 	kvm_vm_free(vm);
30817da79e0SAndrew Jones }
30917da79e0SAndrew Jones 
help(void)31017da79e0SAndrew Jones static void help(void)
31117da79e0SAndrew Jones {
31217da79e0SAndrew Jones 	struct vcpu_reg_list *c;
31317da79e0SAndrew Jones 	int i;
31417da79e0SAndrew Jones 
31517da79e0SAndrew Jones 	printf(
31617da79e0SAndrew Jones 	"\n"
31717da79e0SAndrew Jones 	"usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"
31817da79e0SAndrew Jones 	" --config=<selection>        Used to select a specific vcpu configuration for the test/listing\n"
31917da79e0SAndrew Jones 	"                             '<selection>' may be\n");
32017da79e0SAndrew Jones 
32117da79e0SAndrew Jones 	for (i = 0; i < vcpu_configs_n; ++i) {
32217da79e0SAndrew Jones 		c = vcpu_configs[i];
32317da79e0SAndrew Jones 		printf(
32417da79e0SAndrew Jones 	"                               '%s'\n", config_name(c));
32517da79e0SAndrew Jones 	}
32617da79e0SAndrew Jones 
32717da79e0SAndrew Jones 	printf(
32817da79e0SAndrew Jones 	"\n"
32917da79e0SAndrew Jones 	" --list                      Print the register list rather than test it (requires --config)\n"
33017da79e0SAndrew Jones 	" --list-filtered             Print registers that would normally be filtered out (requires --config)\n"
33117da79e0SAndrew Jones 	"\n"
33217da79e0SAndrew Jones 	);
33317da79e0SAndrew Jones }
33417da79e0SAndrew Jones 
parse_config(const char * config)33517da79e0SAndrew Jones static struct vcpu_reg_list *parse_config(const char *config)
33617da79e0SAndrew Jones {
33717da79e0SAndrew Jones 	struct vcpu_reg_list *c = NULL;
33817da79e0SAndrew Jones 	int i;
33917da79e0SAndrew Jones 
34017da79e0SAndrew Jones 	if (config[8] != '=')
34117da79e0SAndrew Jones 		help(), exit(1);
34217da79e0SAndrew Jones 
34317da79e0SAndrew Jones 	for (i = 0; i < vcpu_configs_n; ++i) {
34417da79e0SAndrew Jones 		c = vcpu_configs[i];
34517da79e0SAndrew Jones 		if (strcmp(config_name(c), &config[9]) == 0)
34617da79e0SAndrew Jones 			break;
34717da79e0SAndrew Jones 	}
34817da79e0SAndrew Jones 
34917da79e0SAndrew Jones 	if (i == vcpu_configs_n)
35017da79e0SAndrew Jones 		help(), exit(1);
35117da79e0SAndrew Jones 
35217da79e0SAndrew Jones 	return c;
35317da79e0SAndrew Jones }
35417da79e0SAndrew Jones 
main(int ac,char ** av)35517da79e0SAndrew Jones int main(int ac, char **av)
35617da79e0SAndrew Jones {
35717da79e0SAndrew Jones 	struct vcpu_reg_list *c, *sel = NULL;
35817da79e0SAndrew Jones 	int i, ret = 0;
35917da79e0SAndrew Jones 	pid_t pid;
36017da79e0SAndrew Jones 
36117da79e0SAndrew Jones 	for (i = 1; i < ac; ++i) {
36217da79e0SAndrew Jones 		if (strncmp(av[i], "--config", 8) == 0)
36317da79e0SAndrew Jones 			sel = parse_config(av[i]);
36417da79e0SAndrew Jones 		else if (strcmp(av[i], "--list") == 0)
36517da79e0SAndrew Jones 			print_list = true;
36617da79e0SAndrew Jones 		else if (strcmp(av[i], "--list-filtered") == 0)
36717da79e0SAndrew Jones 			print_filtered = true;
36817da79e0SAndrew Jones 		else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)
36917da79e0SAndrew Jones 			help(), exit(0);
37017da79e0SAndrew Jones 		else
37117da79e0SAndrew Jones 			help(), exit(1);
37217da79e0SAndrew Jones 	}
37317da79e0SAndrew Jones 
37417da79e0SAndrew Jones 	if (print_list || print_filtered) {
37517da79e0SAndrew Jones 		/*
37617da79e0SAndrew Jones 		 * We only want to print the register list of a single config.
37717da79e0SAndrew Jones 		 */
37817da79e0SAndrew Jones 		if (!sel)
37917da79e0SAndrew Jones 			help(), exit(1);
38017da79e0SAndrew Jones 	}
38117da79e0SAndrew Jones 
38217da79e0SAndrew Jones 	for (i = 0; i < vcpu_configs_n; ++i) {
38317da79e0SAndrew Jones 		c = vcpu_configs[i];
38417da79e0SAndrew Jones 		if (sel && c != sel)
38517da79e0SAndrew Jones 			continue;
38617da79e0SAndrew Jones 
38717da79e0SAndrew Jones 		pid = fork();
38817da79e0SAndrew Jones 
38917da79e0SAndrew Jones 		if (!pid) {
39017da79e0SAndrew Jones 			run_test(c);
39117da79e0SAndrew Jones 			exit(0);
39217da79e0SAndrew Jones 		} else {
39317da79e0SAndrew Jones 			int wstatus;
39417da79e0SAndrew Jones 			pid_t wpid = wait(&wstatus);
39517da79e0SAndrew Jones 			TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");
39617da79e0SAndrew Jones 			if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)
39717da79e0SAndrew Jones 				ret = KSFT_FAIL;
39817da79e0SAndrew Jones 		}
39917da79e0SAndrew Jones 	}
40017da79e0SAndrew Jones 
40117da79e0SAndrew Jones 	return ret;
40217da79e0SAndrew Jones }
403