1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * arch_timer.c - Tests the aarch64 timer IRQ functionality 4 * 5 * The test validates both the virtual and physical timer IRQs using 6 * CVAL and TVAL registers. This consitutes the four stages in the test. 7 * The guest's main thread configures the timer interrupt for a stage 8 * and waits for it to fire, with a timeout equal to the timer period. 9 * It asserts that the timeout doesn't exceed the timer period. 10 * 11 * On the other hand, upon receipt of an interrupt, the guest's interrupt 12 * handler validates the interrupt by checking if the architectural state 13 * is in compliance with the specifications. 14 * 15 * The test provides command-line options to configure the timer's 16 * period (-p), number of vCPUs (-n), and iterations per stage (-i). 17 * To stress-test the timer stack even more, an option to migrate the 18 * vCPUs across pCPUs (-m), at a particular rate, is also provided. 19 * 20 * Copyright (c) 2021, Google LLC. 21 */ 22 23 #define _GNU_SOURCE 24 25 #include <stdlib.h> 26 #include <pthread.h> 27 #include <linux/kvm.h> 28 #include <linux/sizes.h> 29 #include <linux/bitmap.h> 30 #include <sys/sysinfo.h> 31 32 #include "kvm_util.h" 33 #include "processor.h" 34 #include "delay.h" 35 #include "arch_timer.h" 36 #include "gic.h" 37 #include "vgic.h" 38 39 #define NR_VCPUS_DEF 4 40 #define NR_TEST_ITERS_DEF 5 41 #define TIMER_TEST_PERIOD_MS_DEF 10 42 #define TIMER_TEST_ERR_MARGIN_US 100 43 #define TIMER_TEST_MIGRATION_FREQ_MS 2 44 45 struct test_args { 46 int nr_vcpus; 47 int nr_iter; 48 int timer_period_ms; 49 int migration_freq_ms; 50 }; 51 52 static struct test_args test_args = { 53 .nr_vcpus = NR_VCPUS_DEF, 54 .nr_iter = NR_TEST_ITERS_DEF, 55 .timer_period_ms = TIMER_TEST_PERIOD_MS_DEF, 56 .migration_freq_ms = TIMER_TEST_MIGRATION_FREQ_MS, 57 }; 58 59 #define msecs_to_usecs(msec) ((msec) * 1000LL) 60 61 #define GICD_BASE_GPA 0x8000000ULL 62 #define GICR_BASE_GPA 0x80A0000ULL 63 64 enum guest_stage { 65 GUEST_STAGE_VTIMER_CVAL = 1, 66 GUEST_STAGE_VTIMER_TVAL, 67 GUEST_STAGE_PTIMER_CVAL, 68 GUEST_STAGE_PTIMER_TVAL, 69 GUEST_STAGE_MAX, 70 }; 71 72 /* Shared variables between host and guest */ 73 struct test_vcpu_shared_data { 74 int nr_iter; 75 enum guest_stage guest_stage; 76 uint64_t xcnt; 77 }; 78 79 struct test_vcpu { 80 uint32_t vcpuid; 81 pthread_t pt_vcpu_run; 82 struct kvm_vm *vm; 83 }; 84 85 static struct test_vcpu test_vcpu[KVM_MAX_VCPUS]; 86 static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS]; 87 88 static int vtimer_irq, ptimer_irq; 89 90 static unsigned long *vcpu_done_map; 91 static pthread_mutex_t vcpu_done_map_lock; 92 93 static void 94 guest_configure_timer_action(struct test_vcpu_shared_data *shared_data) 95 { 96 switch (shared_data->guest_stage) { 97 case GUEST_STAGE_VTIMER_CVAL: 98 timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms); 99 shared_data->xcnt = timer_get_cntct(VIRTUAL); 100 timer_set_ctl(VIRTUAL, CTL_ENABLE); 101 break; 102 case GUEST_STAGE_VTIMER_TVAL: 103 timer_set_next_tval_ms(VIRTUAL, test_args.timer_period_ms); 104 shared_data->xcnt = timer_get_cntct(VIRTUAL); 105 timer_set_ctl(VIRTUAL, CTL_ENABLE); 106 break; 107 case GUEST_STAGE_PTIMER_CVAL: 108 timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms); 109 shared_data->xcnt = timer_get_cntct(PHYSICAL); 110 timer_set_ctl(PHYSICAL, CTL_ENABLE); 111 break; 112 case GUEST_STAGE_PTIMER_TVAL: 113 timer_set_next_tval_ms(PHYSICAL, test_args.timer_period_ms); 114 shared_data->xcnt = timer_get_cntct(PHYSICAL); 115 timer_set_ctl(PHYSICAL, CTL_ENABLE); 116 break; 117 default: 118 GUEST_ASSERT(0); 119 } 120 } 121 122 static void guest_validate_irq(unsigned int intid, 123 struct test_vcpu_shared_data *shared_data) 124 { 125 enum guest_stage stage = shared_data->guest_stage; 126 uint64_t xcnt = 0, xcnt_diff_us, cval = 0; 127 unsigned long xctl = 0; 128 unsigned int timer_irq = 0; 129 130 if (stage == GUEST_STAGE_VTIMER_CVAL || 131 stage == GUEST_STAGE_VTIMER_TVAL) { 132 xctl = timer_get_ctl(VIRTUAL); 133 timer_set_ctl(VIRTUAL, CTL_IMASK); 134 xcnt = timer_get_cntct(VIRTUAL); 135 cval = timer_get_cval(VIRTUAL); 136 timer_irq = vtimer_irq; 137 } else if (stage == GUEST_STAGE_PTIMER_CVAL || 138 stage == GUEST_STAGE_PTIMER_TVAL) { 139 xctl = timer_get_ctl(PHYSICAL); 140 timer_set_ctl(PHYSICAL, CTL_IMASK); 141 xcnt = timer_get_cntct(PHYSICAL); 142 cval = timer_get_cval(PHYSICAL); 143 timer_irq = ptimer_irq; 144 } else { 145 GUEST_ASSERT(0); 146 } 147 148 xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); 149 150 /* Make sure we are dealing with the correct timer IRQ */ 151 GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq); 152 153 /* Basic 'timer condition met' check */ 154 GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us); 155 GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl); 156 } 157 158 static void guest_irq_handler(struct ex_regs *regs) 159 { 160 unsigned int intid = gic_get_and_ack_irq(); 161 uint32_t cpu = guest_get_vcpuid(); 162 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 163 164 guest_validate_irq(intid, shared_data); 165 166 WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); 167 168 gic_set_eoi(intid); 169 } 170 171 static void guest_run_stage(struct test_vcpu_shared_data *shared_data, 172 enum guest_stage stage) 173 { 174 uint32_t irq_iter, config_iter; 175 176 shared_data->guest_stage = stage; 177 shared_data->nr_iter = 0; 178 179 for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { 180 /* Setup the next interrupt */ 181 guest_configure_timer_action(shared_data); 182 183 /* Setup a timeout for the interrupt to arrive */ 184 udelay(msecs_to_usecs(test_args.timer_period_ms) + 185 TIMER_TEST_ERR_MARGIN_US); 186 187 irq_iter = READ_ONCE(shared_data->nr_iter); 188 GUEST_ASSERT_2(config_iter + 1 == irq_iter, 189 config_iter + 1, irq_iter); 190 } 191 } 192 193 static void guest_code(void) 194 { 195 uint32_t cpu = guest_get_vcpuid(); 196 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 197 198 local_irq_disable(); 199 200 gic_init(GIC_V3, test_args.nr_vcpus, 201 (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA); 202 203 timer_set_ctl(VIRTUAL, CTL_IMASK); 204 timer_set_ctl(PHYSICAL, CTL_IMASK); 205 206 gic_irq_enable(vtimer_irq); 207 gic_irq_enable(ptimer_irq); 208 local_irq_enable(); 209 210 guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL); 211 guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL); 212 guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL); 213 guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL); 214 215 GUEST_DONE(); 216 } 217 218 static void *test_vcpu_run(void *arg) 219 { 220 struct ucall uc; 221 struct test_vcpu *vcpu = arg; 222 struct kvm_vm *vm = vcpu->vm; 223 uint32_t vcpuid = vcpu->vcpuid; 224 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpuid]; 225 226 vcpu_run(vm, vcpuid); 227 228 /* Currently, any exit from guest is an indication of completion */ 229 pthread_mutex_lock(&vcpu_done_map_lock); 230 set_bit(vcpuid, vcpu_done_map); 231 pthread_mutex_unlock(&vcpu_done_map_lock); 232 233 switch (get_ucall(vm, vcpuid, &uc)) { 234 case UCALL_SYNC: 235 case UCALL_DONE: 236 break; 237 case UCALL_ABORT: 238 sync_global_from_guest(vm, *shared_data); 239 TEST_FAIL("%s at %s:%ld\n\tvalues: %lu, %lu; %lu, vcpu: %u; stage: %u; iter: %u", 240 (const char *)uc.args[0], __FILE__, uc.args[1], 241 uc.args[2], uc.args[3], uc.args[4], vcpuid, 242 shared_data->guest_stage, shared_data->nr_iter); 243 break; 244 default: 245 TEST_FAIL("Unexpected guest exit\n"); 246 } 247 248 return NULL; 249 } 250 251 static uint32_t test_get_pcpu(void) 252 { 253 uint32_t pcpu; 254 unsigned int nproc_conf; 255 cpu_set_t online_cpuset; 256 257 nproc_conf = get_nprocs_conf(); 258 sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset); 259 260 /* Randomly find an available pCPU to place a vCPU on */ 261 do { 262 pcpu = rand() % nproc_conf; 263 } while (!CPU_ISSET(pcpu, &online_cpuset)); 264 265 return pcpu; 266 } 267 268 static int test_migrate_vcpu(struct test_vcpu *vcpu) 269 { 270 int ret; 271 cpu_set_t cpuset; 272 uint32_t new_pcpu = test_get_pcpu(); 273 274 CPU_ZERO(&cpuset); 275 CPU_SET(new_pcpu, &cpuset); 276 277 pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu->vcpuid, new_pcpu); 278 279 ret = pthread_setaffinity_np(vcpu->pt_vcpu_run, 280 sizeof(cpuset), &cpuset); 281 282 /* Allow the error where the vCPU thread is already finished */ 283 TEST_ASSERT(ret == 0 || ret == ESRCH, 284 "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n", 285 vcpu->vcpuid, new_pcpu, ret); 286 287 return ret; 288 } 289 290 static void *test_vcpu_migration(void *arg) 291 { 292 unsigned int i, n_done; 293 bool vcpu_done; 294 295 do { 296 usleep(msecs_to_usecs(test_args.migration_freq_ms)); 297 298 for (n_done = 0, i = 0; i < test_args.nr_vcpus; i++) { 299 pthread_mutex_lock(&vcpu_done_map_lock); 300 vcpu_done = test_bit(i, vcpu_done_map); 301 pthread_mutex_unlock(&vcpu_done_map_lock); 302 303 if (vcpu_done) { 304 n_done++; 305 continue; 306 } 307 308 test_migrate_vcpu(&test_vcpu[i]); 309 } 310 } while (test_args.nr_vcpus != n_done); 311 312 return NULL; 313 } 314 315 static void test_run(struct kvm_vm *vm) 316 { 317 int i, ret; 318 pthread_t pt_vcpu_migration; 319 320 pthread_mutex_init(&vcpu_done_map_lock, NULL); 321 vcpu_done_map = bitmap_zalloc(test_args.nr_vcpus); 322 TEST_ASSERT(vcpu_done_map, "Failed to allocate vcpu done bitmap\n"); 323 324 for (i = 0; i < test_args.nr_vcpus; i++) { 325 ret = pthread_create(&test_vcpu[i].pt_vcpu_run, NULL, 326 test_vcpu_run, &test_vcpu[i]); 327 TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i); 328 } 329 330 /* Spawn a thread to control the vCPU migrations */ 331 if (test_args.migration_freq_ms) { 332 srand(time(NULL)); 333 334 ret = pthread_create(&pt_vcpu_migration, NULL, 335 test_vcpu_migration, NULL); 336 TEST_ASSERT(!ret, "Failed to create the migration pthread\n"); 337 } 338 339 340 for (i = 0; i < test_args.nr_vcpus; i++) 341 pthread_join(test_vcpu[i].pt_vcpu_run, NULL); 342 343 if (test_args.migration_freq_ms) 344 pthread_join(pt_vcpu_migration, NULL); 345 346 bitmap_free(vcpu_done_map); 347 } 348 349 static void test_init_timer_irq(struct kvm_vm *vm) 350 { 351 /* Timer initid should be same for all the vCPUs, so query only vCPU-0 */ 352 int vcpu0_fd = vcpu_get_fd(vm, 0); 353 354 kvm_device_access(vcpu0_fd, KVM_ARM_VCPU_TIMER_CTRL, 355 KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq, false); 356 kvm_device_access(vcpu0_fd, KVM_ARM_VCPU_TIMER_CTRL, 357 KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq, false); 358 359 sync_global_to_guest(vm, ptimer_irq); 360 sync_global_to_guest(vm, vtimer_irq); 361 362 pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq); 363 } 364 365 static struct kvm_vm *test_vm_create(void) 366 { 367 struct kvm_vm *vm; 368 unsigned int i; 369 int ret; 370 int nr_vcpus = test_args.nr_vcpus; 371 372 vm = vm_create_default_with_vcpus(nr_vcpus, 0, 0, guest_code, NULL); 373 374 vm_init_descriptor_tables(vm); 375 vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler); 376 377 for (i = 0; i < nr_vcpus; i++) { 378 vcpu_init_descriptor_tables(vm, i); 379 380 test_vcpu[i].vcpuid = i; 381 test_vcpu[i].vm = vm; 382 } 383 384 ucall_init(vm, NULL); 385 test_init_timer_irq(vm); 386 ret = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA); 387 if (ret < 0) { 388 print_skip("Failed to create vgic-v3"); 389 exit(KSFT_SKIP); 390 } 391 392 /* Make all the test's cmdline args visible to the guest */ 393 sync_global_to_guest(vm, test_args); 394 395 return vm; 396 } 397 398 static void test_print_help(char *name) 399 { 400 pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n", 401 name); 402 pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n", 403 NR_VCPUS_DEF, KVM_MAX_VCPUS); 404 pr_info("\t-i: Number of iterations per stage (default: %u)\n", 405 NR_TEST_ITERS_DEF); 406 pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n", 407 TIMER_TEST_PERIOD_MS_DEF); 408 pr_info("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n", 409 TIMER_TEST_MIGRATION_FREQ_MS); 410 pr_info("\t-h: print this help screen\n"); 411 } 412 413 static bool parse_args(int argc, char *argv[]) 414 { 415 int opt; 416 417 while ((opt = getopt(argc, argv, "hn:i:p:m:")) != -1) { 418 switch (opt) { 419 case 'n': 420 test_args.nr_vcpus = atoi(optarg); 421 if (test_args.nr_vcpus <= 0) { 422 pr_info("Positive value needed for -n\n"); 423 goto err; 424 } else if (test_args.nr_vcpus > KVM_MAX_VCPUS) { 425 pr_info("Max allowed vCPUs: %u\n", 426 KVM_MAX_VCPUS); 427 goto err; 428 } 429 break; 430 case 'i': 431 test_args.nr_iter = atoi(optarg); 432 if (test_args.nr_iter <= 0) { 433 pr_info("Positive value needed for -i\n"); 434 goto err; 435 } 436 break; 437 case 'p': 438 test_args.timer_period_ms = atoi(optarg); 439 if (test_args.timer_period_ms <= 0) { 440 pr_info("Positive value needed for -p\n"); 441 goto err; 442 } 443 break; 444 case 'm': 445 test_args.migration_freq_ms = atoi(optarg); 446 if (test_args.migration_freq_ms < 0) { 447 pr_info("0 or positive value needed for -m\n"); 448 goto err; 449 } 450 break; 451 case 'h': 452 default: 453 goto err; 454 } 455 } 456 457 return true; 458 459 err: 460 test_print_help(argv[0]); 461 return false; 462 } 463 464 int main(int argc, char *argv[]) 465 { 466 struct kvm_vm *vm; 467 468 /* Tell stdout not to buffer its content */ 469 setbuf(stdout, NULL); 470 471 if (!parse_args(argc, argv)) 472 exit(KSFT_SKIP); 473 474 if (test_args.migration_freq_ms && get_nprocs() < 2) { 475 print_skip("At least two physical CPUs needed for vCPU migration"); 476 exit(KSFT_SKIP); 477 } 478 479 vm = test_vm_create(); 480 test_run(vm); 481 kvm_vm_free(vm); 482 483 return 0; 484 } 485