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