117da79e0SAndrew Jones // SPDX-License-Identifier: GPL-2.0 217da79e0SAndrew Jones /* 317da79e0SAndrew Jones * Check for KVM_GET_REG_LIST regressions. 417da79e0SAndrew Jones * 517da79e0SAndrew Jones * Copyright (C) 2020, Red Hat, Inc. 617da79e0SAndrew Jones * 717da79e0SAndrew Jones * When attempting to migrate from a host with an older kernel to a host 817da79e0SAndrew Jones * with a newer kernel we allow the newer kernel on the destination to 917da79e0SAndrew Jones * list new registers with get-reg-list. We assume they'll be unused, at 1017da79e0SAndrew Jones * least until the guest reboots, and so they're relatively harmless. 1117da79e0SAndrew Jones * However, if the destination host with the newer kernel is missing 1217da79e0SAndrew Jones * registers which the source host with the older kernel has, then that's 1317da79e0SAndrew Jones * a regression in get-reg-list. This test checks for that regression by 1417da79e0SAndrew Jones * checking the current list against a blessed list. We should never have 1517da79e0SAndrew Jones * missing registers, but if new ones appear then they can probably be 1617da79e0SAndrew Jones * added to the blessed list. A completely new blessed list can be created 1717da79e0SAndrew Jones * by running the test with the --list command line argument. 1817da79e0SAndrew Jones * 1917da79e0SAndrew Jones * The blessed list should be created from the oldest possible kernel. 2017da79e0SAndrew Jones */ 2117da79e0SAndrew Jones #include <stdio.h> 2217da79e0SAndrew Jones #include <stdlib.h> 2317da79e0SAndrew Jones #include <string.h> 2417da79e0SAndrew Jones #include <unistd.h> 2517da79e0SAndrew Jones #include <sys/types.h> 2617da79e0SAndrew Jones #include <sys/wait.h> 2717da79e0SAndrew Jones #include "kvm_util.h" 2817da79e0SAndrew Jones #include "test_util.h" 2917da79e0SAndrew Jones #include "processor.h" 3017da79e0SAndrew Jones 3117da79e0SAndrew Jones static struct kvm_reg_list *reg_list; 3217da79e0SAndrew Jones static __u64 *blessed_reg, blessed_n; 3317da79e0SAndrew Jones 3417da79e0SAndrew Jones extern struct vcpu_reg_list *vcpu_configs[]; 3517da79e0SAndrew Jones extern int vcpu_configs_n; 3617da79e0SAndrew Jones 3717da79e0SAndrew Jones #define for_each_sublist(c, s) \ 3817da79e0SAndrew Jones for ((s) = &(c)->sublists[0]; (s)->regs; ++(s)) 3917da79e0SAndrew Jones 4017da79e0SAndrew Jones #define for_each_reg(i) \ 4117da79e0SAndrew Jones for ((i) = 0; (i) < reg_list->n; ++(i)) 4217da79e0SAndrew Jones 4317da79e0SAndrew Jones #define for_each_reg_filtered(i) \ 4417da79e0SAndrew Jones for_each_reg(i) \ 4517da79e0SAndrew Jones if (!filter_reg(reg_list->reg[i])) 4617da79e0SAndrew Jones 4717da79e0SAndrew Jones #define for_each_missing_reg(i) \ 4817da79e0SAndrew Jones for ((i) = 0; (i) < blessed_n; ++(i)) \ 4917da79e0SAndrew Jones if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ 5017da79e0SAndrew Jones if (check_supported_reg(vcpu, blessed_reg[i])) 5117da79e0SAndrew Jones 5217da79e0SAndrew Jones #define for_each_new_reg(i) \ 5317da79e0SAndrew Jones for_each_reg_filtered(i) \ 5417da79e0SAndrew Jones if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 5517da79e0SAndrew Jones 5617da79e0SAndrew Jones static const char *config_name(struct vcpu_reg_list *c) 5717da79e0SAndrew Jones { 5817da79e0SAndrew Jones struct vcpu_reg_sublist *s; 5917da79e0SAndrew Jones int len = 0; 6017da79e0SAndrew Jones 6117da79e0SAndrew Jones if (c->name) 6217da79e0SAndrew Jones return c->name; 6317da79e0SAndrew Jones 6417da79e0SAndrew Jones for_each_sublist(c, s) 6517da79e0SAndrew Jones len += strlen(s->name) + 1; 6617da79e0SAndrew Jones 6717da79e0SAndrew Jones c->name = malloc(len); 6817da79e0SAndrew Jones 6917da79e0SAndrew Jones len = 0; 7017da79e0SAndrew Jones for_each_sublist(c, s) { 7117da79e0SAndrew Jones if (!strcmp(s->name, "base")) 7217da79e0SAndrew Jones continue; 7317da79e0SAndrew Jones strcat(c->name + len, s->name); 7417da79e0SAndrew Jones len += strlen(s->name) + 1; 7517da79e0SAndrew Jones c->name[len - 1] = '+'; 7617da79e0SAndrew Jones } 7717da79e0SAndrew Jones c->name[len - 1] = '\0'; 7817da79e0SAndrew Jones 7917da79e0SAndrew Jones return c->name; 8017da79e0SAndrew Jones } 8117da79e0SAndrew Jones 8217da79e0SAndrew Jones bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) 8317da79e0SAndrew Jones { 8417da79e0SAndrew Jones return true; 8517da79e0SAndrew Jones } 8617da79e0SAndrew Jones 8717da79e0SAndrew Jones bool __weak filter_reg(__u64 reg) 8817da79e0SAndrew Jones { 8917da79e0SAndrew Jones return false; 9017da79e0SAndrew Jones } 9117da79e0SAndrew Jones 9217da79e0SAndrew Jones static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) 9317da79e0SAndrew Jones { 9417da79e0SAndrew Jones int i; 9517da79e0SAndrew Jones 9617da79e0SAndrew Jones for (i = 0; i < nr_regs; ++i) 9717da79e0SAndrew Jones if (reg == regs[i]) 9817da79e0SAndrew Jones return true; 9917da79e0SAndrew Jones return false; 10017da79e0SAndrew Jones } 10117da79e0SAndrew Jones 10217da79e0SAndrew Jones void __weak print_reg(const char *prefix, __u64 id) 10317da79e0SAndrew Jones { 10417da79e0SAndrew Jones printf("\t0x%llx,\n", id); 10517da79e0SAndrew Jones } 10617da79e0SAndrew Jones 107*90a6bcbcSHaibo Xu bool __weak check_reject_set(int err) 108*90a6bcbcSHaibo Xu { 109*90a6bcbcSHaibo Xu return true; 110*90a6bcbcSHaibo Xu } 111*90a6bcbcSHaibo Xu 112be4c5806SAndrew Jones #ifdef __aarch64__ 11317da79e0SAndrew Jones static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init) 11417da79e0SAndrew Jones { 11517da79e0SAndrew Jones struct vcpu_reg_sublist *s; 11617da79e0SAndrew Jones 11717da79e0SAndrew Jones for_each_sublist(c, s) 11817da79e0SAndrew Jones if (s->capability) 11917da79e0SAndrew Jones init->features[s->feature / 32] |= 1 << (s->feature % 32); 12017da79e0SAndrew Jones } 12117da79e0SAndrew Jones 12217da79e0SAndrew Jones static void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) 12317da79e0SAndrew Jones { 12417da79e0SAndrew Jones struct vcpu_reg_sublist *s; 12517da79e0SAndrew Jones int feature; 12617da79e0SAndrew Jones 12717da79e0SAndrew Jones for_each_sublist(c, s) { 12817da79e0SAndrew Jones if (s->finalize) { 12917da79e0SAndrew Jones feature = s->feature; 13017da79e0SAndrew Jones vcpu_ioctl(vcpu, KVM_ARM_VCPU_FINALIZE, &feature); 13117da79e0SAndrew Jones } 13217da79e0SAndrew Jones } 13317da79e0SAndrew Jones } 13417da79e0SAndrew Jones 135be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 136be4c5806SAndrew Jones { 137be4c5806SAndrew Jones struct kvm_vcpu_init init = { .target = -1, }; 138be4c5806SAndrew Jones struct kvm_vcpu *vcpu; 139be4c5806SAndrew Jones 140be4c5806SAndrew Jones prepare_vcpu_init(c, &init); 141be4c5806SAndrew Jones vcpu = __vm_vcpu_add(vm, 0); 142be4c5806SAndrew Jones aarch64_vcpu_setup(vcpu, &init); 143be4c5806SAndrew Jones finalize_vcpu(vcpu, c); 144be4c5806SAndrew Jones 145be4c5806SAndrew Jones return vcpu; 146be4c5806SAndrew Jones } 147be4c5806SAndrew Jones #else 148be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 149be4c5806SAndrew Jones { 150be4c5806SAndrew Jones return __vm_vcpu_add(vm, 0); 151be4c5806SAndrew Jones } 152be4c5806SAndrew Jones #endif 153be4c5806SAndrew Jones 15417da79e0SAndrew Jones static void check_supported(struct vcpu_reg_list *c) 15517da79e0SAndrew Jones { 15617da79e0SAndrew Jones struct vcpu_reg_sublist *s; 15717da79e0SAndrew Jones 15817da79e0SAndrew Jones for_each_sublist(c, s) { 15917da79e0SAndrew Jones if (!s->capability) 16017da79e0SAndrew Jones continue; 16117da79e0SAndrew Jones 16217da79e0SAndrew Jones __TEST_REQUIRE(kvm_has_cap(s->capability), 16317da79e0SAndrew Jones "%s: %s not available, skipping tests\n", 16417da79e0SAndrew Jones config_name(c), s->name); 16517da79e0SAndrew Jones } 16617da79e0SAndrew Jones } 16717da79e0SAndrew Jones 16817da79e0SAndrew Jones static bool print_list; 16917da79e0SAndrew Jones static bool print_filtered; 17017da79e0SAndrew Jones 17117da79e0SAndrew Jones static void run_test(struct vcpu_reg_list *c) 17217da79e0SAndrew Jones { 17317da79e0SAndrew Jones int new_regs = 0, missing_regs = 0, i, n; 17417da79e0SAndrew Jones int failed_get = 0, failed_set = 0, failed_reject = 0; 17517da79e0SAndrew Jones struct kvm_vcpu *vcpu; 17617da79e0SAndrew Jones struct kvm_vm *vm; 17717da79e0SAndrew Jones struct vcpu_reg_sublist *s; 17817da79e0SAndrew Jones 17917da79e0SAndrew Jones check_supported(c); 18017da79e0SAndrew Jones 18117da79e0SAndrew Jones vm = vm_create_barebones(); 182be4c5806SAndrew Jones vcpu = vcpu_config_get_vcpu(c, vm); 18317da79e0SAndrew Jones 18417da79e0SAndrew Jones reg_list = vcpu_get_reg_list(vcpu); 18517da79e0SAndrew Jones 18617da79e0SAndrew Jones if (print_list || print_filtered) { 18717da79e0SAndrew Jones putchar('\n'); 18817da79e0SAndrew Jones for_each_reg(i) { 18917da79e0SAndrew Jones __u64 id = reg_list->reg[i]; 19017da79e0SAndrew Jones if ((print_list && !filter_reg(id)) || 19117da79e0SAndrew Jones (print_filtered && filter_reg(id))) 19217da79e0SAndrew Jones print_reg(config_name(c), id); 19317da79e0SAndrew Jones } 19417da79e0SAndrew Jones putchar('\n'); 19517da79e0SAndrew Jones return; 19617da79e0SAndrew Jones } 19717da79e0SAndrew Jones 19817da79e0SAndrew Jones /* 19917da79e0SAndrew Jones * We only test that we can get the register and then write back the 20017da79e0SAndrew Jones * same value. Some registers may allow other values to be written 20117da79e0SAndrew Jones * back, but others only allow some bits to be changed, and at least 20217da79e0SAndrew Jones * for ID registers set will fail if the value does not exactly match 20317da79e0SAndrew Jones * what was returned by get. If registers that allow other values to 20417da79e0SAndrew Jones * be written need to have the other values tested, then we should 20517da79e0SAndrew Jones * create a new set of tests for those in a new independent test 20617da79e0SAndrew Jones * executable. 20717da79e0SAndrew Jones */ 20817da79e0SAndrew Jones for_each_reg(i) { 20917da79e0SAndrew Jones uint8_t addr[2048 / 8]; 21017da79e0SAndrew Jones struct kvm_one_reg reg = { 21117da79e0SAndrew Jones .id = reg_list->reg[i], 21217da79e0SAndrew Jones .addr = (__u64)&addr, 21317da79e0SAndrew Jones }; 21417da79e0SAndrew Jones bool reject_reg = false; 21517da79e0SAndrew Jones int ret; 21617da79e0SAndrew Jones 21717da79e0SAndrew Jones ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); 21817da79e0SAndrew Jones if (ret) { 21917da79e0SAndrew Jones printf("%s: Failed to get ", config_name(c)); 22017da79e0SAndrew Jones print_reg(config_name(c), reg.id); 22117da79e0SAndrew Jones putchar('\n'); 22217da79e0SAndrew Jones ++failed_get; 22317da79e0SAndrew Jones } 22417da79e0SAndrew Jones 22517da79e0SAndrew Jones /* rejects_set registers are rejected after KVM_ARM_VCPU_FINALIZE */ 22617da79e0SAndrew Jones for_each_sublist(c, s) { 22717da79e0SAndrew Jones if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { 22817da79e0SAndrew Jones reject_reg = true; 22917da79e0SAndrew Jones ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 230*90a6bcbcSHaibo Xu if (ret != -1 || !check_reject_set(errno)) { 23117da79e0SAndrew Jones printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); 23217da79e0SAndrew Jones print_reg(config_name(c), reg.id); 23317da79e0SAndrew Jones putchar('\n'); 23417da79e0SAndrew Jones ++failed_reject; 23517da79e0SAndrew Jones } 23617da79e0SAndrew Jones break; 23717da79e0SAndrew Jones } 23817da79e0SAndrew Jones } 23917da79e0SAndrew Jones 24017da79e0SAndrew Jones if (!reject_reg) { 24117da79e0SAndrew Jones ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 24217da79e0SAndrew Jones if (ret) { 24317da79e0SAndrew Jones printf("%s: Failed to set ", config_name(c)); 24417da79e0SAndrew Jones print_reg(config_name(c), reg.id); 24517da79e0SAndrew Jones putchar('\n'); 24617da79e0SAndrew Jones ++failed_set; 24717da79e0SAndrew Jones } 24817da79e0SAndrew Jones } 24917da79e0SAndrew Jones } 25017da79e0SAndrew Jones 25117da79e0SAndrew Jones for_each_sublist(c, s) 25217da79e0SAndrew Jones blessed_n += s->regs_n; 25317da79e0SAndrew Jones blessed_reg = calloc(blessed_n, sizeof(__u64)); 25417da79e0SAndrew Jones 25517da79e0SAndrew Jones n = 0; 25617da79e0SAndrew Jones for_each_sublist(c, s) { 25717da79e0SAndrew Jones for (i = 0; i < s->regs_n; ++i) 25817da79e0SAndrew Jones blessed_reg[n++] = s->regs[i]; 25917da79e0SAndrew Jones } 26017da79e0SAndrew Jones 26117da79e0SAndrew Jones for_each_new_reg(i) 26217da79e0SAndrew Jones ++new_regs; 26317da79e0SAndrew Jones 26417da79e0SAndrew Jones for_each_missing_reg(i) 26517da79e0SAndrew Jones ++missing_regs; 26617da79e0SAndrew Jones 26717da79e0SAndrew Jones if (new_regs || missing_regs) { 26817da79e0SAndrew Jones n = 0; 26917da79e0SAndrew Jones for_each_reg_filtered(i) 27017da79e0SAndrew Jones ++n; 27117da79e0SAndrew Jones 27217da79e0SAndrew Jones printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); 27317da79e0SAndrew Jones printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", 27417da79e0SAndrew Jones config_name(c), reg_list->n, reg_list->n - n); 27517da79e0SAndrew Jones } 27617da79e0SAndrew Jones 27717da79e0SAndrew Jones if (new_regs) { 27817da79e0SAndrew Jones printf("\n%s: There are %d new registers.\n" 27917da79e0SAndrew Jones "Consider adding them to the blessed reg " 28017da79e0SAndrew Jones "list with the following lines:\n\n", config_name(c), new_regs); 28117da79e0SAndrew Jones for_each_new_reg(i) 28217da79e0SAndrew Jones print_reg(config_name(c), reg_list->reg[i]); 28317da79e0SAndrew Jones putchar('\n'); 28417da79e0SAndrew Jones } 28517da79e0SAndrew Jones 28617da79e0SAndrew Jones if (missing_regs) { 28717da79e0SAndrew Jones printf("\n%s: There are %d missing registers.\n" 28817da79e0SAndrew Jones "The following lines are missing registers:\n\n", config_name(c), missing_regs); 28917da79e0SAndrew Jones for_each_missing_reg(i) 29017da79e0SAndrew Jones print_reg(config_name(c), blessed_reg[i]); 29117da79e0SAndrew Jones putchar('\n'); 29217da79e0SAndrew Jones } 29317da79e0SAndrew Jones 29417da79e0SAndrew Jones TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, 29517da79e0SAndrew Jones "%s: There are %d missing registers; " 29617da79e0SAndrew Jones "%d registers failed get; %d registers failed set; %d registers failed reject", 29717da79e0SAndrew Jones config_name(c), missing_regs, failed_get, failed_set, failed_reject); 29817da79e0SAndrew Jones 29917da79e0SAndrew Jones pr_info("%s: PASS\n", config_name(c)); 30017da79e0SAndrew Jones blessed_n = 0; 30117da79e0SAndrew Jones free(blessed_reg); 30217da79e0SAndrew Jones free(reg_list); 30317da79e0SAndrew Jones kvm_vm_free(vm); 30417da79e0SAndrew Jones } 30517da79e0SAndrew Jones 30617da79e0SAndrew Jones static void help(void) 30717da79e0SAndrew Jones { 30817da79e0SAndrew Jones struct vcpu_reg_list *c; 30917da79e0SAndrew Jones int i; 31017da79e0SAndrew Jones 31117da79e0SAndrew Jones printf( 31217da79e0SAndrew Jones "\n" 31317da79e0SAndrew Jones "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n" 31417da79e0SAndrew Jones " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" 31517da79e0SAndrew Jones " '<selection>' may be\n"); 31617da79e0SAndrew Jones 31717da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 31817da79e0SAndrew Jones c = vcpu_configs[i]; 31917da79e0SAndrew Jones printf( 32017da79e0SAndrew Jones " '%s'\n", config_name(c)); 32117da79e0SAndrew Jones } 32217da79e0SAndrew Jones 32317da79e0SAndrew Jones printf( 32417da79e0SAndrew Jones "\n" 32517da79e0SAndrew Jones " --list Print the register list rather than test it (requires --config)\n" 32617da79e0SAndrew Jones " --list-filtered Print registers that would normally be filtered out (requires --config)\n" 32717da79e0SAndrew Jones "\n" 32817da79e0SAndrew Jones ); 32917da79e0SAndrew Jones } 33017da79e0SAndrew Jones 33117da79e0SAndrew Jones static struct vcpu_reg_list *parse_config(const char *config) 33217da79e0SAndrew Jones { 33317da79e0SAndrew Jones struct vcpu_reg_list *c = NULL; 33417da79e0SAndrew Jones int i; 33517da79e0SAndrew Jones 33617da79e0SAndrew Jones if (config[8] != '=') 33717da79e0SAndrew Jones help(), exit(1); 33817da79e0SAndrew Jones 33917da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 34017da79e0SAndrew Jones c = vcpu_configs[i]; 34117da79e0SAndrew Jones if (strcmp(config_name(c), &config[9]) == 0) 34217da79e0SAndrew Jones break; 34317da79e0SAndrew Jones } 34417da79e0SAndrew Jones 34517da79e0SAndrew Jones if (i == vcpu_configs_n) 34617da79e0SAndrew Jones help(), exit(1); 34717da79e0SAndrew Jones 34817da79e0SAndrew Jones return c; 34917da79e0SAndrew Jones } 35017da79e0SAndrew Jones 35117da79e0SAndrew Jones int main(int ac, char **av) 35217da79e0SAndrew Jones { 35317da79e0SAndrew Jones struct vcpu_reg_list *c, *sel = NULL; 35417da79e0SAndrew Jones int i, ret = 0; 35517da79e0SAndrew Jones pid_t pid; 35617da79e0SAndrew Jones 35717da79e0SAndrew Jones for (i = 1; i < ac; ++i) { 35817da79e0SAndrew Jones if (strncmp(av[i], "--config", 8) == 0) 35917da79e0SAndrew Jones sel = parse_config(av[i]); 36017da79e0SAndrew Jones else if (strcmp(av[i], "--list") == 0) 36117da79e0SAndrew Jones print_list = true; 36217da79e0SAndrew Jones else if (strcmp(av[i], "--list-filtered") == 0) 36317da79e0SAndrew Jones print_filtered = true; 36417da79e0SAndrew Jones else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) 36517da79e0SAndrew Jones help(), exit(0); 36617da79e0SAndrew Jones else 36717da79e0SAndrew Jones help(), exit(1); 36817da79e0SAndrew Jones } 36917da79e0SAndrew Jones 37017da79e0SAndrew Jones if (print_list || print_filtered) { 37117da79e0SAndrew Jones /* 37217da79e0SAndrew Jones * We only want to print the register list of a single config. 37317da79e0SAndrew Jones */ 37417da79e0SAndrew Jones if (!sel) 37517da79e0SAndrew Jones help(), exit(1); 37617da79e0SAndrew Jones } 37717da79e0SAndrew Jones 37817da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 37917da79e0SAndrew Jones c = vcpu_configs[i]; 38017da79e0SAndrew Jones if (sel && c != sel) 38117da79e0SAndrew Jones continue; 38217da79e0SAndrew Jones 38317da79e0SAndrew Jones pid = fork(); 38417da79e0SAndrew Jones 38517da79e0SAndrew Jones if (!pid) { 38617da79e0SAndrew Jones run_test(c); 38717da79e0SAndrew Jones exit(0); 38817da79e0SAndrew Jones } else { 38917da79e0SAndrew Jones int wstatus; 39017da79e0SAndrew Jones pid_t wpid = wait(&wstatus); 39117da79e0SAndrew Jones TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); 39217da79e0SAndrew Jones if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) 39317da79e0SAndrew Jones ret = KSFT_FAIL; 39417da79e0SAndrew Jones } 39517da79e0SAndrew Jones } 39617da79e0SAndrew Jones 39717da79e0SAndrew Jones return ret; 39817da79e0SAndrew Jones } 399