blob: 47d5339c7ecd7a3687f14ba7283a745f975f6e88 [file] [log] [blame]
/* Copyright 2025 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "common.h"
#include "hwtimer.h"
#include "panic.h"
#include "system.h"
#include "task.h"
#include "test_util.h"
#include <cassert>
test_static int test_watchdog()
{
constexpr uint32_t watchdog_period_us =
CONFIG_WATCHDOG_PERIOD_MS * MSEC;
/* Busy wait slightly longer than watchdog period to account for
* inaccuracies in busy waiting. */
uint32_t us_to_wait = watchdog_period_us * 1.1;
/*
* TODO(b/390021325): It appears that the watchdog timer is being set
* to longer than CONFIG_WATCHDOG_PERIOD_MS on bloonchipper/dartmonkey.
*/
if (IS_ENABLED(BOARD_BLOONCHIPPER) || IS_ENABLED(BOARD_DARTMONKEY)) {
us_to_wait = watchdog_period_us * 3;
}
ccprints("Starting busy loop.");
udelay(us_to_wait);
/* Watchdog should fire before reaching this. */
assert(false);
return EC_ERROR_UNKNOWN;
}
test_static int test_panic_data()
{
constexpr uint32_t expected_reason = PANIC_SW_WATCHDOG;
/* Note: The task_id can be found with the "taskinfo" command. */
constexpr uint8_t expected_task_id = 5;
/*
* The watchdog should fire while udelay is running. We don't know the
* exact end of udelay, but it's short so we just choose a small value
* for the end.
*
* Note that this check depends on build optimizations and inlining,
* which is why we have a special case for helipilot. The helipilot
* build does not inline the __hw_clock_source_read() function called by
* udelay().
*/
uint32_t udelay_start_addr = reinterpret_cast<uint32_t>(udelay);
if (IS_ENABLED(BASEBOARD_HELIPILOT)) {
udelay_start_addr =
reinterpret_cast<uint32_t>(__hw_clock_source_read);
}
uint32_t udelay_end_addr = udelay_start_addr + 0x40;
uint32_t reason = 0;
uint32_t info = 0;
uint8_t exception = UINT8_MAX;
panic_get_reason(&reason, &info, &exception);
TEST_EQ(reason, expected_reason, "%08x");
TEST_GT(info, udelay_start_addr, "%d");
TEST_LT(info, udelay_end_addr, "%d");
TEST_EQ(exception, expected_task_id, "%d");
return EC_SUCCESS;
}
test_static void run_test_step1()
{
test_set_next_step(TEST_STATE_STEP_2);
RUN_TEST(test_watchdog);
}
test_static void run_test_step2()
{
RUN_TEST(test_panic_data);
if (test_get_error_count())
test_reboot_to_next_step(TEST_STATE_FAILED);
else
test_reboot_to_next_step(TEST_STATE_PASSED);
}
void test_run_step(uint32_t state)
{
if (state & TEST_STATE_MASK(TEST_STATE_STEP_1)) {
run_test_step1();
} else if (state & TEST_STATE_MASK(TEST_STATE_STEP_2)) {
run_test_step2();
}
}
extern "C" int task_test(void *unused)
{
if (IS_ENABLED(SECTION_IS_RW))
test_run_multistep();
return EC_SUCCESS;
}
void run_test(int argc, const char **argv)
{
test_reset();
crec_msleep(30); /* Wait for TASK_ID_TEST to initialize */
task_wake(TASK_ID_TEST);
}