1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 *
10 * This test case verifies the correctness of irq_offload(), an important
11 * routine used in many other test cases for running a function in interrupt
12 * context, on the IRQ stack.
13 *
14 */
15 #include <zephyr/kernel.h>
16 #include <zephyr/ztest.h>
17 #include <zephyr/kernel_structs.h>
18 #include <zephyr/irq_offload.h>
19
20 /**
21 * @defgroup kernel_irq_offload_tests IRQ Offload
22 * @ingroup all_tests
23 * @{
24 * @}
25 *
26 * @addtogroup kernel_irq_offload_tests
27 * @{
28 */
29
30 volatile uint32_t sentinel;
31 #define SENTINEL_VALUE 0xDEADBEEF
32
33 K_THREAD_STACK_DEFINE(offload_stack, 384 + CONFIG_TEST_EXTRA_STACK_SIZE);
34 struct k_thread offload_thread;
35
offload_function(const void * param)36 static void offload_function(const void *param)
37 {
38 uint32_t x = POINTER_TO_INT(param);
39
40 /* Make sure we're in IRQ context */
41 zassert_true(k_is_in_isr(), "Not in IRQ context!");
42
43 sentinel = x;
44 }
45
46 /**
47 * @brief Verify thread context
48 *
49 * @details Check whether offloaded running function is in interrupt
50 * context, on the IRQ stack or not.
51 */
ZTEST(irq_offload,test_irq_offload)52 ZTEST(irq_offload, test_irq_offload)
53 {
54 /* Simple validation of nested locking. */
55 unsigned int key1, key2;
56
57 key1 = arch_irq_lock();
58 zassert_true(arch_irq_unlocked(key1),
59 "IRQs should have been unlocked, but key is 0x%x\n",
60 key1);
61 key2 = arch_irq_lock();
62 zassert_false(arch_irq_unlocked(key2),
63 "IRQs should have been locked, but key is 0x%x\n",
64 key2);
65 arch_irq_unlock(key2);
66 arch_irq_unlock(key1);
67
68 /**TESTPOINT: Offload to IRQ context*/
69 irq_offload(offload_function, (const void *)SENTINEL_VALUE);
70
71 zassert_equal(sentinel, SENTINEL_VALUE,
72 "irq_offload() didn't work properly");
73 }
74
75 static struct k_timer nestoff_timer;
76 static bool timer_executed, nested_executed;
77
nestoff_offload(const void * parameter)78 void nestoff_offload(const void *parameter)
79 {
80 /* Suspend the thread we interrupted so we context switch, see below */
81 k_thread_suspend(&offload_thread);
82
83 nested_executed = true;
84 }
85
86
nestoff_timer_fn(struct k_timer * timer)87 static void nestoff_timer_fn(struct k_timer *timer)
88 {
89 zassert_false(nested_executed, "nested irq_offload ran too soon");
90 irq_offload(nestoff_offload, NULL);
91 zassert_true(nested_executed, "nested irq_offload did not run");
92
93 /* Set this last, to be sure we return to this context and not
94 * the enclosing interrupt
95 */
96 timer_executed = true;
97 }
98
offload_thread_fn(void * p0,void * p1,void * p2)99 static void offload_thread_fn(void *p0, void *p1, void *p2)
100 {
101 k_timer_start(&nestoff_timer, K_TICKS(1), K_FOREVER);
102
103 while (true) {
104 zassert_false(timer_executed, "should not return to this thread");
105 }
106 }
107
108 /**
109 * @brief Invoke irq_offload from an interrupt and verify that the
110 * resulting nested interrupt doesn't explode
111 */
ZTEST(common_1cpu,test_nested_irq_offload)112 ZTEST(common_1cpu, test_nested_irq_offload)
113 {
114 if (!IS_ENABLED(CONFIG_IRQ_OFFLOAD_NESTED)) {
115 ztest_test_skip();
116 }
117
118 k_thread_priority_set(k_current_get(), 1);
119
120 k_timer_init(&nestoff_timer, nestoff_timer_fn, NULL);
121
122 zassert_false(timer_executed, "timer ran too soon");
123 zassert_false(nested_executed, "nested irq_offload ran too soon");
124
125 /* Do this in a thread to exercise a regression case: the
126 * offload handler will suspend the thread it interrupted,
127 * ensuring that the interrupt returns back to this thread and
128 * effects a context switch of the nested interrupt (see
129 * #45779). Requires that this be a 1cpu test case,
130 * obviously.
131 */
132 k_thread_create(&offload_thread,
133 offload_stack, K_THREAD_STACK_SIZEOF(offload_stack),
134 offload_thread_fn, NULL, NULL, NULL,
135 0, 0, K_NO_WAIT);
136
137 zassert_true(timer_executed, "timer did not run");
138 zassert_true(nested_executed, "nested irq_offload did not run");
139
140 k_thread_abort(&offload_thread);
141 }
142 /**
143 * @}
144 */
145 extern void *common_setup(void);
146 ZTEST_SUITE(irq_offload, NULL, common_setup, NULL, NULL, NULL);
147