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, ®);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, ®);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