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