1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/printk.h>
9 #include <zephyr/sys/__assert.h>
10 #include <cmsis_core.h>
11 #include <zephyr/sys/barrier.h>
12 
13 #if !defined(CONFIG_CPU_CORTEX_M)
14   #error test can only run on Cortex-M MCUs
15 #endif
16 
17 #if defined(CONFIG_ARMV8_1_M_MAINLINE)
18 /*
19  * For ARMv8.1-M, the FPSCR[18:16] LTPSIZE field may always read 0b010 if MVE
20  * is not implemented, so mask it when validating the value of the FPSCR.
21  */
22 #define FPSCR_MASK		(~FPU_FPDSCR_LTPSIZE_Msk)
23 #else
24 #define FPSCR_MASK		(0xffffffffU)
25 #endif
26 
27 K_THREAD_STACK_DECLARE(z_main_stack, CONFIG_MAIN_STACK_SIZE);
28 
29 static volatile int test_flag;
30 static unsigned int expected_reason;
31 
arm_isr_handler(const void * args)32 void arm_isr_handler(const void *args)
33 {
34 	ARG_UNUSED(args);
35 
36 	test_flag++;
37 }
38 
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * pEsf)39 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
40 {
41 	printk("Caught system error -- reason %d\n", reason);
42 
43 	if (expected_reason == -1) {
44 		printk("Was not expecting a crash\n");
45 		k_fatal_halt(reason);
46 	}
47 
48 	if (reason != expected_reason) {
49 		printk("Wrong crash type got %d expected %d\n", reason,
50 			expected_reason);
51 		k_fatal_halt(reason);
52 	}
53 
54 	expected_reason = -1;
55 }
56 
test_main(void)57 void test_main(void)
58 {
59 	printk("ARM no-multithreading test\n");
60 
61 	uint32_t psp = (uint32_t)__get_PSP();
62 	uint32_t main_stack_base = (uint32_t)K_THREAD_STACK_BUFFER(z_main_stack);
63 	uint32_t main_stack_top = (uint32_t)(K_THREAD_STACK_BUFFER(z_main_stack) +
64 		K_THREAD_STACK_SIZEOF(z_main_stack));
65 
66 	__ASSERT(
67 		(psp >= main_stack_base) && (psp <= main_stack_top),
68 			"PSP out of bounds: 0x%x (0x%x - 0x%x)",
69 			psp, main_stack_base, main_stack_top);
70 
71 #if defined(CONFIG_FPU)
72 	__ASSERT((__get_FPSCR() & FPSCR_MASK) == 0,
73 		"FPSCR not zero (0x%x)", __get_FPSCR());
74 #endif
75 
76 #if defined(CONFIG_BUILTIN_STACK_GUARD)
77 	uint32_t psplim = (uint32_t)__get_PSPLIM();
78 	__ASSERT(
79 		(psplim == main_stack_base),
80 			"PSPLIM not set to main stack base: (0x%x)",
81 			psplim);
82 #endif
83 
84 	int key = arch_irq_lock();
85 	__ASSERT(arch_irq_unlocked(key),
86 		"IRQs locked in main()");
87 
88 	arch_irq_unlock(key);
89 
90 	/* Verify activating the PendSV IRQ triggers a K_ERR_SPURIOUS_IRQ */
91 	expected_reason = K_ERR_CPU_EXCEPTION;
92 	SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
93 	barrier_dsync_fence_full();
94 	barrier_isync_fence_full();
95 
96 	/* Determine an NVIC IRQ line that is not currently in use. */
97 	int i, flag = test_flag;
98 
99 	__ASSERT(flag == 0, "Test flag not initialized to 0\n");
100 
101 	for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) {
102 		if (NVIC_GetEnableIRQ(i) == 0) {
103 			/*
104 			 * Interrupts configured statically with IRQ_CONNECT(.)
105 			 * are automatically enabled. NVIC_GetEnableIRQ()
106 			 * returning false, here, implies that the IRQ line is
107 			 * either not implemented or it is not enabled, thus,
108 			 * currently not in use by Zephyr.
109 			 */
110 
111 			/* Set the NVIC line to pending. */
112 			NVIC_SetPendingIRQ(i);
113 
114 			if (NVIC_GetPendingIRQ(i)) {
115 				/* If the NVIC line is pending, it is
116 				 * guaranteed that it is implemented; clear the
117 				 * line.
118 				 */
119 				NVIC_ClearPendingIRQ(i);
120 
121 				if (!NVIC_GetPendingIRQ(i)) {
122 					/*
123 					 * If the NVIC line can be successfully
124 					 * un-pended, it is guaranteed that it
125 					 * can be used for software interrupt
126 					 * triggering. Trigger it.
127 					 */
128 					NVIC_SetPendingIRQ(i);
129 					break;
130 				}
131 			}
132 		}
133 	}
134 
135 	if (i >= 0) {
136 
137 		printk("Available IRQ line: %u\n", i);
138 
139 		arch_irq_connect_dynamic(i, 0 /* highest priority */,
140 			arm_isr_handler,
141 			NULL,
142 			0);
143 
144 		NVIC_EnableIRQ(i);
145 
146 		barrier_dsync_fence_full();
147 		barrier_isync_fence_full();
148 
149 		flag = test_flag;
150 
151 		__ASSERT(flag > 0, "Test flag not set by IRQ\n");
152 
153 		printk("ARM no multithreading test successful\n");
154 	} else {
155 		__ASSERT(0, "No available IRQ line to use in the test\n");
156 	}
157 }
158