brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 91f05f7 Raw
403 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 host8 * with a newer kernel we allow the newer kernel on the destination to9 * list new registers with get-reg-list. We assume they'll be unused, at10 * least until the guest reboots, and so they're relatively harmless.11 * However, if the destination host with the newer kernel is missing12 * registers which the source host with the older kernel has, then that's13 * a regression in get-reg-list. This test checks for that regression by14 * checking the current list against a blessed list. We should never have15 * missing registers, but if new ones appear then they can probably be16 * added to the blessed list. A completely new blessed list can be created17 * 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 vcpu_reg_list *c, struct kvm_vcpu_init *init)120{121	struct vcpu_reg_sublist *s;122 123	for_each_sublist(c, s)124		if (s->capability)125			init->features[s->feature / 32] |= 1 << (s->feature % 32);126}127 128static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)129{130	struct kvm_vcpu_init init = { .target = -1, };131	struct kvm_vcpu *vcpu;132 133	prepare_vcpu_init(c, &init);134	vcpu = __vm_vcpu_add(vm, 0);135	aarch64_vcpu_setup(vcpu, &init);136 137	return vcpu;138}139#else140static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)141{142	return __vm_vcpu_add(vm, 0);143}144#endif145 146static void check_supported(struct vcpu_reg_list *c)147{148	struct vcpu_reg_sublist *s;149 150	for_each_sublist(c, s) {151		if (!s->capability)152			continue;153 154		__TEST_REQUIRE(kvm_has_cap(s->capability),155			       "%s: %s not available, skipping tests",156			       config_name(c), s->name);157	}158}159 160static bool print_list;161static bool print_filtered;162 163static void run_test(struct vcpu_reg_list *c)164{165	int new_regs = 0, missing_regs = 0, i, n;166	int failed_get = 0, failed_set = 0, failed_reject = 0;167	int skipped_set = 0;168	struct kvm_vcpu *vcpu;169	struct kvm_vm *vm;170	struct vcpu_reg_sublist *s;171 172	check_supported(c);173 174	vm = vm_create_barebones();175	vcpu = vcpu_config_get_vcpu(c, vm);176	finalize_vcpu(vcpu, c);177 178	reg_list = vcpu_get_reg_list(vcpu);179 180	if (print_list || print_filtered) {181		putchar('\n');182		for_each_reg(i) {183			__u64 id = reg_list->reg[i];184			if ((print_list && !filter_reg(id)) ||185			    (print_filtered && filter_reg(id)))186				print_reg(config_name(c), id);187		}188		putchar('\n');189		return;190	}191 192	for_each_sublist(c, s)193		blessed_n += s->regs_n;194	blessed_reg = calloc(blessed_n, sizeof(__u64));195 196	n = 0;197	for_each_sublist(c, s) {198		for (i = 0; i < s->regs_n; ++i)199			blessed_reg[n++] = s->regs[i];200	}201 202	/*203	 * We only test that we can get the register and then write back the204	 * same value. Some registers may allow other values to be written205	 * back, but others only allow some bits to be changed, and at least206	 * for ID registers set will fail if the value does not exactly match207	 * what was returned by get. If registers that allow other values to208	 * be written need to have the other values tested, then we should209	 * create a new set of tests for those in a new independent test210	 * executable.211	 *212	 * Only do the get/set tests on present, blessed list registers,213	 * since we don't know the capabilities of any new registers.214	 */215	for_each_present_blessed_reg(i) {216		uint8_t addr[2048 / 8];217		struct kvm_one_reg reg = {218			.id = reg_list->reg[i],219			.addr = (__u64)&addr,220		};221		bool reject_reg = false, skip_reg = false;222		int ret;223 224		ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);225		if (ret) {226			printf("%s: Failed to get ", config_name(c));227			print_reg(config_name(c), reg.id);228			putchar('\n');229			++failed_get;230		}231 232		for_each_sublist(c, s) {233			/* rejects_set registers are rejected for set operation */234			if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {235				reject_reg = true;236				ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);237				if (ret != -1 || !check_reject_set(errno)) {238					printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);239					print_reg(config_name(c), reg.id);240					putchar('\n');241					++failed_reject;242				}243				break;244			}245 246			/* skips_set registers are skipped for set operation */247			if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {248				skip_reg = true;249				++skipped_set;250				break;251			}252		}253 254		if (!reject_reg && !skip_reg) {255			ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);256			if (ret) {257				printf("%s: Failed to set ", config_name(c));258				print_reg(config_name(c), reg.id);259				putchar('\n');260				++failed_set;261			}262		}263	}264 265	for_each_new_reg(i)266		++new_regs;267 268	for_each_missing_reg(i)269		++missing_regs;270 271	if (new_regs || missing_regs) {272		n = 0;273		for_each_reg_filtered(i)274			++n;275 276		printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);277		printf("%s: Number registers:         %5lld (includes %lld filtered registers)\n",278		       config_name(c), reg_list->n, reg_list->n - n);279	}280 281	if (new_regs) {282		printf("\n%s: There are %d new registers.\n"283		       "Consider adding them to the blessed reg "284		       "list with the following lines:\n\n", config_name(c), new_regs);285		for_each_new_reg(i)286			print_reg(config_name(c), reg_list->reg[i]);287		putchar('\n');288	}289 290	if (missing_regs) {291		printf("\n%s: There are %d missing registers.\n"292		       "The following lines are missing registers:\n\n", config_name(c), missing_regs);293		for_each_missing_reg(i)294			print_reg(config_name(c), blessed_reg[i]);295		putchar('\n');296	}297 298	TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,299		    "%s: There are %d missing registers; %d registers failed get; "300		    "%d registers failed set; %d registers failed reject; %d registers skipped set",301		    config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);302 303	pr_info("%s: PASS\n", config_name(c));304	blessed_n = 0;305	free(blessed_reg);306	free(reg_list);307	kvm_vm_free(vm);308}309 310static void help(void)311{312	struct vcpu_reg_list *c;313	int i;314 315	printf(316	"\n"317	"usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"318	" --config=<selection>        Used to select a specific vcpu configuration for the test/listing\n"319	"                             '<selection>' may be\n");320 321	for (i = 0; i < vcpu_configs_n; ++i) {322		c = vcpu_configs[i];323		printf(324	"                               '%s'\n", config_name(c));325	}326 327	printf(328	"\n"329	" --list                      Print the register list rather than test it (requires --config)\n"330	" --list-filtered             Print registers that would normally be filtered out (requires --config)\n"331	"\n"332	);333}334 335static struct vcpu_reg_list *parse_config(const char *config)336{337	struct vcpu_reg_list *c = NULL;338	int i;339 340	if (config[8] != '=')341		help(), exit(1);342 343	for (i = 0; i < vcpu_configs_n; ++i) {344		c = vcpu_configs[i];345		if (strcmp(config_name(c), &config[9]) == 0)346			break;347	}348 349	if (i == vcpu_configs_n)350		help(), exit(1);351 352	return c;353}354 355int main(int ac, char **av)356{357	struct vcpu_reg_list *c, *sel = NULL;358	int i, ret = 0;359	pid_t pid;360 361	for (i = 1; i < ac; ++i) {362		if (strncmp(av[i], "--config", 8) == 0)363			sel = parse_config(av[i]);364		else if (strcmp(av[i], "--list") == 0)365			print_list = true;366		else if (strcmp(av[i], "--list-filtered") == 0)367			print_filtered = true;368		else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)369			help(), exit(0);370		else371			help(), exit(1);372	}373 374	if (print_list || print_filtered) {375		/*376		 * We only want to print the register list of a single config.377		 */378		if (!sel)379			help(), exit(1);380	}381 382	for (i = 0; i < vcpu_configs_n; ++i) {383		c = vcpu_configs[i];384		if (sel && c != sel)385			continue;386 387		pid = fork();388 389		if (!pid) {390			run_test(c);391			exit(0);392		} else {393			int wstatus;394			pid_t wpid = wait(&wstatus);395			TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");396			if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)397				ret = KSFT_FAIL;398		}399	}400 401	return ret;402}403