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 static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; 80 static pthread_t pt_vcpu_run[KVM_MAX_VCPUS]; 81 static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS]; 82 83 static int vtimer_irq, ptimer_irq; 84 85 static unsigned long *vcpu_done_map; 86 static pthread_mutex_t vcpu_done_map_lock; 87 88 static void 89 guest_configure_timer_action(struct test_vcpu_shared_data *shared_data) 90 { 91 switch (shared_data->guest_stage) { 92 case GUEST_STAGE_VTIMER_CVAL: 93 timer_set_next_cval_ms(VIRTUAL, test_args.timer_period_ms); 94 shared_data->xcnt = timer_get_cntct(VIRTUAL); 95 timer_set_ctl(VIRTUAL, CTL_ENABLE); 96 break; 97 case GUEST_STAGE_VTIMER_TVAL: 98 timer_set_next_tval_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_PTIMER_CVAL: 103 timer_set_next_cval_ms(PHYSICAL, test_args.timer_period_ms); 104 shared_data->xcnt = timer_get_cntct(PHYSICAL); 105 timer_set_ctl(PHYSICAL, CTL_ENABLE); 106 break; 107 case GUEST_STAGE_PTIMER_TVAL: 108 timer_set_next_tval_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 default: 113 GUEST_ASSERT(0); 114 } 115 } 116 117 static void guest_validate_irq(unsigned int intid, 118 struct test_vcpu_shared_data *shared_data) 119 { 120 enum guest_stage stage = shared_data->guest_stage; 121 uint64_t xcnt = 0, xcnt_diff_us, cval = 0; 122 unsigned long xctl = 0; 123 unsigned int timer_irq = 0; 124 125 if (stage == GUEST_STAGE_VTIMER_CVAL || 126 stage == GUEST_STAGE_VTIMER_TVAL) { 127 xctl = timer_get_ctl(VIRTUAL); 128 timer_set_ctl(VIRTUAL, CTL_IMASK); 129 xcnt = timer_get_cntct(VIRTUAL); 130 cval = timer_get_cval(VIRTUAL); 131 timer_irq = vtimer_irq; 132 } else if (stage == GUEST_STAGE_PTIMER_CVAL || 133 stage == GUEST_STAGE_PTIMER_TVAL) { 134 xctl = timer_get_ctl(PHYSICAL); 135 timer_set_ctl(PHYSICAL, CTL_IMASK); 136 xcnt = timer_get_cntct(PHYSICAL); 137 cval = timer_get_cval(PHYSICAL); 138 timer_irq = ptimer_irq; 139 } else { 140 GUEST_ASSERT(0); 141 } 142 143 xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); 144 145 /* Make sure we are dealing with the correct timer IRQ */ 146 GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq); 147 148 /* Basic 'timer condition met' check */ 149 GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us); 150 GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl); 151 } 152 153 static void guest_irq_handler(struct ex_regs *regs) 154 { 155 unsigned int intid = gic_get_and_ack_irq(); 156 uint32_t cpu = guest_get_vcpuid(); 157 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 158 159 guest_validate_irq(intid, shared_data); 160 161 WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); 162 163 gic_set_eoi(intid); 164 } 165 166 static void guest_run_stage(struct test_vcpu_shared_data *shared_data, 167 enum guest_stage stage) 168 { 169 uint32_t irq_iter, config_iter; 170 171 shared_data->guest_stage = stage; 172 shared_data->nr_iter = 0; 173 174 for (config_iter = 0; config_iter < test_args.nr_iter; config_iter++) { 175 /* Setup the next interrupt */ 176 guest_configure_timer_action(shared_data); 177 178 /* Setup a timeout for the interrupt to arrive */ 179 udelay(msecs_to_usecs(test_args.timer_period_ms) + 180 TIMER_TEST_ERR_MARGIN_US); 181 182 irq_iter = READ_ONCE(shared_data->nr_iter); 183 GUEST_ASSERT_2(config_iter + 1 == irq_iter, 184 config_iter + 1, irq_iter); 185 } 186 } 187 188 static void guest_code(void) 189 { 190 uint32_t cpu = guest_get_vcpuid(); 191 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[cpu]; 192 193 local_irq_disable(); 194 195 gic_init(GIC_V3, test_args.nr_vcpus, 196 (void *)GICD_BASE_GPA, (void *)GICR_BASE_GPA); 197 198 timer_set_ctl(VIRTUAL, CTL_IMASK); 199 timer_set_ctl(PHYSICAL, CTL_IMASK); 200 201 gic_irq_enable(vtimer_irq); 202 gic_irq_enable(ptimer_irq); 203 local_irq_enable(); 204 205 guest_run_stage(shared_data, GUEST_STAGE_VTIMER_CVAL); 206 guest_run_stage(shared_data, GUEST_STAGE_VTIMER_TVAL); 207 guest_run_stage(shared_data, GUEST_STAGE_PTIMER_CVAL); 208 guest_run_stage(shared_data, GUEST_STAGE_PTIMER_TVAL); 209 210 GUEST_DONE(); 211 } 212 213 static void *test_vcpu_run(void *arg) 214 { 215 unsigned int vcpu_idx = (unsigned long)arg; 216 struct ucall uc; 217 struct kvm_vcpu *vcpu = vcpus[vcpu_idx]; 218 struct kvm_vm *vm = vcpu->vm; 219 struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpu_idx]; 220 221 vcpu_run(vcpu); 222 223 /* Currently, any exit from guest is an indication of completion */ 224 pthread_mutex_lock(&vcpu_done_map_lock); 225 set_bit(vcpu_idx, vcpu_done_map); 226 pthread_mutex_unlock(&vcpu_done_map_lock); 227 228 switch (get_ucall(vcpu, &uc)) { 229 case UCALL_SYNC: 230 case UCALL_DONE: 231 break; 232 case UCALL_ABORT: 233 sync_global_from_guest(vm, *shared_data); 234 REPORT_GUEST_ASSERT_N(uc, "values: %lu, %lu; %lu, vcpu %u; stage; %u; iter: %u", 235 GUEST_ASSERT_ARG(uc, 0), 236 GUEST_ASSERT_ARG(uc, 1), 237 GUEST_ASSERT_ARG(uc, 2), 238 vcpu_idx, 239 shared_data->guest_stage, 240 shared_data->nr_iter); 241 break; 242 default: 243 TEST_FAIL("Unexpected guest exit\n"); 244 } 245 246 return NULL; 247 } 248 249 static uint32_t test_get_pcpu(void) 250 { 251 uint32_t pcpu; 252 unsigned int nproc_conf; 253 cpu_set_t online_cpuset; 254 255 nproc_conf = get_nprocs_conf(); 256 sched_getaffinity(0, sizeof(cpu_set_t), &online_cpuset); 257 258 /* Randomly find an available pCPU to place a vCPU on */ 259 do { 260 pcpu = rand() % nproc_conf; 261 } while (!CPU_ISSET(pcpu, &online_cpuset)); 262 263 return pcpu; 264 } 265 266 static int test_migrate_vcpu(unsigned int vcpu_idx) 267 { 268 int ret; 269 cpu_set_t cpuset; 270 uint32_t new_pcpu = test_get_pcpu(); 271 272 CPU_ZERO(&cpuset); 273 CPU_SET(new_pcpu, &cpuset); 274 275 pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu_idx, new_pcpu); 276 277 ret = pthread_setaffinity_np(pt_vcpu_run[vcpu_idx], 278 sizeof(cpuset), &cpuset); 279 280 /* Allow the error where the vCPU thread is already finished */ 281 TEST_ASSERT(ret == 0 || ret == ESRCH, 282 "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n", 283 vcpu_idx, new_pcpu, ret); 284 285 return ret; 286 } 287 288 static void *test_vcpu_migration(void *arg) 289 { 290 unsigned int i, n_done; 291 bool vcpu_done; 292 293 do { 294 usleep(msecs_to_usecs(test_args.migration_freq_ms)); 295 296 for (n_done = 0, i = 0; i < test_args.nr_vcpus; i++) { 297 pthread_mutex_lock(&vcpu_done_map_lock); 298 vcpu_done = test_bit(i, vcpu_done_map); 299 pthread_mutex_unlock(&vcpu_done_map_lock); 300 301 if (vcpu_done) { 302 n_done++; 303 continue; 304 } 305 306 test_migrate_vcpu(i); 307 } 308 } while (test_args.nr_vcpus != n_done); 309 310 return NULL; 311 } 312 313 static void test_run(struct kvm_vm *vm) 314 { 315 pthread_t pt_vcpu_migration; 316 unsigned int i; 317 int ret; 318 319 pthread_mutex_init(&vcpu_done_map_lock, NULL); 320 vcpu_done_map = bitmap_zalloc(test_args.nr_vcpus); 321 TEST_ASSERT(vcpu_done_map, "Failed to allocate vcpu done bitmap\n"); 322 323 for (i = 0; i < (unsigned long)test_args.nr_vcpus; i++) { 324 ret = pthread_create(&pt_vcpu_run[i], NULL, test_vcpu_run, 325 (void *)(unsigned long)i); 326 TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i); 327 } 328 329 /* Spawn a thread to control the vCPU migrations */ 330 if (test_args.migration_freq_ms) { 331 srand(time(NULL)); 332 333 ret = pthread_create(&pt_vcpu_migration, NULL, 334 test_vcpu_migration, NULL); 335 TEST_ASSERT(!ret, "Failed to create the migration pthread\n"); 336 } 337 338 339 for (i = 0; i < test_args.nr_vcpus; i++) 340 pthread_join(pt_vcpu_run[i], NULL); 341 342 if (test_args.migration_freq_ms) 343 pthread_join(pt_vcpu_migration, NULL); 344 345 bitmap_free(vcpu_done_map); 346 } 347 348 static void test_init_timer_irq(struct kvm_vm *vm) 349 { 350 /* Timer initid should be same for all the vCPUs, so query only vCPU-0 */ 351 vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL, 352 KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq); 353 vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL, 354 KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq); 355 356 sync_global_to_guest(vm, ptimer_irq); 357 sync_global_to_guest(vm, vtimer_irq); 358 359 pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq); 360 } 361 362 static int gic_fd; 363 364 static struct kvm_vm *test_vm_create(void) 365 { 366 struct kvm_vm *vm; 367 unsigned int i; 368 int nr_vcpus = test_args.nr_vcpus; 369 370 vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); 371 372 vm_init_descriptor_tables(vm); 373 vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler); 374 375 for (i = 0; i < nr_vcpus; i++) 376 vcpu_init_descriptor_tables(vcpus[i]); 377 378 ucall_init(vm, NULL); 379 test_init_timer_irq(vm); 380 gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA); 381 __TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3"); 382 383 /* Make all the test's cmdline args visible to the guest */ 384 sync_global_to_guest(vm, test_args); 385 386 return vm; 387 } 388 389 static void test_vm_cleanup(struct kvm_vm *vm) 390 { 391 close(gic_fd); 392 kvm_vm_free(vm); 393 } 394 395 static void test_print_help(char *name) 396 { 397 pr_info("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n", 398 name); 399 pr_info("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n", 400 NR_VCPUS_DEF, KVM_MAX_VCPUS); 401 pr_info("\t-i: Number of iterations per stage (default: %u)\n", 402 NR_TEST_ITERS_DEF); 403 pr_info("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n", 404 TIMER_TEST_PERIOD_MS_DEF); 405 pr_info("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n", 406 TIMER_TEST_MIGRATION_FREQ_MS); 407 pr_info("\t-h: print this help screen\n"); 408 } 409 410 static bool parse_args(int argc, char *argv[]) 411 { 412 int opt; 413 414 while ((opt = getopt(argc, argv, "hn:i:p:m:")) != -1) { 415 switch (opt) { 416 case 'n': 417 test_args.nr_vcpus = atoi(optarg); 418 if (test_args.nr_vcpus <= 0) { 419 pr_info("Positive value needed for -n\n"); 420 goto err; 421 } else if (test_args.nr_vcpus > KVM_MAX_VCPUS) { 422 pr_info("Max allowed vCPUs: %u\n", 423 KVM_MAX_VCPUS); 424 goto err; 425 } 426 break; 427 case 'i': 428 test_args.nr_iter = atoi(optarg); 429 if (test_args.nr_iter <= 0) { 430 pr_info("Positive value needed for -i\n"); 431 goto err; 432 } 433 break; 434 case 'p': 435 test_args.timer_period_ms = atoi(optarg); 436 if (test_args.timer_period_ms <= 0) { 437 pr_info("Positive value needed for -p\n"); 438 goto err; 439 } 440 break; 441 case 'm': 442 test_args.migration_freq_ms = atoi(optarg); 443 if (test_args.migration_freq_ms < 0) { 444 pr_info("0 or positive value needed for -m\n"); 445 goto err; 446 } 447 break; 448 case 'h': 449 default: 450 goto err; 451 } 452 } 453 454 return true; 455 456 err: 457 test_print_help(argv[0]); 458 return false; 459 } 460 461 int main(int argc, char *argv[]) 462 { 463 struct kvm_vm *vm; 464 465 /* Tell stdout not to buffer its content */ 466 setbuf(stdout, NULL); 467 468 if (!parse_args(argc, argv)) 469 exit(KSFT_SKIP); 470 471 __TEST_REQUIRE(!test_args.migration_freq_ms || get_nprocs() >= 2, 472 "At least two physical CPUs needed for vCPU migration"); 473 474 vm = test_vm_create(); 475 test_run(vm); 476 test_vm_cleanup(vm); 477 478 return 0; 479 } 480