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