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