Path: blob/master/tools/testing/selftests/kvm/get-reg-list.c
38189 views
// SPDX-License-Identifier: GPL-2.01/*2* Check for KVM_GET_REG_LIST regressions.3*4* Copyright (C) 2020, Red Hat, Inc.5*6* When attempting to migrate from a host with an older kernel to a host7* with a newer kernel we allow the newer kernel on the destination to8* list new registers with get-reg-list. We assume they'll be unused, at9* least until the guest reboots, and so they're relatively harmless.10* However, if the destination host with the newer kernel is missing11* registers which the source host with the older kernel has, then that's12* a regression in get-reg-list. This test checks for that regression by13* checking the current list against a blessed list. We should never have14* missing registers, but if new ones appear then they can probably be15* added to the blessed list. A completely new blessed list can be created16* by running the test with the --list command line argument.17*18* The blessed list should be created from the oldest possible kernel.19*/20#include <stdio.h>21#include <stdlib.h>22#include <string.h>23#include <unistd.h>24#include <sys/types.h>25#include <sys/wait.h>26#include "kvm_util.h"27#include "test_util.h"28#include "processor.h"2930static struct kvm_reg_list *reg_list;31static __u64 *blessed_reg, blessed_n;3233extern struct vcpu_reg_list *vcpu_configs[];34extern int vcpu_configs_n;3536#define for_each_reg(i) \37for ((i) = 0; (i) < reg_list->n; ++(i))3839#define for_each_reg_filtered(i) \40for_each_reg(i) \41if (!filter_reg(reg_list->reg[i]))4243#define for_each_missing_reg(i) \44for ((i) = 0; (i) < blessed_n; ++(i)) \45if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \46if (check_supported_reg(vcpu, blessed_reg[i]))4748#define for_each_new_reg(i) \49for_each_reg_filtered(i) \50if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))5152#define for_each_present_blessed_reg(i) \53for_each_reg(i) \54if (find_reg(blessed_reg, blessed_n, reg_list->reg[i]))5556static const char *config_name(struct vcpu_reg_list *c)57{58struct vcpu_reg_sublist *s;59int len = 0;6061if (c->name)62return c->name;6364for_each_sublist(c, s)65len += strlen(s->name) + 1;6667c->name = malloc(len);6869len = 0;70for_each_sublist(c, s) {71if (!strcmp(s->name, "base"))72continue;73if (len)74c->name[len++] = '+';75strcpy(c->name + len, s->name);76len += strlen(s->name);77}78c->name[len] = '\0';7980return c->name;81}8283bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)84{85return true;86}8788bool __weak filter_reg(__u64 reg)89{90return false;91}9293static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)94{95int i;9697for (i = 0; i < nr_regs; ++i)98if (reg == regs[i])99return true;100return false;101}102103void __weak print_reg(const char *prefix, __u64 id)104{105printf("\t0x%llx,\n", id);106}107108bool __weak check_reject_set(int err)109{110return true;111}112113void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)114{115}116117#ifdef __aarch64__118static void prepare_vcpu_init(struct kvm_vm *vm, struct vcpu_reg_list *c,119struct kvm_vcpu_init *init)120{121struct vcpu_reg_sublist *s;122123vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, init);124125for_each_sublist(c, s)126if (s->capability)127init->features[s->feature / 32] |= 1 << (s->feature % 32);128}129130static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)131{132struct kvm_vcpu_init init;133struct kvm_vcpu *vcpu;134135prepare_vcpu_init(vm, c, &init);136vcpu = __vm_vcpu_add(vm, 0);137aarch64_vcpu_setup(vcpu, &init);138139return vcpu;140}141#else142static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)143{144return __vm_vcpu_add(vm, 0);145}146#endif147148static void check_supported(struct vcpu_reg_list *c)149{150struct vcpu_reg_sublist *s;151152for_each_sublist(c, s) {153if (!s->capability)154continue;155156__TEST_REQUIRE(kvm_has_cap(s->capability),157"%s: %s not available, skipping tests",158config_name(c), s->name);159}160}161162static bool print_list;163static bool print_filtered;164165static void run_test(struct vcpu_reg_list *c)166{167int new_regs = 0, missing_regs = 0, i, n;168int failed_get = 0, failed_set = 0, failed_reject = 0;169int skipped_set = 0;170struct kvm_vcpu *vcpu;171struct kvm_vm *vm;172struct vcpu_reg_sublist *s;173174check_supported(c);175176vm = vm_create_barebones();177vcpu = vcpu_config_get_vcpu(c, vm);178finalize_vcpu(vcpu, c);179180reg_list = vcpu_get_reg_list(vcpu);181182if (print_list || print_filtered) {183putchar('\n');184for_each_reg(i) {185__u64 id = reg_list->reg[i];186if ((print_list && !filter_reg(id)) ||187(print_filtered && filter_reg(id)))188print_reg(config_name(c), id);189}190putchar('\n');191return;192}193194for_each_sublist(c, s)195blessed_n += s->regs_n;196blessed_reg = calloc(blessed_n, sizeof(__u64));197198n = 0;199for_each_sublist(c, s) {200for (i = 0; i < s->regs_n; ++i)201blessed_reg[n++] = s->regs[i];202}203204/*205* We only test that we can get the register and then write back the206* same value. Some registers may allow other values to be written207* back, but others only allow some bits to be changed, and at least208* for ID registers set will fail if the value does not exactly match209* what was returned by get. If registers that allow other values to210* be written need to have the other values tested, then we should211* create a new set of tests for those in a new independent test212* executable.213*214* Only do the get/set tests on present, blessed list registers,215* since we don't know the capabilities of any new registers.216*/217for_each_present_blessed_reg(i) {218uint8_t addr[2048 / 8];219struct kvm_one_reg reg = {220.id = reg_list->reg[i],221.addr = (__u64)&addr,222};223bool reject_reg = false, skip_reg = false;224int ret;225226ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);227if (ret) {228printf("%s: Failed to get ", config_name(c));229print_reg(config_name(c), reg.id);230putchar('\n');231++failed_get;232}233234for_each_sublist(c, s) {235/* rejects_set registers are rejected for set operation */236if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {237reject_reg = true;238ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®);239if (ret != -1 || !check_reject_set(errno)) {240printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);241print_reg(config_name(c), reg.id);242putchar('\n');243++failed_reject;244}245break;246}247248/* skips_set registers are skipped for set operation */249if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {250skip_reg = true;251++skipped_set;252break;253}254}255256if (!reject_reg && !skip_reg) {257ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®);258if (ret) {259printf("%s: Failed to set ", config_name(c));260print_reg(config_name(c), reg.id);261putchar('\n');262++failed_set;263}264}265}266267for_each_new_reg(i)268++new_regs;269270for_each_missing_reg(i)271++missing_regs;272273if (new_regs || missing_regs) {274n = 0;275for_each_reg_filtered(i)276++n;277278printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);279printf("%s: Number registers: %5lld (includes %lld filtered registers)\n",280config_name(c), reg_list->n, reg_list->n - n);281}282283if (new_regs) {284printf("\n%s: There are %d new registers.\n"285"Consider adding them to the blessed reg "286"list with the following lines:\n\n", config_name(c), new_regs);287for_each_new_reg(i)288print_reg(config_name(c), reg_list->reg[i]);289putchar('\n');290}291292if (missing_regs) {293printf("\n%s: There are %d missing registers.\n"294"The following lines are missing registers:\n\n", config_name(c), missing_regs);295for_each_missing_reg(i)296print_reg(config_name(c), blessed_reg[i]);297putchar('\n');298}299300TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,301"%s: There are %d missing registers; %d registers failed get; "302"%d registers failed set; %d registers failed reject; %d registers skipped set",303config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);304305pr_info("%s: PASS\n", config_name(c));306blessed_n = 0;307free(blessed_reg);308free(reg_list);309kvm_vm_free(vm);310}311312static void help(void)313{314struct vcpu_reg_list *c;315int i;316317printf(318"\n"319"usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"320" --config=<selection> Used to select a specific vcpu configuration for the test/listing\n"321" '<selection>' may be\n");322323for (i = 0; i < vcpu_configs_n; ++i) {324c = vcpu_configs[i];325printf(326" '%s'\n", config_name(c));327}328329printf(330"\n"331" --list Print the register list rather than test it (requires --config)\n"332" --list-filtered Print registers that would normally be filtered out (requires --config)\n"333"\n"334);335}336337static struct vcpu_reg_list *parse_config(const char *config)338{339struct vcpu_reg_list *c = NULL;340int i;341342if (config[8] != '=')343help(), exit(1);344345for (i = 0; i < vcpu_configs_n; ++i) {346c = vcpu_configs[i];347if (strcmp(config_name(c), &config[9]) == 0)348break;349}350351if (i == vcpu_configs_n)352help(), exit(1);353354return c;355}356357int main(int ac, char **av)358{359struct vcpu_reg_list *c, *sel = NULL;360int i, ret = 0;361pid_t pid;362363for (i = 1; i < ac; ++i) {364if (strncmp(av[i], "--config", 8) == 0)365sel = parse_config(av[i]);366else if (strcmp(av[i], "--list") == 0)367print_list = true;368else if (strcmp(av[i], "--list-filtered") == 0)369print_filtered = true;370else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)371help(), exit(0);372else373help(), exit(1);374}375376if (print_list || print_filtered) {377/*378* We only want to print the register list of a single config.379*/380if (!sel)381help(), exit(1);382}383384for (i = 0; i < vcpu_configs_n; ++i) {385c = vcpu_configs[i];386if (sel && c != sel)387continue;388389pid = fork();390391if (!pid) {392run_test(c);393exit(0);394} else {395int wstatus;396pid_t wpid = wait(&wstatus);397TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");398if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)399ret = KSFT_FAIL;400}401}402403return ret;404}405406407