diff options
Diffstat (limited to 'tests/qtest')
-rw-r--r-- | tests/qtest/libqos/libqtest.h | 37 | ||||
-rw-r--r-- | tests/qtest/libqtest.c | 82 | ||||
-rw-r--r-- | tests/qtest/meson.build | 1 | ||||
-rw-r--r-- | tests/qtest/sse-timer-test.c | 240 |
4 files changed, 334 insertions, 26 deletions
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h index 724f65aa94..a68dcd79d4 100644 --- a/tests/qtest/libqos/libqtest.h +++ b/tests/qtest/libqos/libqtest.h @@ -75,6 +75,17 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); /** + * qtest_kill_qemu: + * @s: #QTestState instance to operate on. + * + * Kill the QEMU process and wait for it to terminate. It is safe to call this + * function multiple times. Normally qtest_quit() is used instead because it + * also frees QTestState. Use qtest_kill_qemu() when you just want to kill QEMU + * and qtest_quit() will be called later. + */ +void qtest_kill_qemu(QTestState *s); + +/** * qtest_quit: * @s: #QTestState instance to operate on. * @@ -133,6 +144,14 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** + * qtest_socket_server: + * @socket_path: the UNIX domain socket path + * + * Create and return a listen socket file descriptor, or abort on failure. + */ +int qtest_socket_server(const char *socket_path); + +/** * qtest_vqmp_fds: * @s: #QTestState instance to operate on. * @fds: array of file descriptors @@ -630,9 +649,27 @@ void qtest_add_data_func_full(const char *str, void *data, g_free(path); \ } while (0) +/** + * qtest_add_abrt_handler: + * @fn: Handler function + * @data: Argument that is passed to the handler + * + * Add a handler function that is invoked on SIGABRT. This can be used to + * terminate processes and perform other cleanup. The handler can be removed + * with qtest_remove_abrt_handler(). + */ void qtest_add_abrt_handler(GHookFunc fn, const void *data); /** + * qtest_remove_abrt_handler: + * @data: Argument previously passed to qtest_add_abrt_handler() + * + * Remove an abrt handler that was previously added with + * qtest_add_abrt_handler(). + */ +void qtest_remove_abrt_handler(void *data); + +/** * qtest_qmp_assert_success: * @qts: QTestState instance to operate on * @fmt: QMP message to send to qemu, formatted like diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index fd043b0570..71e359efcd 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -81,24 +81,8 @@ static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv); static int init_socket(const char *socket_path) { - struct sockaddr_un addr; - int sock; - int ret; - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - g_assert_cmpint(sock, !=, -1); - - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); + int sock = qtest_socket_server(socket_path); qemu_set_cloexec(sock); - - do { - ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - } while (ret == -1 && errno == EINTR); - g_assert_cmpint(ret, !=, -1); - ret = listen(sock, 1); - g_assert_cmpint(ret, !=, -1); - return sock; } @@ -149,7 +133,7 @@ void qtest_set_expected_status(QTestState *s, int status) s->expected_status = status; } -static void kill_qemu(QTestState *s) +void qtest_kill_qemu(QTestState *s) { pid_t pid = s->qemu_pid; int wstatus; @@ -159,6 +143,7 @@ static void kill_qemu(QTestState *s) kill(pid, SIGTERM); TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); assert(pid == s->qemu_pid); + s->qemu_pid = -1; } /* @@ -185,7 +170,7 @@ static void kill_qemu(QTestState *s) static void kill_qemu_hook_func(void *s) { - kill_qemu(s); + qtest_kill_qemu(s); } static void sigabrt_handler(int signo) @@ -211,15 +196,30 @@ static void cleanup_sigabrt_handler(void) sigaction(SIGABRT, &sigact_old, NULL); } +static bool hook_list_is_empty(GHookList *hook_list) +{ + GHook *hook = g_hook_first_valid(hook_list, TRUE); + + if (!hook) { + return false; + } + + g_hook_unref(hook_list, hook); + return true; +} + void qtest_add_abrt_handler(GHookFunc fn, const void *data) { GHook *hook; - /* Only install SIGABRT handler once */ if (!abrt_hooks.is_setup) { g_hook_list_init(&abrt_hooks, sizeof(GHook)); } - setup_sigabrt_handler(); + + /* Only install SIGABRT handler once */ + if (hook_list_is_empty(&abrt_hooks)) { + setup_sigabrt_handler(); + } hook = g_hook_alloc(&abrt_hooks); hook->func = fn; @@ -228,6 +228,17 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data) g_hook_prepend(&abrt_hooks, hook); } +void qtest_remove_abrt_handler(void *data) +{ + GHook *hook = g_hook_find_data(&abrt_hooks, TRUE, data); + g_hook_destroy_link(&abrt_hooks, hook); + + /* Uninstall SIGABRT handler on last instance */ + if (hook_list_is_empty(&abrt_hooks)) { + cleanup_sigabrt_handler(); + } +} + static const char *qtest_qemu_binary(void) { const char *qemu_bin; @@ -384,12 +395,9 @@ QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) void qtest_quit(QTestState *s) { - g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s)); - - /* Uninstall SIGABRT handler on last instance */ - cleanup_sigabrt_handler(); + qtest_remove_abrt_handler(s); - kill_qemu(s); + qtest_kill_qemu(s); close(s->fd); close(s->qmp_fd); g_string_free(s->rx, true); @@ -638,6 +646,28 @@ QDict *qtest_qmp_receive_dict(QTestState *s) return qmp_fd_receive(s->qmp_fd); } +int qtest_socket_server(const char *socket_path) +{ + struct sockaddr_un addr; + int sock; + int ret; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + g_assert_cmpint(sock, !=, -1); + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); + + do { + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + } while (ret == -1 && errno == EINTR); + g_assert_cmpint(ret, !=, -1); + ret = listen(sock, 1); + g_assert_cmpint(ret, !=, -1); + + return sock; +} + /** * Allow users to send a message without waiting for the reply, * in the case that they choose to discard all replies up until diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 58efc46144..2688e1bfad 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -157,6 +157,7 @@ qtests_npcm7xx = \ 'npcm7xx_watchdog_timer-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) qtests_arm = \ + (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \ diff --git a/tests/qtest/sse-timer-test.c b/tests/qtest/sse-timer-test.c new file mode 100644 index 0000000000..a65d7542d5 --- /dev/null +++ b/tests/qtest/sse-timer-test.c @@ -0,0 +1,240 @@ +/* + * QTest testcase for the SSE timer device + * + * Copyright (c) 2021 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* + * SSE-123/SSE-300 timer in the mps3-an547 board, where it is driven + * at 32MHz, so 31.25ns per tick. + */ +#define TIMER_BASE 0x48000000 + +/* PERIPHNSPPC0 register in the SSE-300 Secure Access Configuration block */ +#define PERIPHNSPPC0 (0x50080000 + 0x70) + +/* Base of the System Counter control frame */ +#define COUNTER_BASE 0x58100000 + +/* SSE counter register offsets in the control frame */ +#define CNTCR 0 +#define CNTSR 0x4 +#define CNTCV_LO 0x8 +#define CNTCV_HI 0xc +#define CNTSCR 0x10 + +/* SSE timer register offsets */ +#define CNTPCT_LO 0 +#define CNTPCT_HI 4 +#define CNTFRQ 0x10 +#define CNTP_CVAL_LO 0x20 +#define CNTP_CVAL_HI 0x24 +#define CNTP_TVAL 0x28 +#define CNTP_CTL 0x2c +#define CNTP_AIVAL_LO 0x40 +#define CNTP_AIVAL_HI 0x44 +#define CNTP_AIVAL_RELOAD 0x48 +#define CNTP_AIVAL_CTL 0x4c + +/* 4 ticks in nanoseconds (so we can work in integers) */ +#define FOUR_TICKS 125 + +static void clock_step_ticks(uint64_t ticks) +{ + /* + * Advance the qtest clock by however many nanoseconds we + * need to move the timer forward the specified number of ticks. + * ticks must be a multiple of 4, so we get a whole number of ns. + */ + assert(!(ticks & 3)); + clock_step(FOUR_TICKS * (ticks >> 2)); +} + +static void reset_counter_and_timer(void) +{ + /* + * Reset the system counter and the timer between tests. This + * isn't a full reset, but it's sufficient for what the tests check. + */ + writel(COUNTER_BASE + CNTCR, 0); + writel(TIMER_BASE + CNTP_CTL, 0); + writel(TIMER_BASE + CNTP_AIVAL_CTL, 0); + writel(COUNTER_BASE + CNTCV_LO, 0); + writel(COUNTER_BASE + CNTCV_HI, 0); +} + +static void test_counter(void) +{ + /* Basic counter functionality test */ + + reset_counter_and_timer(); + /* The counter should start disabled: check that it doesn't move */ + clock_step_ticks(100); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 0); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0); + /* Now enable it and check that it does count */ + writel(COUNTER_BASE + CNTCR, 1); + clock_step_ticks(100); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 100); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0); + /* Check the counter scaling functionality */ + writel(COUNTER_BASE + CNTCR, 0); + writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */ + writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */ + clock_step_ticks(160); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_LO), ==, 110); + g_assert_cmpuint(readl(COUNTER_BASE + CNTCV_HI), ==, 0); +} + +static void test_timer(void) +{ + /* Basic timer functionality test */ + + reset_counter_and_timer(); + /* + * The timer is behind a Peripheral Protection Controller, and + * qtest accesses are always non-secure (no memory attributes), + * so we must program the PPC to accept NS transactions. TIMER0 + * is on port 0 of PPC0, controlled by bit 0 of this register. + */ + writel(PERIPHNSPPC0, 1); + /* We must enable the System Counter or the timer won't run. */ + writel(COUNTER_BASE + CNTCR, 1); + + /* Timer starts disabled and with a counter of 0 */ + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 0); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 0); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0); + + /* Turn it on */ + writel(TIMER_BASE + CNTP_CTL, 1); + + /* Is the timer ticking? */ + clock_step_ticks(100); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 100); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0); + + /* Set the CompareValue to 4000 ticks */ + writel(TIMER_BASE + CNTP_CVAL_LO, 4000); + writel(TIMER_BASE + CNTP_CVAL_HI, 0); + + /* Check TVAL view of the counter */ + g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 3900); + + /* Advance to the CompareValue mark and check ISTATUS is set */ + clock_step_ticks(3900); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_TVAL), ==, 0); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5); + + /* Now exercise the auto-reload part of the timer */ + writel(TIMER_BASE + CNTP_AIVAL_RELOAD, 200); + writel(TIMER_BASE + CNTP_AIVAL_CTL, 1); + + /* Check AIVAL was reloaded and that ISTATUS is now clear */ + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4200); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + + /* + * Check that when we advance forward to the reload time the interrupt + * fires and the value reloads + */ + clock_step_ticks(100); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + clock_step_ticks(100); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4400); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0); + + clock_step_ticks(100); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5); + /* Check that writing 0 to CLR clears the interrupt */ + writel(TIMER_BASE + CNTP_AIVAL_CTL, 1); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + /* Check that when we move forward to the reload time it fires again */ + clock_step_ticks(100); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0); + + /* + * Step the clock far enough that we overflow the low half of the + * CNTPCT and AIVAL registers, and check that their high halves + * give the right values. We do the forward movement in + * non-autoinc mode because otherwise it takes forever as the + * timer has to emulate all the 'reload at t + N, t + 2N, etc' + * steps. + */ + writel(TIMER_BASE + CNTP_AIVAL_CTL, 0); + clock_step_ticks(0x42ULL << 32); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400); + g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42); + + /* Turn on the autoinc again to check AIVAL_HI */ + writel(TIMER_BASE + CNTP_AIVAL_CTL, 1); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42); +} + +static void test_timer_scale_change(void) +{ + /* + * Test that the timer responds correctly to counter + * scaling changes while it has an active timer. + */ + reset_counter_and_timer(); + /* Give ourselves access to the timer, and enable the counter and timer */ + writel(PERIPHNSPPC0, 1); + writel(COUNTER_BASE + CNTCR, 1); + writel(TIMER_BASE + CNTP_CTL, 1); + /* Set the CompareValue to 4000 ticks */ + writel(TIMER_BASE + CNTP_CVAL_LO, 4000); + writel(TIMER_BASE + CNTP_CVAL_HI, 0); + /* Advance halfway and check ISTATUS is not set */ + clock_step_ticks(2000); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + /* Reprogram the counter to run at 1/16th speed */ + writel(COUNTER_BASE + CNTCR, 0); + writel(COUNTER_BASE + CNTSCR, 0x00100000); /* 1/16th normal speed */ + writel(COUNTER_BASE + CNTCR, 5); /* EN, SCEN */ + /* Advance to where the timer would have fired and check it has not */ + clock_step_ticks(2000); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + /* Advance to where the timer must fire at the new clock rate */ + clock_step_ticks(29996); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 1); + clock_step_ticks(4); + g_assert_cmpuint(readl(TIMER_BASE + CNTP_CTL), ==, 5); +} + +int main(int argc, char **argv) +{ + int r; + + g_test_init(&argc, &argv, NULL); + + qtest_start("-machine mps3-an547"); + + qtest_add_func("/sse-timer/counter", test_counter); + qtest_add_func("/sse-timer/timer", test_timer); + qtest_add_func("/sse-timer/timer-scale-change", test_timer_scale_change); + + r = g_test_run(); + + qtest_end(); + + return r; +} |