1 // SPDX-License-Identifier: GPL-2.0 2 /* 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 host 8 * with a newer kernel we allow the newer kernel on the destination to 9 * list new registers with get-reg-list. We assume they'll be unused, at 10 * least until the guest reboots, and so they're relatively harmless. 11 * However, if the destination host with the newer kernel is missing 12 * registers which the source host with the older kernel has, then that's 13 * a regression in get-reg-list. This test checks for that regression by 14 * checking the current list against a blessed list. We should never have 15 * missing registers, but if new ones appear then they can probably be 16 * added to the blessed list. A completely new blessed list can be created 17 * 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 31 static struct kvm_reg_list *reg_list; 32 static __u64 *blessed_reg, blessed_n; 33 34 extern struct vcpu_reg_list *vcpu_configs[]; 35 extern 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 57 static 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 84 bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) 85 { 86 return true; 87 } 88 89 bool __weak filter_reg(__u64 reg) 90 { 91 return false; 92 } 93 94 static 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 104 void __weak print_reg(const char *prefix, __u64 id) 105 { 106 printf("\t0x%llx,\n", id); 107 } 108 109 bool __weak check_reject_set(int err) 110 { 111 return true; 112 } 113 114 void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) 115 { 116 } 117 118 #ifdef __aarch64__ 119 static 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 128 static 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 #else 140 static 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 #endif 145 146 static 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 160 static bool print_list; 161 static bool print_filtered; 162 163 static 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 the 204 * same value. Some registers may allow other values to be written 205 * back, but others only allow some bits to be changed, and at least 206 * for ID registers set will fail if the value does not exactly match 207 * what was returned by get. If registers that allow other values to 208 * be written need to have the other values tested, then we should 209 * create a new set of tests for those in a new independent test 210 * 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 310 static 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 335 static 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 355 int 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 else 371 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.