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_sublist(c, s) \ 38 for ((s) = &(c)->sublists[0]; (s)->regs; ++(s)) 39 40 #define for_each_reg(i) \ 41 for ((i) = 0; (i) < reg_list->n; ++(i)) 42 43 #define for_each_reg_filtered(i) \ 44 for_each_reg(i) \ 45 if (!filter_reg(reg_list->reg[i])) 46 47 #define for_each_missing_reg(i) \ 48 for ((i) = 0; (i) < blessed_n; ++(i)) \ 49 if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ 50 if (check_supported_reg(vcpu, blessed_reg[i])) 51 52 #define for_each_new_reg(i) \ 53 for_each_reg_filtered(i) \ 54 if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 55 56 static const char *config_name(struct vcpu_reg_list *c) 57 { 58 struct vcpu_reg_sublist *s; 59 int len = 0; 60 61 if (c->name) 62 return c->name; 63 64 for_each_sublist(c, s) 65 len += strlen(s->name) + 1; 66 67 c->name = malloc(len); 68 69 len = 0; 70 for_each_sublist(c, s) { 71 if (!strcmp(s->name, "base")) 72 continue; 73 strcat(c->name + len, s->name); 74 len += strlen(s->name) + 1; 75 c->name[len - 1] = '+'; 76 } 77 c->name[len - 1] = '\0'; 78 79 return c->name; 80 } 81 82 bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) 83 { 84 return true; 85 } 86 87 bool __weak filter_reg(__u64 reg) 88 { 89 return false; 90 } 91 92 static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) 93 { 94 int i; 95 96 for (i = 0; i < nr_regs; ++i) 97 if (reg == regs[i]) 98 return true; 99 return false; 100 } 101 102 void __weak print_reg(const char *prefix, __u64 id) 103 { 104 printf("\t0x%llx,\n", id); 105 } 106 107 static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init) 108 { 109 struct vcpu_reg_sublist *s; 110 111 for_each_sublist(c, s) 112 if (s->capability) 113 init->features[s->feature / 32] |= 1 << (s->feature % 32); 114 } 115 116 static void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) 117 { 118 struct vcpu_reg_sublist *s; 119 int feature; 120 121 for_each_sublist(c, s) { 122 if (s->finalize) { 123 feature = s->feature; 124 vcpu_ioctl(vcpu, KVM_ARM_VCPU_FINALIZE, &feature); 125 } 126 } 127 } 128 129 static void check_supported(struct vcpu_reg_list *c) 130 { 131 struct vcpu_reg_sublist *s; 132 133 for_each_sublist(c, s) { 134 if (!s->capability) 135 continue; 136 137 __TEST_REQUIRE(kvm_has_cap(s->capability), 138 "%s: %s not available, skipping tests\n", 139 config_name(c), s->name); 140 } 141 } 142 143 static bool print_list; 144 static bool print_filtered; 145 146 static void run_test(struct vcpu_reg_list *c) 147 { 148 struct kvm_vcpu_init init = { .target = -1, }; 149 int new_regs = 0, missing_regs = 0, i, n; 150 int failed_get = 0, failed_set = 0, failed_reject = 0; 151 struct kvm_vcpu *vcpu; 152 struct kvm_vm *vm; 153 struct vcpu_reg_sublist *s; 154 155 check_supported(c); 156 157 vm = vm_create_barebones(); 158 prepare_vcpu_init(c, &init); 159 vcpu = __vm_vcpu_add(vm, 0); 160 aarch64_vcpu_setup(vcpu, &init); 161 finalize_vcpu(vcpu, c); 162 163 reg_list = vcpu_get_reg_list(vcpu); 164 165 if (print_list || print_filtered) { 166 putchar('\n'); 167 for_each_reg(i) { 168 __u64 id = reg_list->reg[i]; 169 if ((print_list && !filter_reg(id)) || 170 (print_filtered && filter_reg(id))) 171 print_reg(config_name(c), id); 172 } 173 putchar('\n'); 174 return; 175 } 176 177 /* 178 * We only test that we can get the register and then write back the 179 * same value. Some registers may allow other values to be written 180 * back, but others only allow some bits to be changed, and at least 181 * for ID registers set will fail if the value does not exactly match 182 * what was returned by get. If registers that allow other values to 183 * be written need to have the other values tested, then we should 184 * create a new set of tests for those in a new independent test 185 * executable. 186 */ 187 for_each_reg(i) { 188 uint8_t addr[2048 / 8]; 189 struct kvm_one_reg reg = { 190 .id = reg_list->reg[i], 191 .addr = (__u64)&addr, 192 }; 193 bool reject_reg = false; 194 int ret; 195 196 ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); 197 if (ret) { 198 printf("%s: Failed to get ", config_name(c)); 199 print_reg(config_name(c), reg.id); 200 putchar('\n'); 201 ++failed_get; 202 } 203 204 /* rejects_set registers are rejected after KVM_ARM_VCPU_FINALIZE */ 205 for_each_sublist(c, s) { 206 if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { 207 reject_reg = true; 208 ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 209 if (ret != -1 || errno != EPERM) { 210 printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); 211 print_reg(config_name(c), reg.id); 212 putchar('\n'); 213 ++failed_reject; 214 } 215 break; 216 } 217 } 218 219 if (!reject_reg) { 220 ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 221 if (ret) { 222 printf("%s: Failed to set ", config_name(c)); 223 print_reg(config_name(c), reg.id); 224 putchar('\n'); 225 ++failed_set; 226 } 227 } 228 } 229 230 for_each_sublist(c, s) 231 blessed_n += s->regs_n; 232 blessed_reg = calloc(blessed_n, sizeof(__u64)); 233 234 n = 0; 235 for_each_sublist(c, s) { 236 for (i = 0; i < s->regs_n; ++i) 237 blessed_reg[n++] = s->regs[i]; 238 } 239 240 for_each_new_reg(i) 241 ++new_regs; 242 243 for_each_missing_reg(i) 244 ++missing_regs; 245 246 if (new_regs || missing_regs) { 247 n = 0; 248 for_each_reg_filtered(i) 249 ++n; 250 251 printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); 252 printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", 253 config_name(c), reg_list->n, reg_list->n - n); 254 } 255 256 if (new_regs) { 257 printf("\n%s: There are %d new registers.\n" 258 "Consider adding them to the blessed reg " 259 "list with the following lines:\n\n", config_name(c), new_regs); 260 for_each_new_reg(i) 261 print_reg(config_name(c), reg_list->reg[i]); 262 putchar('\n'); 263 } 264 265 if (missing_regs) { 266 printf("\n%s: There are %d missing registers.\n" 267 "The following lines are missing registers:\n\n", config_name(c), missing_regs); 268 for_each_missing_reg(i) 269 print_reg(config_name(c), blessed_reg[i]); 270 putchar('\n'); 271 } 272 273 TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, 274 "%s: There are %d missing registers; " 275 "%d registers failed get; %d registers failed set; %d registers failed reject", 276 config_name(c), missing_regs, failed_get, failed_set, failed_reject); 277 278 pr_info("%s: PASS\n", config_name(c)); 279 blessed_n = 0; 280 free(blessed_reg); 281 free(reg_list); 282 kvm_vm_free(vm); 283 } 284 285 static void help(void) 286 { 287 struct vcpu_reg_list *c; 288 int i; 289 290 printf( 291 "\n" 292 "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n" 293 " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" 294 " '<selection>' may be\n"); 295 296 for (i = 0; i < vcpu_configs_n; ++i) { 297 c = vcpu_configs[i]; 298 printf( 299 " '%s'\n", config_name(c)); 300 } 301 302 printf( 303 "\n" 304 " --list Print the register list rather than test it (requires --config)\n" 305 " --list-filtered Print registers that would normally be filtered out (requires --config)\n" 306 "\n" 307 ); 308 } 309 310 static struct vcpu_reg_list *parse_config(const char *config) 311 { 312 struct vcpu_reg_list *c = NULL; 313 int i; 314 315 if (config[8] != '=') 316 help(), exit(1); 317 318 for (i = 0; i < vcpu_configs_n; ++i) { 319 c = vcpu_configs[i]; 320 if (strcmp(config_name(c), &config[9]) == 0) 321 break; 322 } 323 324 if (i == vcpu_configs_n) 325 help(), exit(1); 326 327 return c; 328 } 329 330 int main(int ac, char **av) 331 { 332 struct vcpu_reg_list *c, *sel = NULL; 333 int i, ret = 0; 334 pid_t pid; 335 336 for (i = 1; i < ac; ++i) { 337 if (strncmp(av[i], "--config", 8) == 0) 338 sel = parse_config(av[i]); 339 else if (strcmp(av[i], "--list") == 0) 340 print_list = true; 341 else if (strcmp(av[i], "--list-filtered") == 0) 342 print_filtered = true; 343 else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) 344 help(), exit(0); 345 else 346 help(), exit(1); 347 } 348 349 if (print_list || print_filtered) { 350 /* 351 * We only want to print the register list of a single config. 352 */ 353 if (!sel) 354 help(), exit(1); 355 } 356 357 for (i = 0; i < vcpu_configs_n; ++i) { 358 c = vcpu_configs[i]; 359 if (sel && c != sel) 360 continue; 361 362 pid = fork(); 363 364 if (!pid) { 365 run_test(c); 366 exit(0); 367 } else { 368 int wstatus; 369 pid_t wpid = wait(&wstatus); 370 TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); 371 if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) 372 ret = KSFT_FAIL; 373 } 374 } 375 376 return ret; 377 } 378