162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Check for KVM_GET_REG_LIST regressions.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020, Red Hat, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * When attempting to migrate from a host with an older kernel to a host
862306a36Sopenharmony_ci * with a newer kernel we allow the newer kernel on the destination to
962306a36Sopenharmony_ci * list new registers with get-reg-list. We assume they'll be unused, at
1062306a36Sopenharmony_ci * least until the guest reboots, and so they're relatively harmless.
1162306a36Sopenharmony_ci * However, if the destination host with the newer kernel is missing
1262306a36Sopenharmony_ci * registers which the source host with the older kernel has, then that's
1362306a36Sopenharmony_ci * a regression in get-reg-list. This test checks for that regression by
1462306a36Sopenharmony_ci * checking the current list against a blessed list. We should never have
1562306a36Sopenharmony_ci * missing registers, but if new ones appear then they can probably be
1662306a36Sopenharmony_ci * added to the blessed list. A completely new blessed list can be created
1762306a36Sopenharmony_ci * by running the test with the --list command line argument.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * The blessed list should be created from the oldest possible kernel.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#include <stdio.h>
2262306a36Sopenharmony_ci#include <stdlib.h>
2362306a36Sopenharmony_ci#include <string.h>
2462306a36Sopenharmony_ci#include <unistd.h>
2562306a36Sopenharmony_ci#include <sys/types.h>
2662306a36Sopenharmony_ci#include <sys/wait.h>
2762306a36Sopenharmony_ci#include "kvm_util.h"
2862306a36Sopenharmony_ci#include "test_util.h"
2962306a36Sopenharmony_ci#include "processor.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic struct kvm_reg_list *reg_list;
3262306a36Sopenharmony_cistatic __u64 *blessed_reg, blessed_n;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciextern struct vcpu_reg_list *vcpu_configs[];
3562306a36Sopenharmony_ciextern int vcpu_configs_n;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define for_each_reg(i)								\
3862306a36Sopenharmony_ci	for ((i) = 0; (i) < reg_list->n; ++(i))
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define for_each_reg_filtered(i)						\
4162306a36Sopenharmony_ci	for_each_reg(i)								\
4262306a36Sopenharmony_ci		if (!filter_reg(reg_list->reg[i]))
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define for_each_missing_reg(i)							\
4562306a36Sopenharmony_ci	for ((i) = 0; (i) < blessed_n; ++(i))					\
4662306a36Sopenharmony_ci		if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i]))	\
4762306a36Sopenharmony_ci			if (check_supported_reg(vcpu, blessed_reg[i]))
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define for_each_new_reg(i)							\
5062306a36Sopenharmony_ci	for_each_reg_filtered(i)						\
5162306a36Sopenharmony_ci		if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define for_each_present_blessed_reg(i)						\
5462306a36Sopenharmony_ci	for_each_reg(i)								\
5562306a36Sopenharmony_ci		if (find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const char *config_name(struct vcpu_reg_list *c)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct vcpu_reg_sublist *s;
6062306a36Sopenharmony_ci	int len = 0;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (c->name)
6362306a36Sopenharmony_ci		return c->name;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	for_each_sublist(c, s)
6662306a36Sopenharmony_ci		len += strlen(s->name) + 1;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	c->name = malloc(len);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	len = 0;
7162306a36Sopenharmony_ci	for_each_sublist(c, s) {
7262306a36Sopenharmony_ci		if (!strcmp(s->name, "base"))
7362306a36Sopenharmony_ci			continue;
7462306a36Sopenharmony_ci		strcat(c->name + len, s->name);
7562306a36Sopenharmony_ci		len += strlen(s->name) + 1;
7662306a36Sopenharmony_ci		c->name[len - 1] = '+';
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	c->name[len - 1] = '\0';
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return c->name;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cibool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return true;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cibool __weak filter_reg(__u64 reg)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return false;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int i;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for (i = 0; i < nr_regs; ++i)
9862306a36Sopenharmony_ci		if (reg == regs[i])
9962306a36Sopenharmony_ci			return true;
10062306a36Sopenharmony_ci	return false;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid __weak print_reg(const char *prefix, __u64 id)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	printf("\t0x%llx,\n", id);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cibool __weak check_reject_set(int err)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	return true;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_civoid __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#ifdef __aarch64__
11862306a36Sopenharmony_cistatic void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct vcpu_reg_sublist *s;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for_each_sublist(c, s)
12362306a36Sopenharmony_ci		if (s->capability)
12462306a36Sopenharmony_ci			init->features[s->feature / 32] |= 1 << (s->feature % 32);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct kvm_vcpu_init init = { .target = -1, };
13062306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	prepare_vcpu_init(c, &init);
13362306a36Sopenharmony_ci	vcpu = __vm_vcpu_add(vm, 0);
13462306a36Sopenharmony_ci	aarch64_vcpu_setup(vcpu, &init);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return vcpu;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci#else
13962306a36Sopenharmony_cistatic struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return __vm_vcpu_add(vm, 0);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci#endif
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void check_supported(struct vcpu_reg_list *c)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct vcpu_reg_sublist *s;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	for_each_sublist(c, s) {
15062306a36Sopenharmony_ci		if (!s->capability)
15162306a36Sopenharmony_ci			continue;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		__TEST_REQUIRE(kvm_has_cap(s->capability),
15462306a36Sopenharmony_ci			       "%s: %s not available, skipping tests\n",
15562306a36Sopenharmony_ci			       config_name(c), s->name);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic bool print_list;
16062306a36Sopenharmony_cistatic bool print_filtered;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void run_test(struct vcpu_reg_list *c)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int new_regs = 0, missing_regs = 0, i, n;
16562306a36Sopenharmony_ci	int failed_get = 0, failed_set = 0, failed_reject = 0;
16662306a36Sopenharmony_ci	int skipped_set = 0;
16762306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
16862306a36Sopenharmony_ci	struct kvm_vm *vm;
16962306a36Sopenharmony_ci	struct vcpu_reg_sublist *s;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	check_supported(c);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	vm = vm_create_barebones();
17462306a36Sopenharmony_ci	vcpu = vcpu_config_get_vcpu(c, vm);
17562306a36Sopenharmony_ci	finalize_vcpu(vcpu, c);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	reg_list = vcpu_get_reg_list(vcpu);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (print_list || print_filtered) {
18062306a36Sopenharmony_ci		putchar('\n');
18162306a36Sopenharmony_ci		for_each_reg(i) {
18262306a36Sopenharmony_ci			__u64 id = reg_list->reg[i];
18362306a36Sopenharmony_ci			if ((print_list && !filter_reg(id)) ||
18462306a36Sopenharmony_ci			    (print_filtered && filter_reg(id)))
18562306a36Sopenharmony_ci				print_reg(config_name(c), id);
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci		putchar('\n');
18862306a36Sopenharmony_ci		return;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for_each_sublist(c, s)
19262306a36Sopenharmony_ci		blessed_n += s->regs_n;
19362306a36Sopenharmony_ci	blessed_reg = calloc(blessed_n, sizeof(__u64));
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	n = 0;
19662306a36Sopenharmony_ci	for_each_sublist(c, s) {
19762306a36Sopenharmony_ci		for (i = 0; i < s->regs_n; ++i)
19862306a36Sopenharmony_ci			blessed_reg[n++] = s->regs[i];
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * We only test that we can get the register and then write back the
20362306a36Sopenharmony_ci	 * same value. Some registers may allow other values to be written
20462306a36Sopenharmony_ci	 * back, but others only allow some bits to be changed, and at least
20562306a36Sopenharmony_ci	 * for ID registers set will fail if the value does not exactly match
20662306a36Sopenharmony_ci	 * what was returned by get. If registers that allow other values to
20762306a36Sopenharmony_ci	 * be written need to have the other values tested, then we should
20862306a36Sopenharmony_ci	 * create a new set of tests for those in a new independent test
20962306a36Sopenharmony_ci	 * executable.
21062306a36Sopenharmony_ci	 *
21162306a36Sopenharmony_ci	 * Only do the get/set tests on present, blessed list registers,
21262306a36Sopenharmony_ci	 * since we don't know the capabilities of any new registers.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	for_each_present_blessed_reg(i) {
21562306a36Sopenharmony_ci		uint8_t addr[2048 / 8];
21662306a36Sopenharmony_ci		struct kvm_one_reg reg = {
21762306a36Sopenharmony_ci			.id = reg_list->reg[i],
21862306a36Sopenharmony_ci			.addr = (__u64)&addr,
21962306a36Sopenharmony_ci		};
22062306a36Sopenharmony_ci		bool reject_reg = false, skip_reg = false;
22162306a36Sopenharmony_ci		int ret;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);
22462306a36Sopenharmony_ci		if (ret) {
22562306a36Sopenharmony_ci			printf("%s: Failed to get ", config_name(c));
22662306a36Sopenharmony_ci			print_reg(config_name(c), reg.id);
22762306a36Sopenharmony_ci			putchar('\n');
22862306a36Sopenharmony_ci			++failed_get;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		for_each_sublist(c, s) {
23262306a36Sopenharmony_ci			/* rejects_set registers are rejected for set operation */
23362306a36Sopenharmony_ci			if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {
23462306a36Sopenharmony_ci				reject_reg = true;
23562306a36Sopenharmony_ci				ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
23662306a36Sopenharmony_ci				if (ret != -1 || !check_reject_set(errno)) {
23762306a36Sopenharmony_ci					printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);
23862306a36Sopenharmony_ci					print_reg(config_name(c), reg.id);
23962306a36Sopenharmony_ci					putchar('\n');
24062306a36Sopenharmony_ci					++failed_reject;
24162306a36Sopenharmony_ci				}
24262306a36Sopenharmony_ci				break;
24362306a36Sopenharmony_ci			}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci			/* skips_set registers are skipped for set operation */
24662306a36Sopenharmony_ci			if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {
24762306a36Sopenharmony_ci				skip_reg = true;
24862306a36Sopenharmony_ci				++skipped_set;
24962306a36Sopenharmony_ci				break;
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (!reject_reg && !skip_reg) {
25462306a36Sopenharmony_ci			ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
25562306a36Sopenharmony_ci			if (ret) {
25662306a36Sopenharmony_ci				printf("%s: Failed to set ", config_name(c));
25762306a36Sopenharmony_ci				print_reg(config_name(c), reg.id);
25862306a36Sopenharmony_ci				putchar('\n');
25962306a36Sopenharmony_ci				++failed_set;
26062306a36Sopenharmony_ci			}
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for_each_new_reg(i)
26562306a36Sopenharmony_ci		++new_regs;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for_each_missing_reg(i)
26862306a36Sopenharmony_ci		++missing_regs;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (new_regs || missing_regs) {
27162306a36Sopenharmony_ci		n = 0;
27262306a36Sopenharmony_ci		for_each_reg_filtered(i)
27362306a36Sopenharmony_ci			++n;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);
27662306a36Sopenharmony_ci		printf("%s: Number registers:         %5lld (includes %lld filtered registers)\n",
27762306a36Sopenharmony_ci		       config_name(c), reg_list->n, reg_list->n - n);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (new_regs) {
28162306a36Sopenharmony_ci		printf("\n%s: There are %d new registers.\n"
28262306a36Sopenharmony_ci		       "Consider adding them to the blessed reg "
28362306a36Sopenharmony_ci		       "list with the following lines:\n\n", config_name(c), new_regs);
28462306a36Sopenharmony_ci		for_each_new_reg(i)
28562306a36Sopenharmony_ci			print_reg(config_name(c), reg_list->reg[i]);
28662306a36Sopenharmony_ci		putchar('\n');
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (missing_regs) {
29062306a36Sopenharmony_ci		printf("\n%s: There are %d missing registers.\n"
29162306a36Sopenharmony_ci		       "The following lines are missing registers:\n\n", config_name(c), missing_regs);
29262306a36Sopenharmony_ci		for_each_missing_reg(i)
29362306a36Sopenharmony_ci			print_reg(config_name(c), blessed_reg[i]);
29462306a36Sopenharmony_ci		putchar('\n');
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
29862306a36Sopenharmony_ci		    "%s: There are %d missing registers; %d registers failed get; "
29962306a36Sopenharmony_ci		    "%d registers failed set; %d registers failed reject; %d registers skipped set",
30062306a36Sopenharmony_ci		    config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	pr_info("%s: PASS\n", config_name(c));
30362306a36Sopenharmony_ci	blessed_n = 0;
30462306a36Sopenharmony_ci	free(blessed_reg);
30562306a36Sopenharmony_ci	free(reg_list);
30662306a36Sopenharmony_ci	kvm_vm_free(vm);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void help(void)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct vcpu_reg_list *c;
31262306a36Sopenharmony_ci	int i;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	printf(
31562306a36Sopenharmony_ci	"\n"
31662306a36Sopenharmony_ci	"usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"
31762306a36Sopenharmony_ci	" --config=<selection>        Used to select a specific vcpu configuration for the test/listing\n"
31862306a36Sopenharmony_ci	"                             '<selection>' may be\n");
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	for (i = 0; i < vcpu_configs_n; ++i) {
32162306a36Sopenharmony_ci		c = vcpu_configs[i];
32262306a36Sopenharmony_ci		printf(
32362306a36Sopenharmony_ci	"                               '%s'\n", config_name(c));
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	printf(
32762306a36Sopenharmony_ci	"\n"
32862306a36Sopenharmony_ci	" --list                      Print the register list rather than test it (requires --config)\n"
32962306a36Sopenharmony_ci	" --list-filtered             Print registers that would normally be filtered out (requires --config)\n"
33062306a36Sopenharmony_ci	"\n"
33162306a36Sopenharmony_ci	);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic struct vcpu_reg_list *parse_config(const char *config)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct vcpu_reg_list *c = NULL;
33762306a36Sopenharmony_ci	int i;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (config[8] != '=')
34062306a36Sopenharmony_ci		help(), exit(1);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	for (i = 0; i < vcpu_configs_n; ++i) {
34362306a36Sopenharmony_ci		c = vcpu_configs[i];
34462306a36Sopenharmony_ci		if (strcmp(config_name(c), &config[9]) == 0)
34562306a36Sopenharmony_ci			break;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (i == vcpu_configs_n)
34962306a36Sopenharmony_ci		help(), exit(1);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return c;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ciint main(int ac, char **av)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct vcpu_reg_list *c, *sel = NULL;
35762306a36Sopenharmony_ci	int i, ret = 0;
35862306a36Sopenharmony_ci	pid_t pid;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	for (i = 1; i < ac; ++i) {
36162306a36Sopenharmony_ci		if (strncmp(av[i], "--config", 8) == 0)
36262306a36Sopenharmony_ci			sel = parse_config(av[i]);
36362306a36Sopenharmony_ci		else if (strcmp(av[i], "--list") == 0)
36462306a36Sopenharmony_ci			print_list = true;
36562306a36Sopenharmony_ci		else if (strcmp(av[i], "--list-filtered") == 0)
36662306a36Sopenharmony_ci			print_filtered = true;
36762306a36Sopenharmony_ci		else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)
36862306a36Sopenharmony_ci			help(), exit(0);
36962306a36Sopenharmony_ci		else
37062306a36Sopenharmony_ci			help(), exit(1);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (print_list || print_filtered) {
37462306a36Sopenharmony_ci		/*
37562306a36Sopenharmony_ci		 * We only want to print the register list of a single config.
37662306a36Sopenharmony_ci		 */
37762306a36Sopenharmony_ci		if (!sel)
37862306a36Sopenharmony_ci			help(), exit(1);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	for (i = 0; i < vcpu_configs_n; ++i) {
38262306a36Sopenharmony_ci		c = vcpu_configs[i];
38362306a36Sopenharmony_ci		if (sel && c != sel)
38462306a36Sopenharmony_ci			continue;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		pid = fork();
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (!pid) {
38962306a36Sopenharmony_ci			run_test(c);
39062306a36Sopenharmony_ci			exit(0);
39162306a36Sopenharmony_ci		} else {
39262306a36Sopenharmony_ci			int wstatus;
39362306a36Sopenharmony_ci			pid_t wpid = wait(&wstatus);
39462306a36Sopenharmony_ci			TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");
39562306a36Sopenharmony_ci			if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)
39662306a36Sopenharmony_ci				ret = KSFT_FAIL;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return ret;
40162306a36Sopenharmony_ci}
402