/* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * * This test case verifies the correctness of irq_offload(), an important * routine used in many other test cases for running a function in interrupt * context, on the IRQ stack. * */ #include #include #include #include volatile uint32_t sentinel; #define SENTINEL_VALUE 0xDEADBEEF K_THREAD_STACK_DEFINE(offload_stack, 384 + CONFIG_TEST_EXTRA_STACK_SIZE); struct k_thread offload_thread; static void offload_function(const void *param) { uint32_t x = POINTER_TO_INT(param); /* Make sure we're in IRQ context */ zassert_true(k_is_in_isr(), "Not in IRQ context!"); sentinel = x; } /** * @brief Verify thread context * * @ingroup kernel_interrupt_tests * * @details Check whether offloaded running function is in interrupt * context, on the IRQ stack or not. */ ZTEST(irq_offload, test_irq_offload) { /* Simple validation of nested locking. */ unsigned int key1, key2; key1 = arch_irq_lock(); zassert_true(arch_irq_unlocked(key1), "IRQs should have been unlocked, but key is 0x%x\n", key1); key2 = arch_irq_lock(); zassert_false(arch_irq_unlocked(key2), "IRQs should have been locked, but key is 0x%x\n", key2); arch_irq_unlock(key2); arch_irq_unlock(key1); /**TESTPOINT: Offload to IRQ context*/ irq_offload(offload_function, (const void *)SENTINEL_VALUE); zassert_equal(sentinel, SENTINEL_VALUE, "irq_offload() didn't work properly"); } static struct k_timer nestoff_timer; static bool timer_executed, nested_executed; void nestoff_offload(const void *parameter) { /* Suspend the thread we interrupted so we context switch, see below */ k_thread_suspend(&offload_thread); nested_executed = true; } static void nestoff_timer_fn(struct k_timer *timer) { zassert_false(nested_executed, "nested irq_offload ran too soon"); irq_offload(nestoff_offload, NULL); zassert_true(nested_executed, "nested irq_offload did not run"); /* Set this last, to be sure we return to this context and not * the enclosing interrupt */ timer_executed = true; } static void offload_thread_fn(void *p0, void *p1, void *p2) { k_timer_start(&nestoff_timer, K_TICKS(1), K_FOREVER); while (true) { zassert_false(timer_executed, "should not return to this thread"); } } /* Invoke irq_offload() from an interrupt and verify that the * resulting nested interrupt doesn't explode */ ZTEST(common_1cpu, test_nested_irq_offload) { if (!IS_ENABLED(CONFIG_IRQ_OFFLOAD_NESTED)) { ztest_test_skip(); } k_thread_priority_set(k_current_get(), 1); k_timer_init(&nestoff_timer, nestoff_timer_fn, NULL); zassert_false(timer_executed, "timer ran too soon"); zassert_false(nested_executed, "nested irq_offload ran too soon"); /* Do this in a thread to exercise a regression case: the * offload handler will suspend the thread it interrupted, * ensuring that the interrupt returns back to this thread and * effects a context switch of the nested interrupt (see * #45779). Requires that this be a 1cpu test case, * obviously. */ k_thread_create(&offload_thread, offload_stack, K_THREAD_STACK_SIZEOF(offload_stack), offload_thread_fn, NULL, NULL, NULL, 0, 0, K_NO_WAIT); zassert_true(timer_executed, "timer did not run"); zassert_true(nested_executed, "nested irq_offload did not run"); k_thread_abort(&offload_thread); } extern void *common_setup(void); ZTEST_SUITE(irq_offload, NULL, common_setup, NULL, NULL, NULL);