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_reg(i) \ 3817da79e0SAndrew Jones for ((i) = 0; (i) < reg_list->n; ++(i)) 3917da79e0SAndrew Jones 4017da79e0SAndrew Jones #define for_each_reg_filtered(i) \ 4117da79e0SAndrew Jones for_each_reg(i) \ 4217da79e0SAndrew Jones if (!filter_reg(reg_list->reg[i])) 4317da79e0SAndrew Jones 4417da79e0SAndrew Jones #define for_each_missing_reg(i) \ 4517da79e0SAndrew Jones for ((i) = 0; (i) < blessed_n; ++(i)) \ 4617da79e0SAndrew Jones if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ 4717da79e0SAndrew Jones if (check_supported_reg(vcpu, blessed_reg[i])) 4817da79e0SAndrew Jones 4917da79e0SAndrew Jones #define for_each_new_reg(i) \ 5017da79e0SAndrew Jones for_each_reg_filtered(i) \ 5117da79e0SAndrew Jones if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 5217da79e0SAndrew Jones 53*c4746771SHaibo Xu #define for_each_present_blessed_reg(i) \ 54*c4746771SHaibo Xu for_each_reg(i) \ 55*c4746771SHaibo Xu if (find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 56*c4746771SHaibo Xu 5717da79e0SAndrew Jones static const char *config_name(struct vcpu_reg_list *c) 5817da79e0SAndrew Jones { 5917da79e0SAndrew Jones struct vcpu_reg_sublist *s; 6017da79e0SAndrew Jones int len = 0; 6117da79e0SAndrew Jones 6217da79e0SAndrew Jones if (c->name) 6317da79e0SAndrew Jones return c->name; 6417da79e0SAndrew Jones 6517da79e0SAndrew Jones for_each_sublist(c, s) 6617da79e0SAndrew Jones len += strlen(s->name) + 1; 6717da79e0SAndrew Jones 6817da79e0SAndrew Jones c->name = malloc(len); 6917da79e0SAndrew Jones 7017da79e0SAndrew Jones len = 0; 7117da79e0SAndrew Jones for_each_sublist(c, s) { 7217da79e0SAndrew Jones if (!strcmp(s->name, "base")) 7317da79e0SAndrew Jones continue; 7417da79e0SAndrew Jones strcat(c->name + len, s->name); 7517da79e0SAndrew Jones len += strlen(s->name) + 1; 7617da79e0SAndrew Jones c->name[len - 1] = '+'; 7717da79e0SAndrew Jones } 7817da79e0SAndrew Jones c->name[len - 1] = '\0'; 7917da79e0SAndrew Jones 8017da79e0SAndrew Jones return c->name; 8117da79e0SAndrew Jones } 8217da79e0SAndrew Jones 8317da79e0SAndrew Jones bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) 8417da79e0SAndrew Jones { 8517da79e0SAndrew Jones return true; 8617da79e0SAndrew Jones } 8717da79e0SAndrew Jones 8817da79e0SAndrew Jones bool __weak filter_reg(__u64 reg) 8917da79e0SAndrew Jones { 9017da79e0SAndrew Jones return false; 9117da79e0SAndrew Jones } 9217da79e0SAndrew Jones 9317da79e0SAndrew Jones static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) 9417da79e0SAndrew Jones { 9517da79e0SAndrew Jones int i; 9617da79e0SAndrew Jones 9717da79e0SAndrew Jones for (i = 0; i < nr_regs; ++i) 9817da79e0SAndrew Jones if (reg == regs[i]) 9917da79e0SAndrew Jones return true; 10017da79e0SAndrew Jones return false; 10117da79e0SAndrew Jones } 10217da79e0SAndrew Jones 10317da79e0SAndrew Jones void __weak print_reg(const char *prefix, __u64 id) 10417da79e0SAndrew Jones { 10517da79e0SAndrew Jones printf("\t0x%llx,\n", id); 10617da79e0SAndrew Jones } 10717da79e0SAndrew Jones 10890a6bcbcSHaibo Xu bool __weak check_reject_set(int err) 10990a6bcbcSHaibo Xu { 11090a6bcbcSHaibo Xu return true; 11190a6bcbcSHaibo Xu } 11290a6bcbcSHaibo Xu 113e8566033SHaibo Xu void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) 114e8566033SHaibo Xu { 115e8566033SHaibo Xu } 116e8566033SHaibo Xu 117be4c5806SAndrew Jones #ifdef __aarch64__ 11817da79e0SAndrew Jones static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init) 11917da79e0SAndrew Jones { 12017da79e0SAndrew Jones struct vcpu_reg_sublist *s; 12117da79e0SAndrew Jones 12217da79e0SAndrew Jones for_each_sublist(c, s) 12317da79e0SAndrew Jones if (s->capability) 12417da79e0SAndrew Jones init->features[s->feature / 32] |= 1 << (s->feature % 32); 12517da79e0SAndrew Jones } 12617da79e0SAndrew Jones 127be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 128be4c5806SAndrew Jones { 129be4c5806SAndrew Jones struct kvm_vcpu_init init = { .target = -1, }; 130be4c5806SAndrew Jones struct kvm_vcpu *vcpu; 131be4c5806SAndrew Jones 132be4c5806SAndrew Jones prepare_vcpu_init(c, &init); 133be4c5806SAndrew Jones vcpu = __vm_vcpu_add(vm, 0); 134be4c5806SAndrew Jones aarch64_vcpu_setup(vcpu, &init); 135be4c5806SAndrew Jones 136be4c5806SAndrew Jones return vcpu; 137be4c5806SAndrew Jones } 138be4c5806SAndrew Jones #else 139be4c5806SAndrew Jones static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 140be4c5806SAndrew Jones { 141be4c5806SAndrew Jones return __vm_vcpu_add(vm, 0); 142be4c5806SAndrew Jones } 143be4c5806SAndrew Jones #endif 144be4c5806SAndrew Jones 14517da79e0SAndrew Jones static void check_supported(struct vcpu_reg_list *c) 14617da79e0SAndrew Jones { 14717da79e0SAndrew Jones struct vcpu_reg_sublist *s; 14817da79e0SAndrew Jones 14917da79e0SAndrew Jones for_each_sublist(c, s) { 15017da79e0SAndrew Jones if (!s->capability) 15117da79e0SAndrew Jones continue; 15217da79e0SAndrew Jones 15317da79e0SAndrew Jones __TEST_REQUIRE(kvm_has_cap(s->capability), 15417da79e0SAndrew Jones "%s: %s not available, skipping tests\n", 15517da79e0SAndrew Jones config_name(c), s->name); 15617da79e0SAndrew Jones } 15717da79e0SAndrew Jones } 15817da79e0SAndrew Jones 15917da79e0SAndrew Jones static bool print_list; 16017da79e0SAndrew Jones static bool print_filtered; 16117da79e0SAndrew Jones 16217da79e0SAndrew Jones static void run_test(struct vcpu_reg_list *c) 16317da79e0SAndrew Jones { 16417da79e0SAndrew Jones int new_regs = 0, missing_regs = 0, i, n; 16517da79e0SAndrew Jones int failed_get = 0, failed_set = 0, failed_reject = 0; 16617da79e0SAndrew Jones struct kvm_vcpu *vcpu; 16717da79e0SAndrew Jones struct kvm_vm *vm; 16817da79e0SAndrew Jones struct vcpu_reg_sublist *s; 16917da79e0SAndrew Jones 17017da79e0SAndrew Jones check_supported(c); 17117da79e0SAndrew Jones 17217da79e0SAndrew Jones vm = vm_create_barebones(); 173be4c5806SAndrew Jones vcpu = vcpu_config_get_vcpu(c, vm); 174e8566033SHaibo Xu finalize_vcpu(vcpu, c); 17517da79e0SAndrew Jones 17617da79e0SAndrew Jones reg_list = vcpu_get_reg_list(vcpu); 17717da79e0SAndrew Jones 17817da79e0SAndrew Jones if (print_list || print_filtered) { 17917da79e0SAndrew Jones putchar('\n'); 18017da79e0SAndrew Jones for_each_reg(i) { 18117da79e0SAndrew Jones __u64 id = reg_list->reg[i]; 18217da79e0SAndrew Jones if ((print_list && !filter_reg(id)) || 18317da79e0SAndrew Jones (print_filtered && filter_reg(id))) 18417da79e0SAndrew Jones print_reg(config_name(c), id); 18517da79e0SAndrew Jones } 18617da79e0SAndrew Jones putchar('\n'); 18717da79e0SAndrew Jones return; 18817da79e0SAndrew Jones } 18917da79e0SAndrew Jones 190*c4746771SHaibo Xu for_each_sublist(c, s) 191*c4746771SHaibo Xu blessed_n += s->regs_n; 192*c4746771SHaibo Xu blessed_reg = calloc(blessed_n, sizeof(__u64)); 193*c4746771SHaibo Xu 194*c4746771SHaibo Xu n = 0; 195*c4746771SHaibo Xu for_each_sublist(c, s) { 196*c4746771SHaibo Xu for (i = 0; i < s->regs_n; ++i) 197*c4746771SHaibo Xu blessed_reg[n++] = s->regs[i]; 198*c4746771SHaibo Xu } 199*c4746771SHaibo Xu 20017da79e0SAndrew Jones /* 20117da79e0SAndrew Jones * We only test that we can get the register and then write back the 20217da79e0SAndrew Jones * same value. Some registers may allow other values to be written 20317da79e0SAndrew Jones * back, but others only allow some bits to be changed, and at least 20417da79e0SAndrew Jones * for ID registers set will fail if the value does not exactly match 20517da79e0SAndrew Jones * what was returned by get. If registers that allow other values to 20617da79e0SAndrew Jones * be written need to have the other values tested, then we should 20717da79e0SAndrew Jones * create a new set of tests for those in a new independent test 20817da79e0SAndrew Jones * executable. 209*c4746771SHaibo Xu * 210*c4746771SHaibo Xu * Only do the get/set tests on present, blessed list registers, 211*c4746771SHaibo Xu * since we don't know the capabilities of any new registers. 21217da79e0SAndrew Jones */ 213*c4746771SHaibo Xu for_each_present_blessed_reg(i) { 21417da79e0SAndrew Jones uint8_t addr[2048 / 8]; 21517da79e0SAndrew Jones struct kvm_one_reg reg = { 21617da79e0SAndrew Jones .id = reg_list->reg[i], 21717da79e0SAndrew Jones .addr = (__u64)&addr, 21817da79e0SAndrew Jones }; 21917da79e0SAndrew Jones bool reject_reg = false; 22017da79e0SAndrew Jones int ret; 22117da79e0SAndrew Jones 22217da79e0SAndrew Jones ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); 22317da79e0SAndrew Jones if (ret) { 22417da79e0SAndrew Jones printf("%s: Failed to get ", config_name(c)); 22517da79e0SAndrew Jones print_reg(config_name(c), reg.id); 22617da79e0SAndrew Jones putchar('\n'); 22717da79e0SAndrew Jones ++failed_get; 22817da79e0SAndrew Jones } 22917da79e0SAndrew Jones 23017da79e0SAndrew Jones /* rejects_set registers are rejected after KVM_ARM_VCPU_FINALIZE */ 23117da79e0SAndrew Jones for_each_sublist(c, s) { 23217da79e0SAndrew Jones if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { 23317da79e0SAndrew Jones reject_reg = true; 23417da79e0SAndrew Jones ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 23590a6bcbcSHaibo Xu if (ret != -1 || !check_reject_set(errno)) { 23617da79e0SAndrew Jones printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); 23717da79e0SAndrew Jones print_reg(config_name(c), reg.id); 23817da79e0SAndrew Jones putchar('\n'); 23917da79e0SAndrew Jones ++failed_reject; 24017da79e0SAndrew Jones } 24117da79e0SAndrew Jones break; 24217da79e0SAndrew Jones } 24317da79e0SAndrew Jones } 24417da79e0SAndrew Jones 24517da79e0SAndrew Jones if (!reject_reg) { 24617da79e0SAndrew Jones ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 24717da79e0SAndrew Jones if (ret) { 24817da79e0SAndrew Jones printf("%s: Failed to set ", config_name(c)); 24917da79e0SAndrew Jones print_reg(config_name(c), reg.id); 25017da79e0SAndrew Jones putchar('\n'); 25117da79e0SAndrew Jones ++failed_set; 25217da79e0SAndrew Jones } 25317da79e0SAndrew Jones } 25417da79e0SAndrew Jones } 25517da79e0SAndrew Jones 25617da79e0SAndrew Jones for_each_new_reg(i) 25717da79e0SAndrew Jones ++new_regs; 25817da79e0SAndrew Jones 25917da79e0SAndrew Jones for_each_missing_reg(i) 26017da79e0SAndrew Jones ++missing_regs; 26117da79e0SAndrew Jones 26217da79e0SAndrew Jones if (new_regs || missing_regs) { 26317da79e0SAndrew Jones n = 0; 26417da79e0SAndrew Jones for_each_reg_filtered(i) 26517da79e0SAndrew Jones ++n; 26617da79e0SAndrew Jones 26717da79e0SAndrew Jones printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); 26817da79e0SAndrew Jones printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", 26917da79e0SAndrew Jones config_name(c), reg_list->n, reg_list->n - n); 27017da79e0SAndrew Jones } 27117da79e0SAndrew Jones 27217da79e0SAndrew Jones if (new_regs) { 27317da79e0SAndrew Jones printf("\n%s: There are %d new registers.\n" 27417da79e0SAndrew Jones "Consider adding them to the blessed reg " 27517da79e0SAndrew Jones "list with the following lines:\n\n", config_name(c), new_regs); 27617da79e0SAndrew Jones for_each_new_reg(i) 27717da79e0SAndrew Jones print_reg(config_name(c), reg_list->reg[i]); 27817da79e0SAndrew Jones putchar('\n'); 27917da79e0SAndrew Jones } 28017da79e0SAndrew Jones 28117da79e0SAndrew Jones if (missing_regs) { 28217da79e0SAndrew Jones printf("\n%s: There are %d missing registers.\n" 28317da79e0SAndrew Jones "The following lines are missing registers:\n\n", config_name(c), missing_regs); 28417da79e0SAndrew Jones for_each_missing_reg(i) 28517da79e0SAndrew Jones print_reg(config_name(c), blessed_reg[i]); 28617da79e0SAndrew Jones putchar('\n'); 28717da79e0SAndrew Jones } 28817da79e0SAndrew Jones 28917da79e0SAndrew Jones TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, 29017da79e0SAndrew Jones "%s: There are %d missing registers; " 29117da79e0SAndrew Jones "%d registers failed get; %d registers failed set; %d registers failed reject", 29217da79e0SAndrew Jones config_name(c), missing_regs, failed_get, failed_set, failed_reject); 29317da79e0SAndrew Jones 29417da79e0SAndrew Jones pr_info("%s: PASS\n", config_name(c)); 29517da79e0SAndrew Jones blessed_n = 0; 29617da79e0SAndrew Jones free(blessed_reg); 29717da79e0SAndrew Jones free(reg_list); 29817da79e0SAndrew Jones kvm_vm_free(vm); 29917da79e0SAndrew Jones } 30017da79e0SAndrew Jones 30117da79e0SAndrew Jones static void help(void) 30217da79e0SAndrew Jones { 30317da79e0SAndrew Jones struct vcpu_reg_list *c; 30417da79e0SAndrew Jones int i; 30517da79e0SAndrew Jones 30617da79e0SAndrew Jones printf( 30717da79e0SAndrew Jones "\n" 30817da79e0SAndrew Jones "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n" 30917da79e0SAndrew Jones " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" 31017da79e0SAndrew Jones " '<selection>' may be\n"); 31117da79e0SAndrew Jones 31217da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 31317da79e0SAndrew Jones c = vcpu_configs[i]; 31417da79e0SAndrew Jones printf( 31517da79e0SAndrew Jones " '%s'\n", config_name(c)); 31617da79e0SAndrew Jones } 31717da79e0SAndrew Jones 31817da79e0SAndrew Jones printf( 31917da79e0SAndrew Jones "\n" 32017da79e0SAndrew Jones " --list Print the register list rather than test it (requires --config)\n" 32117da79e0SAndrew Jones " --list-filtered Print registers that would normally be filtered out (requires --config)\n" 32217da79e0SAndrew Jones "\n" 32317da79e0SAndrew Jones ); 32417da79e0SAndrew Jones } 32517da79e0SAndrew Jones 32617da79e0SAndrew Jones static struct vcpu_reg_list *parse_config(const char *config) 32717da79e0SAndrew Jones { 32817da79e0SAndrew Jones struct vcpu_reg_list *c = NULL; 32917da79e0SAndrew Jones int i; 33017da79e0SAndrew Jones 33117da79e0SAndrew Jones if (config[8] != '=') 33217da79e0SAndrew Jones help(), exit(1); 33317da79e0SAndrew Jones 33417da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 33517da79e0SAndrew Jones c = vcpu_configs[i]; 33617da79e0SAndrew Jones if (strcmp(config_name(c), &config[9]) == 0) 33717da79e0SAndrew Jones break; 33817da79e0SAndrew Jones } 33917da79e0SAndrew Jones 34017da79e0SAndrew Jones if (i == vcpu_configs_n) 34117da79e0SAndrew Jones help(), exit(1); 34217da79e0SAndrew Jones 34317da79e0SAndrew Jones return c; 34417da79e0SAndrew Jones } 34517da79e0SAndrew Jones 34617da79e0SAndrew Jones int main(int ac, char **av) 34717da79e0SAndrew Jones { 34817da79e0SAndrew Jones struct vcpu_reg_list *c, *sel = NULL; 34917da79e0SAndrew Jones int i, ret = 0; 35017da79e0SAndrew Jones pid_t pid; 35117da79e0SAndrew Jones 35217da79e0SAndrew Jones for (i = 1; i < ac; ++i) { 35317da79e0SAndrew Jones if (strncmp(av[i], "--config", 8) == 0) 35417da79e0SAndrew Jones sel = parse_config(av[i]); 35517da79e0SAndrew Jones else if (strcmp(av[i], "--list") == 0) 35617da79e0SAndrew Jones print_list = true; 35717da79e0SAndrew Jones else if (strcmp(av[i], "--list-filtered") == 0) 35817da79e0SAndrew Jones print_filtered = true; 35917da79e0SAndrew Jones else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) 36017da79e0SAndrew Jones help(), exit(0); 36117da79e0SAndrew Jones else 36217da79e0SAndrew Jones help(), exit(1); 36317da79e0SAndrew Jones } 36417da79e0SAndrew Jones 36517da79e0SAndrew Jones if (print_list || print_filtered) { 36617da79e0SAndrew Jones /* 36717da79e0SAndrew Jones * We only want to print the register list of a single config. 36817da79e0SAndrew Jones */ 36917da79e0SAndrew Jones if (!sel) 37017da79e0SAndrew Jones help(), exit(1); 37117da79e0SAndrew Jones } 37217da79e0SAndrew Jones 37317da79e0SAndrew Jones for (i = 0; i < vcpu_configs_n; ++i) { 37417da79e0SAndrew Jones c = vcpu_configs[i]; 37517da79e0SAndrew Jones if (sel && c != sel) 37617da79e0SAndrew Jones continue; 37717da79e0SAndrew Jones 37817da79e0SAndrew Jones pid = fork(); 37917da79e0SAndrew Jones 38017da79e0SAndrew Jones if (!pid) { 38117da79e0SAndrew Jones run_test(c); 38217da79e0SAndrew Jones exit(0); 38317da79e0SAndrew Jones } else { 38417da79e0SAndrew Jones int wstatus; 38517da79e0SAndrew Jones pid_t wpid = wait(&wstatus); 38617da79e0SAndrew Jones TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); 38717da79e0SAndrew Jones if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) 38817da79e0SAndrew Jones ret = KSFT_FAIL; 38917da79e0SAndrew Jones } 39017da79e0SAndrew Jones } 39117da79e0SAndrew Jones 39217da79e0SAndrew Jones return ret; 39317da79e0SAndrew Jones } 394