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