The following tests focus on making sure the counter is not running
out of reset and the proper use of INTEN as the counter enable. As
described in:
https://developer.arm.com/documentation/ddi0479/d/apb-components/apb-watchdog/programmers-model
The new tests have to target an MPS2 machine because the original
machine used by the test (stellaris) has a variation of the
cmsdk_apb_watchdog that locks INTEN when it is programmed to 1. The
stellaris machine also does not reproduce the problem of the counter
running out of cold reset due to the way the clocks are initialized.
Signed-off-by: Roque Arcudia Hernandez <roqueh@google.com>
Reviewed-by: Stephen Longfield <slongfield@google.com>
---
tests/qtest/cmsdk-apb-watchdog-test.c | 214 ++++++++++++++++++++++++++
1 file changed, 214 insertions(+)
diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c
index fe535a553c..3777b7bd59 100644
--- a/tests/qtest/cmsdk-apb-watchdog-test.c
+++ b/tests/qtest/cmsdk-apb-watchdog-test.c
@@ -68,6 +68,15 @@ static const CMSDKAPBWatchdogTestArgs machine_info[] = {
},
};
+static void system_reset(QTestState *qtest)
+{
+ QDict *resp;
+
+ resp = qtest_qmp(qtest, "{'execute': 'system_reset'}");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+}
+
static void test_watchdog(const void *ptr)
{
const CMSDKAPBWatchdogTestArgs *args = ptr;
@@ -159,6 +168,199 @@ static void test_clock_change(const void *ptr)
qtest_end();
}
+/* Tests the counter is not running after reset. */
+static void test_watchdog_reset(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0);
+
+ /*
+ * The counter should not be running if WDOGCONTROL.INTEN has not been set,
+ * as it is the case after a cold reset.
+ */
+ clock_step(15 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /* Let the counter run before reset */
+ writel(wdog_base + WDOGLOAD, 3000);
+ writel(wdog_base + WDOGCONTROL, 1);
+
+ /* Verify it is running */
+ clock_step(1000 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 3000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 2000);
+
+ system_reset(global_qtest);
+
+ /* Check defaults after reset */
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /* The counter should not be running after reset. */
+ clock_step(1000 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ qtest_end();
+}
+
+/*
+ * Tests inten works as the counter enable based on this description:
+ *
+ * Enable the interrupt event, WDOGINT. Set HIGH to enable the counter and the
+ * interrupt, or LOW to disable the counter and interrupt. Reloads the counter
+ * from the value in WDOGLOAD when the interrupt is enabled, after previously
+ * being disabled.
+ */
+static void test_watchdog_inten(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should not be running as long as WDOGCONTROL.INTEN is
+ * not set
+ */
+ writel(wdog_base + WDOGLOAD, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */
+ writel(wdog_base + WDOGCONTROL, 1);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /* or LOW to disable the counter and interrupt. */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(100 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /*
+ * Reloads the counter from the value in WDOGLOAD when the interrupt is
+ * enabled, after previously being disabled.
+ */
+ writel(wdog_base + WDOGCONTROL, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Test counter is still on */
+ clock_step(50 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3950);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should be running since WDOGCONTROL.INTEN is set
+ */
+ writel(wdog_base + WDOGLOAD, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 5000);
+ clock_step(4999 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ /* Finally disable and check the conditions don't change */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(10 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ qtest_end();
+}
+
+/*
+ * Tests the following custom behavior:
+ *
+ * The Luminary version of this device ignores writes to this register after the
+ * guest has enabled interrupts (so they can only be disabled again via reset).
+ */
+static void test_watchdog_inten_luminary(const void *ptr)
+{
+ const CMSDKAPBWatchdogTestArgs *args = ptr;
+ hwaddr wdog_base = args->wdog_base;
+ int64_t tick = args->tick;
+ g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine);
+ qtest_start(cmdline);
+ g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0);
+
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ /*
+ * When WDOGLOAD is written to, the count is immediately restarted from the
+ * new value.
+ *
+ * Note: the counter should not be running as long as WDOGCONTROL.INTEN is
+ * not set
+ */
+ writel(wdog_base + WDOGLOAD, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000);
+
+ /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */
+ writel(wdog_base + WDOGCONTROL, 1);
+ clock_step(500 * tick + 1);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500);
+
+ /*
+ * The Luminary version of this device ignores writes to this register after
+ * the guest has enabled interrupts
+ */
+ writel(wdog_base + WDOGCONTROL, 0);
+ clock_step(100 * tick);
+ g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000);
+ g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3400);
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0x1);
+
+ /* They can only be disabled again via reset */
+ system_reset(global_qtest);
+
+ /* Check defaults after reset */
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0);
+
+ /* The counter should not be running after reset. */
+ clock_step(1000 * tick + 1);
+ g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT);
+ g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT);
+
+ qtest_end();
+}
+
int main(int argc, char **argv)
{
int r;
@@ -172,10 +374,22 @@ int main(int argc, char **argv)
qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_clock_change",
&machine_info[MACHINE_LM3S811EVB],
test_clock_change);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset",
+ &machine_info[MACHINE_LM3S811EVB],
+ test_watchdog_reset);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten_luminary",
+ &machine_info[MACHINE_LM3S811EVB],
+ test_watchdog_inten_luminary);
}
if (qtest_has_machine(machine_info[MACHINE_MPS2_AN385].machine)) {
qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_mps2",
&machine_info[MACHINE_MPS2_AN385], test_watchdog);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset_mps2",
+ &machine_info[MACHINE_MPS2_AN385],
+ test_watchdog_reset);
+ qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten",
+ &machine_info[MACHINE_MPS2_AN385],
+ test_watchdog_inten);
}
r = g_test_run();
--
2.47.0.277.g8800431eea-goog
On Fri, 8 Nov 2024 at 19:10, Roque Arcudia Hernandez <roqueh@google.com> wrote: > > The following tests focus on making sure the counter is not running > out of reset and the proper use of INTEN as the counter enable. As > described in: > > https://developer.arm.com/documentation/ddi0479/d/apb-components/apb-watchdog/programmers-model > > The new tests have to target an MPS2 machine because the original > machine used by the test (stellaris) has a variation of the > cmsdk_apb_watchdog that locks INTEN when it is programmed to 1. The > stellaris machine also does not reproduce the problem of the counter > running out of cold reset due to the way the clocks are initialized. > > Signed-off-by: Roque Arcudia Hernandez <roqueh@google.com> > Reviewed-by: Stephen Longfield <slongfield@google.com> > --- > tests/qtest/cmsdk-apb-watchdog-test.c | 214 ++++++++++++++++++++++++++ > 1 file changed, 214 insertions(+) > > diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c > index fe535a553c..3777b7bd59 100644 > --- a/tests/qtest/cmsdk-apb-watchdog-test.c > +++ b/tests/qtest/cmsdk-apb-watchdog-test.c > @@ -68,6 +68,15 @@ static const CMSDKAPBWatchdogTestArgs machine_info[] = { > }, > }; > > +static void system_reset(QTestState *qtest) > +{ > + QDict *resp; > + > + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); > + g_assert(qdict_haskey(resp, "return")); > + qobject_unref(resp); > +} The system_reset QMP command only requests a reset; it does not wait for it to actually happen. For that you need to qtest_qmp_eventwait(qtest, "RESET"); We seem to already have several implementations of this kind of "reset the system under test" function, several of which have this bug. That suggests to me that we ought to provide it as a utility method qtest_system_reset() in libqtest. thanks -- PMM
Thanks for pointing this out. For now I'll be adding the extra line in the version 2 of this patch. On Thu, Nov 14, 2024 at 5:01 AM Peter Maydell <peter.maydell@linaro.org> wrote: > > On Fri, 8 Nov 2024 at 19:10, Roque Arcudia Hernandez <roqueh@google.com> wrote: > > > > The following tests focus on making sure the counter is not running > > out of reset and the proper use of INTEN as the counter enable. As > > described in: > > > > https://developer.arm.com/documentation/ddi0479/d/apb-components/apb-watchdog/programmers-model > > > > The new tests have to target an MPS2 machine because the original > > machine used by the test (stellaris) has a variation of the > > cmsdk_apb_watchdog that locks INTEN when it is programmed to 1. The > > stellaris machine also does not reproduce the problem of the counter > > running out of cold reset due to the way the clocks are initialized. > > > > Signed-off-by: Roque Arcudia Hernandez <roqueh@google.com> > > Reviewed-by: Stephen Longfield <slongfield@google.com> > > --- > > tests/qtest/cmsdk-apb-watchdog-test.c | 214 ++++++++++++++++++++++++++ > > 1 file changed, 214 insertions(+) > > > > diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c > > index fe535a553c..3777b7bd59 100644 > > --- a/tests/qtest/cmsdk-apb-watchdog-test.c > > +++ b/tests/qtest/cmsdk-apb-watchdog-test.c > > @@ -68,6 +68,15 @@ static const CMSDKAPBWatchdogTestArgs machine_info[] = { > > }, > > }; > > > > +static void system_reset(QTestState *qtest) > > +{ > > + QDict *resp; > > + > > + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); > > + g_assert(qdict_haskey(resp, "return")); > > + qobject_unref(resp); > > +} > > The system_reset QMP command only requests a reset; it does > not wait for it to actually happen. For that you need to > qtest_qmp_eventwait(qtest, "RESET"); > > We seem to already have several implementations of this > kind of "reset the system under test" function, several > of which have this bug. That suggests to me that we ought > to provide it as a utility method qtest_system_reset() > in libqtest. > > thanks > -- PMM
© 2016 - 2024 Red Hat, Inc.