diff options
Diffstat (limited to 'tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c')
-rw-r--r-- | tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c | 1062 |
1 files changed, 0 insertions, 1062 deletions
diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c b/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c deleted file mode 100644 index a36a7e2db434..000000000000 --- a/tools/testing/selftests/kvm/aarch64/arch_timer_edge_cases.c +++ /dev/null @@ -1,1062 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * arch_timer_edge_cases.c - Tests the aarch64 timer IRQ functionality. - * - * The test validates some edge cases related to the arch-timer: - * - timers above the max TVAL value. - * - timers in the past - * - moving counters ahead and behind pending timers. - * - reprograming timers. - * - timers fired multiple times. - * - masking/unmasking using the timer control mask. - * - * Copyright (c) 2021, Google LLC. - */ - -#define _GNU_SOURCE - -#include <pthread.h> -#include <sys/sysinfo.h> - -#include "arch_timer.h" -#include "gic.h" -#include "vgic.h" - -static const uint64_t CVAL_MAX = ~0ULL; -/* tval is a signed 32-bit int. */ -static const int32_t TVAL_MAX = INT32_MAX; -static const int32_t TVAL_MIN = INT32_MIN; - -/* After how much time we say there is no IRQ. */ -static const uint32_t TIMEOUT_NO_IRQ_US = 50000; - -/* A nice counter value to use as the starting one for most tests. */ -static const uint64_t DEF_CNT = (CVAL_MAX / 2); - -/* Number of runs. */ -static const uint32_t NR_TEST_ITERS_DEF = 5; - -/* Default wait test time in ms. */ -static const uint32_t WAIT_TEST_MS = 10; - -/* Default "long" wait test time in ms. */ -static const uint32_t LONG_WAIT_TEST_MS = 100; - -/* Shared with IRQ handler. */ -struct test_vcpu_shared_data { - atomic_t handled; - atomic_t spurious; -} shared_data; - -struct test_args { - /* Virtual or physical timer and counter tests. */ - enum arch_timer timer; - /* Delay used for most timer tests. */ - uint64_t wait_ms; - /* Delay used in the test_long_timer_delays test. */ - uint64_t long_wait_ms; - /* Number of iterations. */ - int iterations; - /* Whether to test the physical timer. */ - bool test_physical; - /* Whether to test the virtual timer. */ - bool test_virtual; -}; - -struct test_args test_args = { - .wait_ms = WAIT_TEST_MS, - .long_wait_ms = LONG_WAIT_TEST_MS, - .iterations = NR_TEST_ITERS_DEF, - .test_physical = true, - .test_virtual = true, -}; - -static int vtimer_irq, ptimer_irq; - -enum sync_cmd { - SET_COUNTER_VALUE, - USERSPACE_USLEEP, - USERSPACE_SCHED_YIELD, - USERSPACE_MIGRATE_SELF, - NO_USERSPACE_CMD, -}; - -typedef void (*sleep_method_t)(enum arch_timer timer, uint64_t usec); - -static void sleep_poll(enum arch_timer timer, uint64_t usec); -static void sleep_sched_poll(enum arch_timer timer, uint64_t usec); -static void sleep_in_userspace(enum arch_timer timer, uint64_t usec); -static void sleep_migrate(enum arch_timer timer, uint64_t usec); - -sleep_method_t sleep_method[] = { - sleep_poll, - sleep_sched_poll, - sleep_migrate, - sleep_in_userspace, -}; - -typedef void (*irq_wait_method_t)(void); - -static void wait_for_non_spurious_irq(void); -static void wait_poll_for_irq(void); -static void wait_sched_poll_for_irq(void); -static void wait_migrate_poll_for_irq(void); - -irq_wait_method_t irq_wait_method[] = { - wait_for_non_spurious_irq, - wait_poll_for_irq, - wait_sched_poll_for_irq, - wait_migrate_poll_for_irq, -}; - -enum timer_view { - TIMER_CVAL, - TIMER_TVAL, -}; - -static void assert_irqs_handled(uint32_t n) -{ - int h = atomic_read(&shared_data.handled); - - __GUEST_ASSERT(h == n, "Handled %d IRQS but expected %d", h, n); -} - -static void userspace_cmd(uint64_t cmd) -{ - GUEST_SYNC_ARGS(cmd, 0, 0, 0, 0); -} - -static void userspace_migrate_vcpu(void) -{ - userspace_cmd(USERSPACE_MIGRATE_SELF); -} - -static void userspace_sleep(uint64_t usecs) -{ - GUEST_SYNC_ARGS(USERSPACE_USLEEP, usecs, 0, 0, 0); -} - -static void set_counter(enum arch_timer timer, uint64_t counter) -{ - GUEST_SYNC_ARGS(SET_COUNTER_VALUE, counter, timer, 0, 0); -} - -static void guest_irq_handler(struct ex_regs *regs) -{ - unsigned int intid = gic_get_and_ack_irq(); - enum arch_timer timer; - uint64_t cnt, cval; - uint32_t ctl; - bool timer_condition, istatus; - - if (intid == IAR_SPURIOUS) { - atomic_inc(&shared_data.spurious); - goto out; - } - - if (intid == ptimer_irq) - timer = PHYSICAL; - else if (intid == vtimer_irq) - timer = VIRTUAL; - else - goto out; - - ctl = timer_get_ctl(timer); - cval = timer_get_cval(timer); - cnt = timer_get_cntct(timer); - timer_condition = cnt >= cval; - istatus = (ctl & CTL_ISTATUS) && (ctl & CTL_ENABLE); - GUEST_ASSERT_EQ(timer_condition, istatus); - - /* Disable and mask the timer. */ - timer_set_ctl(timer, CTL_IMASK); - - atomic_inc(&shared_data.handled); - -out: - gic_set_eoi(intid); -} - -static void set_cval_irq(enum arch_timer timer, uint64_t cval_cycles, - uint32_t ctl) -{ - atomic_set(&shared_data.handled, 0); - atomic_set(&shared_data.spurious, 0); - timer_set_cval(timer, cval_cycles); - timer_set_ctl(timer, ctl); -} - -static void set_tval_irq(enum arch_timer timer, uint64_t tval_cycles, - uint32_t ctl) -{ - atomic_set(&shared_data.handled, 0); - atomic_set(&shared_data.spurious, 0); - timer_set_ctl(timer, ctl); - timer_set_tval(timer, tval_cycles); -} - -static void set_xval_irq(enum arch_timer timer, uint64_t xval, uint32_t ctl, - enum timer_view tv) -{ - switch (tv) { - case TIMER_CVAL: - set_cval_irq(timer, xval, ctl); - break; - case TIMER_TVAL: - set_tval_irq(timer, xval, ctl); - break; - default: - GUEST_FAIL("Could not get timer %d", timer); - } -} - -/* - * Note that this can theoretically hang forever, so we rely on having - * a timeout mechanism in the "runner", like: - * tools/testing/selftests/kselftest/runner.sh. - */ -static void wait_for_non_spurious_irq(void) -{ - int h; - - local_irq_disable(); - - for (h = atomic_read(&shared_data.handled); h == atomic_read(&shared_data.handled);) { - wfi(); - local_irq_enable(); - isb(); /* handle IRQ */ - local_irq_disable(); - } -} - -/* - * Wait for an non-spurious IRQ by polling in the guest or in - * userspace (e.g. userspace_cmd=USERSPACE_SCHED_YIELD). - * - * Note that this can theoretically hang forever, so we rely on having - * a timeout mechanism in the "runner", like: - * tools/testing/selftests/kselftest/runner.sh. - */ -static void poll_for_non_spurious_irq(enum sync_cmd usp_cmd) -{ - int h; - - local_irq_disable(); - - h = atomic_read(&shared_data.handled); - - local_irq_enable(); - while (h == atomic_read(&shared_data.handled)) { - if (usp_cmd == NO_USERSPACE_CMD) - cpu_relax(); - else - userspace_cmd(usp_cmd); - } - local_irq_disable(); -} - -static void wait_poll_for_irq(void) -{ - poll_for_non_spurious_irq(NO_USERSPACE_CMD); -} - -static void wait_sched_poll_for_irq(void) -{ - poll_for_non_spurious_irq(USERSPACE_SCHED_YIELD); -} - -static void wait_migrate_poll_for_irq(void) -{ - poll_for_non_spurious_irq(USERSPACE_MIGRATE_SELF); -} - -/* - * Sleep for usec microseconds by polling in the guest or in - * userspace (e.g. userspace_cmd=USERSPACE_SCHEDULE). - */ -static void guest_poll(enum arch_timer test_timer, uint64_t usec, - enum sync_cmd usp_cmd) -{ - uint64_t cycles = usec_to_cycles(usec); - /* Whichever timer we are testing with, sleep with the other. */ - enum arch_timer sleep_timer = 1 - test_timer; - uint64_t start = timer_get_cntct(sleep_timer); - - while ((timer_get_cntct(sleep_timer) - start) < cycles) { - if (usp_cmd == NO_USERSPACE_CMD) - cpu_relax(); - else - userspace_cmd(usp_cmd); - } -} - -static void sleep_poll(enum arch_timer timer, uint64_t usec) -{ - guest_poll(timer, usec, NO_USERSPACE_CMD); -} - -static void sleep_sched_poll(enum arch_timer timer, uint64_t usec) -{ - guest_poll(timer, usec, USERSPACE_SCHED_YIELD); -} - -static void sleep_migrate(enum arch_timer timer, uint64_t usec) -{ - guest_poll(timer, usec, USERSPACE_MIGRATE_SELF); -} - -static void sleep_in_userspace(enum arch_timer timer, uint64_t usec) -{ - userspace_sleep(usec); -} - -/* - * Reset the timer state to some nice values like the counter not being close - * to the edge, and the control register masked and disabled. - */ -static void reset_timer_state(enum arch_timer timer, uint64_t cnt) -{ - set_counter(timer, cnt); - timer_set_ctl(timer, CTL_IMASK); -} - -static void test_timer_xval(enum arch_timer timer, uint64_t xval, - enum timer_view tv, irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) -{ - local_irq_disable(); - - if (reset_state) - reset_timer_state(timer, reset_cnt); - - set_xval_irq(timer, xval, CTL_ENABLE, tv); - - /* This method re-enables IRQs to handle the one we're looking for. */ - wm(); - - assert_irqs_handled(1); - local_irq_enable(); -} - -/* - * The test_timer_* functions will program the timer, wait for it, and assert - * the firing of the correct IRQ. - * - * These functions don't have a timeout and return as soon as they receive an - * IRQ. They can hang (forever), so we rely on having a timeout mechanism in - * the "runner", like: tools/testing/selftests/kselftest/runner.sh. - */ - -static void test_timer_cval(enum arch_timer timer, uint64_t cval, - irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) -{ - test_timer_xval(timer, cval, TIMER_CVAL, wm, reset_state, reset_cnt); -} - -static void test_timer_tval(enum arch_timer timer, int32_t tval, - irq_wait_method_t wm, bool reset_state, - uint64_t reset_cnt) -{ - test_timer_xval(timer, (uint64_t) tval, TIMER_TVAL, wm, reset_state, - reset_cnt); -} - -static void test_xval_check_no_irq(enum arch_timer timer, uint64_t xval, - uint64_t usec, enum timer_view timer_view, - sleep_method_t guest_sleep) -{ - local_irq_disable(); - - set_xval_irq(timer, xval, CTL_ENABLE | CTL_IMASK, timer_view); - guest_sleep(timer, usec); - - local_irq_enable(); - isb(); - - /* Assume success (no IRQ) after waiting usec microseconds */ - assert_irqs_handled(0); -} - -static void test_cval_no_irq(enum arch_timer timer, uint64_t cval, - uint64_t usec, sleep_method_t wm) -{ - test_xval_check_no_irq(timer, cval, usec, TIMER_CVAL, wm); -} - -static void test_tval_no_irq(enum arch_timer timer, int32_t tval, uint64_t usec, - sleep_method_t wm) -{ - /* tval will be cast to an int32_t in test_xval_check_no_irq */ - test_xval_check_no_irq(timer, (uint64_t) tval, usec, TIMER_TVAL, wm); -} - -/* Test masking/unmasking a timer using the timer mask (not the IRQ mask). */ -static void test_timer_control_mask_then_unmask(enum arch_timer timer) -{ - reset_timer_state(timer, DEF_CNT); - set_tval_irq(timer, -1, CTL_ENABLE | CTL_IMASK); - - /* Unmask the timer, and then get an IRQ. */ - local_irq_disable(); - timer_set_ctl(timer, CTL_ENABLE); - /* This method re-enables IRQs to handle the one we're looking for. */ - wait_for_non_spurious_irq(); - - assert_irqs_handled(1); - local_irq_enable(); -} - -/* Check that timer control masks actually mask a timer being fired. */ -static void test_timer_control_masks(enum arch_timer timer) -{ - reset_timer_state(timer, DEF_CNT); - - /* Local IRQs are not masked at this point. */ - - set_tval_irq(timer, -1, CTL_ENABLE | CTL_IMASK); - - /* Assume no IRQ after waiting TIMEOUT_NO_IRQ_US microseconds */ - sleep_poll(timer, TIMEOUT_NO_IRQ_US); - - assert_irqs_handled(0); - timer_set_ctl(timer, CTL_IMASK); -} - -static void test_fire_a_timer_multiple_times(enum arch_timer timer, - irq_wait_method_t wm, int num) -{ - int i; - - local_irq_disable(); - reset_timer_state(timer, DEF_CNT); - - set_tval_irq(timer, 0, CTL_ENABLE); - - for (i = 1; i <= num; i++) { - /* This method re-enables IRQs to handle the one we're looking for. */ - wm(); - - /* The IRQ handler masked and disabled the timer. - * Enable and unmmask it again. - */ - timer_set_ctl(timer, CTL_ENABLE); - - assert_irqs_handled(i); - } - - local_irq_enable(); -} - -static void test_timers_fired_multiple_times(enum arch_timer timer) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) - test_fire_a_timer_multiple_times(timer, irq_wait_method[i], 10); -} - -/* - * Set a timer for tval=delta_1_ms then reprogram it to - * tval=delta_2_ms. Check that we get the timer fired. There is no - * timeout for the wait: we use the wfi instruction. - */ -static void test_reprogramming_timer(enum arch_timer timer, irq_wait_method_t wm, - int32_t delta_1_ms, int32_t delta_2_ms) -{ - local_irq_disable(); - reset_timer_state(timer, DEF_CNT); - - /* Program the timer to DEF_CNT + delta_1_ms. */ - set_tval_irq(timer, msec_to_cycles(delta_1_ms), CTL_ENABLE); - - /* Reprogram the timer to DEF_CNT + delta_2_ms. */ - timer_set_tval(timer, msec_to_cycles(delta_2_ms)); - - /* This method re-enables IRQs to handle the one we're looking for. */ - wm(); - - /* The IRQ should arrive at DEF_CNT + delta_2_ms (or after). */ - GUEST_ASSERT(timer_get_cntct(timer) >= - DEF_CNT + msec_to_cycles(delta_2_ms)); - - local_irq_enable(); - assert_irqs_handled(1); -}; - -static void test_reprogram_timers(enum arch_timer timer) -{ - int i; - uint64_t base_wait = test_args.wait_ms; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - /* - * Ensure reprogramming works whether going from a - * longer time to a shorter or vice versa. - */ - test_reprogramming_timer(timer, irq_wait_method[i], 2 * base_wait, - base_wait); - test_reprogramming_timer(timer, irq_wait_method[i], base_wait, - 2 * base_wait); - } -} - -static void test_basic_functionality(enum arch_timer timer) -{ - int32_t tval = (int32_t) msec_to_cycles(test_args.wait_ms); - uint64_t cval = DEF_CNT + msec_to_cycles(test_args.wait_ms); - int i; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - irq_wait_method_t wm = irq_wait_method[i]; - - test_timer_cval(timer, cval, wm, true, DEF_CNT); - test_timer_tval(timer, tval, wm, true, DEF_CNT); - } -} - -/* - * This test checks basic timer behavior without actually firing timers, things - * like: the relationship between cval and tval, tval down-counting. - */ -static void timers_sanity_checks(enum arch_timer timer, bool use_sched) -{ - reset_timer_state(timer, DEF_CNT); - - local_irq_disable(); - - /* cval in the past */ - timer_set_cval(timer, - timer_get_cntct(timer) - - msec_to_cycles(test_args.wait_ms)); - if (use_sched) - userspace_migrate_vcpu(); - GUEST_ASSERT(timer_get_tval(timer) < 0); - - /* tval in the past */ - timer_set_tval(timer, -1); - if (use_sched) - userspace_migrate_vcpu(); - GUEST_ASSERT(timer_get_cval(timer) < timer_get_cntct(timer)); - - /* tval larger than TVAL_MAX. This requires programming with - * timer_set_cval instead so the value is expressible - */ - timer_set_cval(timer, - timer_get_cntct(timer) + TVAL_MAX + - msec_to_cycles(test_args.wait_ms)); - if (use_sched) - userspace_migrate_vcpu(); - GUEST_ASSERT(timer_get_tval(timer) <= 0); - - /* - * tval larger than 2 * TVAL_MAX. - * Twice the TVAL_MAX completely loops around the TVAL. - */ - timer_set_cval(timer, - timer_get_cntct(timer) + 2ULL * TVAL_MAX + - msec_to_cycles(test_args.wait_ms)); - if (use_sched) - userspace_migrate_vcpu(); - GUEST_ASSERT(timer_get_tval(timer) <= - msec_to_cycles(test_args.wait_ms)); - - /* negative tval that rollovers from 0. */ - set_counter(timer, msec_to_cycles(1)); - timer_set_tval(timer, -1 * msec_to_cycles(test_args.wait_ms)); - if (use_sched) - userspace_migrate_vcpu(); - GUEST_ASSERT(timer_get_cval(timer) >= (CVAL_MAX - msec_to_cycles(test_args.wait_ms))); - - /* tval should keep down-counting from 0 to -1. */ - timer_set_tval(timer, 0); - sleep_poll(timer, 1); - GUEST_ASSERT(timer_get_tval(timer) < 0); - - local_irq_enable(); - - /* Mask and disable any pending timer. */ - timer_set_ctl(timer, CTL_IMASK); -} - -static void test_timers_sanity_checks(enum arch_timer timer) -{ - timers_sanity_checks(timer, false); - /* Check how KVM saves/restores these edge-case values. */ - timers_sanity_checks(timer, true); -} - -static void test_set_cnt_after_tval_max(enum arch_timer timer, irq_wait_method_t wm) -{ - local_irq_disable(); - reset_timer_state(timer, DEF_CNT); - - set_cval_irq(timer, - (uint64_t) TVAL_MAX + - msec_to_cycles(test_args.wait_ms) / 2, CTL_ENABLE); - - set_counter(timer, TVAL_MAX); - - /* This method re-enables IRQs to handle the one we're looking for. */ - wm(); - - assert_irqs_handled(1); - local_irq_enable(); -} - -/* Test timers set for: cval = now + TVAL_MAX + wait_ms / 2 */ -static void test_timers_above_tval_max(enum arch_timer timer) -{ - uint64_t cval; - int i; - - /* - * Test that the system is not implementing cval in terms of - * tval. If that was the case, setting a cval to "cval = now - * + TVAL_MAX + wait_ms" would wrap to "cval = now + - * wait_ms", and the timer would fire immediately. Test that it - * doesn't. - */ - for (i = 0; i < ARRAY_SIZE(sleep_method); i++) { - reset_timer_state(timer, DEF_CNT); - cval = timer_get_cntct(timer) + TVAL_MAX + - msec_to_cycles(test_args.wait_ms); - test_cval_no_irq(timer, cval, - msecs_to_usecs(test_args.wait_ms) + - TIMEOUT_NO_IRQ_US, sleep_method[i]); - } - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - /* Get the IRQ by moving the counter forward. */ - test_set_cnt_after_tval_max(timer, irq_wait_method[i]); - } -} - -/* - * Template function to be used by the test_move_counter_ahead_* tests. It - * sets the counter to cnt_1, the [c|t]val, the counter to cnt_2, and - * then waits for an IRQ. - */ -static void test_set_cnt_after_xval(enum arch_timer timer, uint64_t cnt_1, - uint64_t xval, uint64_t cnt_2, - irq_wait_method_t wm, enum timer_view tv) -{ - local_irq_disable(); - - set_counter(timer, cnt_1); - timer_set_ctl(timer, CTL_IMASK); - - set_xval_irq(timer, xval, CTL_ENABLE, tv); - set_counter(timer, cnt_2); - /* This method re-enables IRQs to handle the one we're looking for. */ - wm(); - - assert_irqs_handled(1); - local_irq_enable(); -} - -/* - * Template function to be used by the test_move_counter_ahead_* tests. It - * sets the counter to cnt_1, the [c|t]val, the counter to cnt_2, and - * then waits for an IRQ. - */ -static void test_set_cnt_after_xval_no_irq(enum arch_timer timer, - uint64_t cnt_1, uint64_t xval, - uint64_t cnt_2, - sleep_method_t guest_sleep, - enum timer_view tv) -{ - local_irq_disable(); - - set_counter(timer, cnt_1); - timer_set_ctl(timer, CTL_IMASK); - - set_xval_irq(timer, xval, CTL_ENABLE, tv); - set_counter(timer, cnt_2); - guest_sleep(timer, TIMEOUT_NO_IRQ_US); - - local_irq_enable(); - isb(); - - /* Assume no IRQ after waiting TIMEOUT_NO_IRQ_US microseconds */ - assert_irqs_handled(0); - timer_set_ctl(timer, CTL_IMASK); -} - -static void test_set_cnt_after_tval(enum arch_timer timer, uint64_t cnt_1, - int32_t tval, uint64_t cnt_2, - irq_wait_method_t wm) -{ - test_set_cnt_after_xval(timer, cnt_1, tval, cnt_2, wm, TIMER_TVAL); -} - -static void test_set_cnt_after_cval(enum arch_timer timer, uint64_t cnt_1, - uint64_t cval, uint64_t cnt_2, - irq_wait_method_t wm) -{ - test_set_cnt_after_xval(timer, cnt_1, cval, cnt_2, wm, TIMER_CVAL); -} - -static void test_set_cnt_after_tval_no_irq(enum arch_timer timer, - uint64_t cnt_1, int32_t tval, - uint64_t cnt_2, sleep_method_t wm) -{ - test_set_cnt_after_xval_no_irq(timer, cnt_1, tval, cnt_2, wm, - TIMER_TVAL); -} - -static void test_set_cnt_after_cval_no_irq(enum arch_timer timer, - uint64_t cnt_1, uint64_t cval, - uint64_t cnt_2, sleep_method_t wm) -{ - test_set_cnt_after_xval_no_irq(timer, cnt_1, cval, cnt_2, wm, - TIMER_CVAL); -} - -/* Set a timer and then move the counter ahead of it. */ -static void test_move_counters_ahead_of_timers(enum arch_timer timer) -{ - int i; - int32_t tval; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - irq_wait_method_t wm = irq_wait_method[i]; - - test_set_cnt_after_cval(timer, 0, DEF_CNT, DEF_CNT + 1, wm); - test_set_cnt_after_cval(timer, CVAL_MAX, 1, 2, wm); - - /* Move counter ahead of negative tval. */ - test_set_cnt_after_tval(timer, 0, -1, DEF_CNT + 1, wm); - test_set_cnt_after_tval(timer, 0, -1, TVAL_MAX, wm); - tval = TVAL_MAX; - test_set_cnt_after_tval(timer, 0, tval, (uint64_t) tval + 1, - wm); - } - - for (i = 0; i < ARRAY_SIZE(sleep_method); i++) { - sleep_method_t sm = sleep_method[i]; - - test_set_cnt_after_cval_no_irq(timer, 0, DEF_CNT, CVAL_MAX, sm); - } -} - -/* - * Program a timer, mask it, and then change the tval or counter to cancel it. - * Unmask it and check that nothing fires. - */ -static void test_move_counters_behind_timers(enum arch_timer timer) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sleep_method); i++) { - sleep_method_t sm = sleep_method[i]; - - test_set_cnt_after_cval_no_irq(timer, DEF_CNT, DEF_CNT - 1, 0, - sm); - test_set_cnt_after_tval_no_irq(timer, DEF_CNT, -1, 0, sm); - } -} - -static void test_timers_in_the_past(enum arch_timer timer) -{ - int32_t tval = -1 * (int32_t) msec_to_cycles(test_args.wait_ms); - uint64_t cval; - int i; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - irq_wait_method_t wm = irq_wait_method[i]; - - /* set a timer wait_ms the past. */ - cval = DEF_CNT - msec_to_cycles(test_args.wait_ms); - test_timer_cval(timer, cval, wm, true, DEF_CNT); - test_timer_tval(timer, tval, wm, true, DEF_CNT); - - /* Set a timer to counter=0 (in the past) */ - test_timer_cval(timer, 0, wm, true, DEF_CNT); - - /* Set a time for tval=0 (now) */ - test_timer_tval(timer, 0, wm, true, DEF_CNT); - - /* Set a timer to as far in the past as possible */ - test_timer_tval(timer, TVAL_MIN, wm, true, DEF_CNT); - } - - /* - * Set the counter to wait_ms, and a tval to -wait_ms. There should be no - * IRQ as that tval means cval=CVAL_MAX-wait_ms. - */ - for (i = 0; i < ARRAY_SIZE(sleep_method); i++) { - sleep_method_t sm = sleep_method[i]; - - set_counter(timer, msec_to_cycles(test_args.wait_ms)); - test_tval_no_irq(timer, tval, TIMEOUT_NO_IRQ_US, sm); - } -} - -static void test_long_timer_delays(enum arch_timer timer) -{ - int32_t tval = (int32_t) msec_to_cycles(test_args.long_wait_ms); - uint64_t cval = DEF_CNT + msec_to_cycles(test_args.long_wait_ms); - int i; - - for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) { - irq_wait_method_t wm = irq_wait_method[i]; - - test_timer_cval(timer, cval, wm, true, DEF_CNT); - test_timer_tval(timer, tval, wm, true, DEF_CNT); - } -} - -static void guest_run_iteration(enum arch_timer timer) -{ - test_basic_functionality(timer); - test_timers_sanity_checks(timer); - - test_timers_above_tval_max(timer); - test_timers_in_the_past(timer); - - test_move_counters_ahead_of_timers(timer); - test_move_counters_behind_timers(timer); - test_reprogram_timers(timer); - - test_timers_fired_multiple_times(timer); - - test_timer_control_mask_then_unmask(timer); - test_timer_control_masks(timer); -} - -static void guest_code(enum arch_timer timer) -{ - int i; - - local_irq_disable(); - - gic_init(GIC_V3, 1); - - timer_set_ctl(VIRTUAL, CTL_IMASK); - timer_set_ctl(PHYSICAL, CTL_IMASK); - - gic_irq_enable(vtimer_irq); - gic_irq_enable(ptimer_irq); - local_irq_enable(); - - for (i = 0; i < test_args.iterations; i++) { - GUEST_SYNC(i); - guest_run_iteration(timer); - } - - test_long_timer_delays(timer); - GUEST_DONE(); -} - -static uint32_t next_pcpu(void) -{ - uint32_t max = get_nprocs(); - uint32_t cur = sched_getcpu(); - uint32_t next = cur; - cpu_set_t cpuset; - - TEST_ASSERT(max > 1, "Need at least two physical cpus"); - - sched_getaffinity(0, sizeof(cpuset), &cpuset); - - do { - next = (next + 1) % CPU_SETSIZE; - } while (!CPU_ISSET(next, &cpuset)); - - return next; -} - -static void migrate_self(uint32_t new_pcpu) -{ - int ret; - cpu_set_t cpuset; - pthread_t thread; - - thread = pthread_self(); - - CPU_ZERO(&cpuset); - CPU_SET(new_pcpu, &cpuset); - - pr_debug("Migrating from %u to %u\n", sched_getcpu(), new_pcpu); - - ret = pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset); - - TEST_ASSERT(ret == 0, "Failed to migrate to pCPU: %u; ret: %d\n", - new_pcpu, ret); -} - -static void kvm_set_cntxct(struct kvm_vcpu *vcpu, uint64_t cnt, - enum arch_timer timer) -{ - if (timer == PHYSICAL) - vcpu_set_reg(vcpu, KVM_REG_ARM_PTIMER_CNT, cnt); - else - vcpu_set_reg(vcpu, KVM_REG_ARM_TIMER_CNT, cnt); -} - -static void handle_sync(struct kvm_vcpu *vcpu, struct ucall *uc) -{ - enum sync_cmd cmd = uc->args[1]; - uint64_t val = uc->args[2]; - enum arch_timer timer = uc->args[3]; - - switch (cmd) { - case SET_COUNTER_VALUE: - kvm_set_cntxct(vcpu, val, timer); - break; - case USERSPACE_USLEEP: - usleep(val); - break; - case USERSPACE_SCHED_YIELD: - sched_yield(); - break; - case USERSPACE_MIGRATE_SELF: - migrate_self(next_pcpu()); - break; - default: - break; - } -} - -static void test_run(struct kvm_vm *vm, struct kvm_vcpu *vcpu) -{ - struct ucall uc; - - /* Start on CPU 0 */ - migrate_self(0); - - while (true) { - vcpu_run(vcpu); - switch (get_ucall(vcpu, &uc)) { - case UCALL_SYNC: - handle_sync(vcpu, &uc); - break; - case UCALL_DONE: - goto out; - case UCALL_ABORT: - REPORT_GUEST_ASSERT(uc); - goto out; - default: - TEST_FAIL("Unexpected guest exit\n"); - } - } - - out: - return; -} - -static void test_init_timer_irq(struct kvm_vm *vm, struct kvm_vcpu *vcpu) -{ - vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq); - vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq); - - sync_global_to_guest(vm, ptimer_irq); - sync_global_to_guest(vm, vtimer_irq); - - pr_debug("ptimer_irq: %d; vtimer_irq: %d\n", ptimer_irq, vtimer_irq); -} - -static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu, - enum arch_timer timer) -{ - *vm = vm_create_with_one_vcpu(vcpu, guest_code); - TEST_ASSERT(*vm, "Failed to create the test VM\n"); - - vm_init_descriptor_tables(*vm); - vm_install_exception_handler(*vm, VECTOR_IRQ_CURRENT, - guest_irq_handler); - - vcpu_init_descriptor_tables(*vcpu); - vcpu_args_set(*vcpu, 1, timer); - - test_init_timer_irq(*vm, *vcpu); - vgic_v3_setup(*vm, 1, 64); - sync_global_to_guest(*vm, test_args); -} - -static void test_print_help(char *name) -{ - pr_info("Usage: %s [-h] [-b] [-i iterations] [-l long_wait_ms] [-p] [-v]\n" - , name); - pr_info("\t-i: Number of iterations (default: %u)\n", - NR_TEST_ITERS_DEF); - pr_info("\t-b: Test both physical and virtual timers (default: true)\n"); - pr_info("\t-l: Delta (in ms) used for long wait time test (default: %u)\n", - LONG_WAIT_TEST_MS); - pr_info("\t-l: Delta (in ms) used for wait times (default: %u)\n", - WAIT_TEST_MS); - pr_info("\t-p: Test physical timer (default: true)\n"); - pr_info("\t-v: Test virtual timer (default: true)\n"); - pr_info("\t-h: Print this help message\n"); -} - -static bool parse_args(int argc, char *argv[]) -{ - int opt; - - while ((opt = getopt(argc, argv, "bhi:l:pvw:")) != -1) { - switch (opt) { - case 'b': - test_args.test_physical = true; - test_args.test_virtual = true; - break; - case 'i': - test_args.iterations = - atoi_positive("Number of iterations", optarg); - break; - case 'l': - test_args.long_wait_ms = - atoi_positive("Long wait time", optarg); - break; - case 'p': - test_args.test_physical = true; - test_args.test_virtual = false; - break; - case 'v': - test_args.test_virtual = true; - test_args.test_physical = false; - break; - case 'w': - test_args.wait_ms = atoi_positive("Wait time", optarg); - break; - case 'h': - default: - goto err; - } - } - - return true; - - err: - test_print_help(argv[0]); - return false; -} - -int main(int argc, char *argv[]) -{ - struct kvm_vcpu *vcpu; - struct kvm_vm *vm; - - /* Tell stdout not to buffer its content */ - setbuf(stdout, NULL); - - if (!parse_args(argc, argv)) - exit(KSFT_SKIP); - - if (test_args.test_virtual) { - test_vm_create(&vm, &vcpu, VIRTUAL); - test_run(vm, vcpu); - kvm_vm_free(vm); - } - - if (test_args.test_physical) { - test_vm_create(&vm, &vcpu, PHYSICAL); - test_run(vm, vcpu); - kvm_vm_free(vm); - } - - return 0; -} |