1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2011-10-21 Bernard the first version.
9 * 2011-10-27 aozima update for cortex-M4 FPU.
10 * 2011-12-31 aozima fixed stack align issues.
11 * 2012-01-01 aozima support context switch load/store FPU register.
12 * 2012-12-11 lgnq fixed the coding style.
13 * 2012-12-23 aozima stack addr align to 8byte.
14 * 2012-12-29 Bernard Add exception hook.
15 * 2013-06-23 aozima support lazy stack optimized.
16 * 2018-07-24 aozima enhancement hard fault exception handler.
17 * 2019-07-03 yangjie add __rt_ffs() for armclang.
18 */
19
20 #include <rtthread.h>
21 #ifdef RT_USING_HW_STACK_GUARD
22 #include <mprotect.h>
23 #endif
24
25 #if /* ARMCC */ ( (defined ( __CC_ARM ) && defined ( __TARGET_FPU_VFP )) \
26 /* Clang */ || (defined ( __clang__ ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) \
27 /* IAR */ || (defined ( __ICCARM__ ) && defined ( __ARMVFP__ )) \
28 /* GNU */ || (defined ( __GNUC__ ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) )
29 #define USE_FPU 1
30 #else
31 #define USE_FPU 0
32 #endif
33
34 /* exception and interrupt handler table */
35 rt_uint32_t rt_interrupt_from_thread;
36 rt_uint32_t rt_interrupt_to_thread;
37 rt_uint32_t rt_thread_switch_interrupt_flag;
38
39 /* exception hook */
40 static rt_err_t (*rt_exception_hook)(void *context) = RT_NULL;
41
42 struct exception_stack_frame
43 {
44 rt_uint32_t r0;
45 rt_uint32_t r1;
46 rt_uint32_t r2;
47 rt_uint32_t r3;
48 rt_uint32_t r12;
49 rt_uint32_t lr;
50 rt_uint32_t pc;
51 rt_uint32_t psr;
52 };
53
54 struct stack_frame
55 {
56 rt_uint32_t tz;
57 rt_uint32_t lr;
58 rt_uint32_t psplim;
59 rt_uint32_t control;
60
61 /* r4 ~ r11 register */
62 rt_uint32_t r4;
63 rt_uint32_t r5;
64 rt_uint32_t r6;
65 rt_uint32_t r7;
66 rt_uint32_t r8;
67 rt_uint32_t r9;
68 rt_uint32_t r10;
69 rt_uint32_t r11;
70
71 struct exception_stack_frame exception_stack_frame;
72 };
73
74 struct exception_stack_frame_fpu
75 {
76 rt_uint32_t r0;
77 rt_uint32_t r1;
78 rt_uint32_t r2;
79 rt_uint32_t r3;
80 rt_uint32_t r12;
81 rt_uint32_t lr;
82 rt_uint32_t pc;
83 rt_uint32_t psr;
84
85 #if USE_FPU
86 /* FPU register */
87 rt_uint32_t S0;
88 rt_uint32_t S1;
89 rt_uint32_t S2;
90 rt_uint32_t S3;
91 rt_uint32_t S4;
92 rt_uint32_t S5;
93 rt_uint32_t S6;
94 rt_uint32_t S7;
95 rt_uint32_t S8;
96 rt_uint32_t S9;
97 rt_uint32_t S10;
98 rt_uint32_t S11;
99 rt_uint32_t S12;
100 rt_uint32_t S13;
101 rt_uint32_t S14;
102 rt_uint32_t S15;
103 rt_uint32_t FPSCR;
104 rt_uint32_t NO_NAME;
105 #endif
106 };
107
108 struct stack_frame_fpu
109 {
110 rt_uint32_t flag;
111
112 /* r4 ~ r11 register */
113 rt_uint32_t r4;
114 rt_uint32_t r5;
115 rt_uint32_t r6;
116 rt_uint32_t r7;
117 rt_uint32_t r8;
118 rt_uint32_t r9;
119 rt_uint32_t r10;
120 rt_uint32_t r11;
121
122 #if USE_FPU
123 /* FPU register s16 ~ s31 */
124 rt_uint32_t s16;
125 rt_uint32_t s17;
126 rt_uint32_t s18;
127 rt_uint32_t s19;
128 rt_uint32_t s20;
129 rt_uint32_t s21;
130 rt_uint32_t s22;
131 rt_uint32_t s23;
132 rt_uint32_t s24;
133 rt_uint32_t s25;
134 rt_uint32_t s26;
135 rt_uint32_t s27;
136 rt_uint32_t s28;
137 rt_uint32_t s29;
138 rt_uint32_t s30;
139 rt_uint32_t s31;
140 #endif
141
142 struct exception_stack_frame_fpu exception_stack_frame;
143 };
144
rt_hw_stack_init(void * tentry,void * parameter,rt_uint8_t * stack_addr,void * texit)145 rt_uint8_t *rt_hw_stack_init(void *tentry,
146 void *parameter,
147 rt_uint8_t *stack_addr,
148 void *texit)
149 {
150 struct stack_frame *stack_frame;
151 rt_uint8_t *stk;
152 unsigned long i;
153
154 stk = stack_addr + sizeof(rt_uint32_t);
155 stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
156 stk -= sizeof(struct stack_frame);
157
158 stack_frame = (struct stack_frame *)stk;
159
160 /* init all register */
161 for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
162 {
163 ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
164 }
165
166 stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
167 stack_frame->exception_stack_frame.r1 = 0; /* r1 */
168 stack_frame->exception_stack_frame.r2 = 0; /* r2 */
169 stack_frame->exception_stack_frame.r3 = 0; /* r3 */
170 stack_frame->exception_stack_frame.r12 = 0; /* r12 */
171 stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
172 stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
173 stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */
174
175 stack_frame->tz = 0x00; /* trustzone thread context */
176 /*
177 * Exception return behavior
178 * +--------+---+---+------+-------+------+-------+---+----+
179 * | PREFIX | - | S | DCRS | FType | Mode | SPSEL | - | ES |
180 * +--------+---+---+------+-------+------+-------+---+----+
181 * PREFIX [31:24] - Indicates that this is an EXC_RETURN value. This field reads as 0b11111111.
182 * S [6] - Indicates whether registers have been pushed to a Secure or Non-secure stack.
183 * 0: Non-secure stack used.
184 * 1: Secure stack used.
185 * DCRS [5] - Indicates whether the default stacking rules apply, or whether the callee registers are already on the stack.
186 * 0: Stacking of the callee saved registers is skipped.
187 * 1: Default rules for stacking the callee registers are followed.
188 * FType [4] - In a PE with the Main and Floating-point Extensions:
189 * 0: The PE allocated space on the stack for FP context.
190 * 1: The PE did not allocate space on the stack for FP context.
191 * In a PE without the Floating-point Extension, this bit is Reserved, RES1.
192 * Mode [3] - Indicates the mode that was stacked from.
193 * 0: Handler mode.
194 * 1: Thread mode.
195 * SPSEL [2] - Indicates which stack contains the exception stack frame.
196 * 0: Main stack pointer.
197 * 1: Process stack pointer.
198 * ES [0] - Indicates the Security state the exception was taken to.
199 * 0: Non-secure.
200 * 1: Secure.
201 */
202 #ifdef ARCH_ARM_CORTEX_SECURE
203 stack_frame->lr = 0xfffffffdL;
204 #else
205 stack_frame->lr = 0xffffffbcL;
206 #endif
207 stack_frame->psplim = 0x00;
208 /*
209 * CONTROL register bit assignments
210 * +---+------+------+-------+-------+
211 * | - | SFPA | FPCA | SPSEL | nPRIV |
212 * +---+------+------+-------+-------+
213 * SFPA [3] - Indicates that the floating-point registers contain active state that belongs to the Secure state:
214 * 0: The floating-point registers do not contain state that belongs to the Secure state.
215 * 1: The floating-point registers contain state that belongs to the Secure state.
216 * This bit is not banked between Security states and RAZ/WI from Non-secure state.
217 * FPCA [2] - Indicates whether floating-point context is active:
218 * 0: No floating-point context active.
219 * 1: Floating-point context active.
220 * This bit is used to determine whether to preserve floating-point state when processing an exception.
221 * This bit is not banked between Security states.
222 * SPSEL [1] - Defines the currently active stack pointer:
223 * 0: MSP is the current stack pointer.
224 * 1: PSP is the current stack pointer.
225 * In Handler mode, this bit reads as zero and ignores writes. The CortexM33 core updates this bit automatically onexception return.
226 * This bit is banked between Security states.
227 * nPRIV [0] - Defines the Thread mode privilege level:
228 * 0: Privileged.
229 * 1: Unprivileged.
230 * This bit is banked between Security states.
231 *
232 */
233 stack_frame->control = 0x00000000L;
234
235 /* return task's current stack address */
236 return stk;
237 }
238
239 #ifdef RT_USING_HW_STACK_GUARD
rt_hw_stack_guard_init(rt_thread_t thread)240 void rt_hw_stack_guard_init(rt_thread_t thread)
241 {
242 rt_mem_region_t stack_top_region, stack_bottom_region;
243 rt_ubase_t stack_bottom = (rt_ubase_t)thread->stack_addr;
244 rt_ubase_t stack_top = (rt_ubase_t)((rt_uint8_t *)thread->stack_addr + thread->stack_size);
245 rt_ubase_t stack_bottom_region_start = RT_ALIGN(stack_bottom, MPU_MIN_REGION_SIZE);
246 rt_ubase_t stack_top_region_start = RT_ALIGN_DOWN(stack_top - MPU_MIN_REGION_SIZE, MPU_MIN_REGION_SIZE);
247 stack_top_region.start = (void *)stack_top_region_start;
248 stack_top_region.size = MPU_MIN_REGION_SIZE;
249 stack_top_region.attr = RT_MEM_REGION_P_RO_U_NA;
250 stack_bottom_region.start = (void *)stack_bottom_region_start;
251 stack_bottom_region.size = MPU_MIN_REGION_SIZE;
252 stack_bottom_region.attr = RT_MEM_REGION_P_RO_U_NA;
253 rt_mprotect_add_region(thread, &stack_top_region);
254 rt_mprotect_add_region(thread, &stack_bottom_region);
255 thread->stack_buf = thread->stack_addr;
256 thread->stack_addr = (void *)(stack_bottom_region_start + MPU_MIN_REGION_SIZE);
257 thread->stack_size = (rt_uint32_t)(stack_top_region_start - (rt_ubase_t)thread->stack_addr);
258 }
259 #endif
260
261 /**
262 * This function set the hook, which is invoked on fault exception handling.
263 *
264 * @param exception_handle the exception handling hook function.
265 */
rt_hw_exception_install(rt_err_t (* exception_handle)(void * context))266 void rt_hw_exception_install(rt_err_t (*exception_handle)(void *context))
267 {
268 rt_exception_hook = exception_handle;
269 }
270
271 #define SCB_CFSR (*(volatile const unsigned *)0xE000ED28) /* Configurable Fault Status Register */
272 #define SCB_HFSR (*(volatile const unsigned *)0xE000ED2C) /* HardFault Status Register */
273 #define SCB_MMAR (*(volatile const unsigned *)0xE000ED34) /* MemManage Fault Address register */
274 #define SCB_BFAR (*(volatile const unsigned *)0xE000ED38) /* Bus Fault Address Register */
275 #define SCB_AIRCR (*(volatile unsigned long *)0xE000ED0C) /* Reset control Address Register */
276 #define SCB_RESET_VALUE 0x05FA0004 /* Reset value, write to SCB_AIRCR can reset cpu */
277
278 #define SCB_CFSR_MFSR (*(volatile const unsigned char*)0xE000ED28) /* Memory-management Fault Status Register */
279 #define SCB_CFSR_BFSR (*(volatile const unsigned char*)0xE000ED29) /* Bus Fault Status Register */
280 #define SCB_CFSR_UFSR (*(volatile const unsigned short*)0xE000ED2A) /* Usage Fault Status Register */
281
282 #ifdef RT_USING_FINSH
usage_fault_track(void)283 static void usage_fault_track(void)
284 {
285 rt_kprintf("usage fault:\n");
286 rt_kprintf("SCB_CFSR_UFSR:0x%02X ", SCB_CFSR_UFSR);
287
288 if(SCB_CFSR_UFSR & (1<<0))
289 {
290 /* [0]:UNDEFINSTR */
291 rt_kprintf("UNDEFINSTR ");
292 }
293
294 if(SCB_CFSR_UFSR & (1<<1))
295 {
296 /* [1]:INVSTATE */
297 rt_kprintf("INVSTATE ");
298 }
299
300 if(SCB_CFSR_UFSR & (1<<2))
301 {
302 /* [2]:INVPC */
303 rt_kprintf("INVPC ");
304 }
305
306 if(SCB_CFSR_UFSR & (1<<3))
307 {
308 /* [3]:NOCP */
309 rt_kprintf("NOCP ");
310 }
311
312 if(SCB_CFSR_UFSR & (1<<8))
313 {
314 /* [8]:UNALIGNED */
315 rt_kprintf("UNALIGNED ");
316 }
317
318 if(SCB_CFSR_UFSR & (1<<9))
319 {
320 /* [9]:DIVBYZERO */
321 rt_kprintf("DIVBYZERO ");
322 }
323
324 rt_kprintf("\n");
325 }
326
bus_fault_track(void)327 static void bus_fault_track(void)
328 {
329 rt_kprintf("bus fault:\n");
330 rt_kprintf("SCB_CFSR_BFSR:0x%02X ", SCB_CFSR_BFSR);
331
332 if(SCB_CFSR_BFSR & (1<<0))
333 {
334 /* [0]:IBUSERR */
335 rt_kprintf("IBUSERR ");
336 }
337
338 if(SCB_CFSR_BFSR & (1<<1))
339 {
340 /* [1]:PRECISERR */
341 rt_kprintf("PRECISERR ");
342 }
343
344 if(SCB_CFSR_BFSR & (1<<2))
345 {
346 /* [2]:IMPRECISERR */
347 rt_kprintf("IMPRECISERR ");
348 }
349
350 if(SCB_CFSR_BFSR & (1<<3))
351 {
352 /* [3]:UNSTKERR */
353 rt_kprintf("UNSTKERR ");
354 }
355
356 if(SCB_CFSR_BFSR & (1<<4))
357 {
358 /* [4]:STKERR */
359 rt_kprintf("STKERR ");
360 }
361
362 if(SCB_CFSR_BFSR & (1<<7))
363 {
364 rt_kprintf("SCB->BFAR:%08X\n", SCB_BFAR);
365 }
366 else
367 {
368 rt_kprintf("\n");
369 }
370 }
371
mem_manage_fault_track(void)372 static void mem_manage_fault_track(void)
373 {
374 rt_kprintf("mem manage fault:\n");
375 rt_kprintf("SCB_CFSR_MFSR:0x%02X ", SCB_CFSR_MFSR);
376
377 if(SCB_CFSR_MFSR & (1<<0))
378 {
379 /* [0]:IACCVIOL */
380 rt_kprintf("IACCVIOL ");
381 }
382
383 if(SCB_CFSR_MFSR & (1<<1))
384 {
385 /* [1]:DACCVIOL */
386 rt_kprintf("DACCVIOL ");
387 }
388
389 if(SCB_CFSR_MFSR & (1<<3))
390 {
391 /* [3]:MUNSTKERR */
392 rt_kprintf("MUNSTKERR ");
393 }
394
395 if(SCB_CFSR_MFSR & (1<<4))
396 {
397 /* [4]:MSTKERR */
398 rt_kprintf("MSTKERR ");
399 }
400
401 if(SCB_CFSR_MFSR & (1<<7))
402 {
403 /* [7]:MMARVALID */
404 rt_kprintf("SCB->MMAR:%08X\n", SCB_MMAR);
405 }
406 else
407 {
408 rt_kprintf("\n");
409 }
410 }
411
hard_fault_track(void)412 static void hard_fault_track(void)
413 {
414 if(SCB_HFSR & (1UL<<1))
415 {
416 /* [1]:VECTBL, Indicates hard fault is caused by failed vector fetch. */
417 rt_kprintf("failed vector fetch\n");
418 }
419
420 if(SCB_HFSR & (1UL<<30))
421 {
422 /* [30]:FORCED, Indicates hard fault is taken because of bus fault,
423 memory management fault, or usage fault. */
424 if(SCB_CFSR_BFSR)
425 {
426 bus_fault_track();
427 }
428
429 if(SCB_CFSR_MFSR)
430 {
431 mem_manage_fault_track();
432 }
433
434 if(SCB_CFSR_UFSR)
435 {
436 usage_fault_track();
437 }
438 }
439
440 if(SCB_HFSR & (1UL<<31))
441 {
442 /* [31]:DEBUGEVT, Indicates hard fault is triggered by debug event. */
443 rt_kprintf("debug event\n");
444 }
445 }
446 #endif /* RT_USING_FINSH */
447
448 struct exception_info
449 {
450 rt_uint32_t exc_return;
451 struct stack_frame stack_frame;
452 };
453
rt_hw_hard_fault_exception(struct exception_info * exception_info)454 void rt_hw_hard_fault_exception(struct exception_info *exception_info)
455 {
456 #if defined(RT_USING_FINSH) && defined(MSH_USING_BUILT_IN_COMMANDS)
457 extern long list_thread(void);
458 #endif
459 struct exception_stack_frame *exception_stack = &exception_info->stack_frame.exception_stack_frame;
460 struct stack_frame *context = &exception_info->stack_frame;
461
462 if (rt_exception_hook != RT_NULL)
463 {
464 rt_err_t result;
465
466 result = rt_exception_hook(exception_stack);
467 if (result == RT_EOK) return;
468 }
469
470 rt_kprintf("psr: 0x%08x\n", context->exception_stack_frame.psr);
471
472 rt_kprintf("r00: 0x%08x\n", context->exception_stack_frame.r0);
473 rt_kprintf("r01: 0x%08x\n", context->exception_stack_frame.r1);
474 rt_kprintf("r02: 0x%08x\n", context->exception_stack_frame.r2);
475 rt_kprintf("r03: 0x%08x\n", context->exception_stack_frame.r3);
476 rt_kprintf("r04: 0x%08x\n", context->r4);
477 rt_kprintf("r05: 0x%08x\n", context->r5);
478 rt_kprintf("r06: 0x%08x\n", context->r6);
479 rt_kprintf("r07: 0x%08x\n", context->r7);
480 rt_kprintf("r08: 0x%08x\n", context->r8);
481 rt_kprintf("r09: 0x%08x\n", context->r9);
482 rt_kprintf("r10: 0x%08x\n", context->r10);
483 rt_kprintf("r11: 0x%08x\n", context->r11);
484 rt_kprintf("r12: 0x%08x\n", context->exception_stack_frame.r12);
485 rt_kprintf(" lr: 0x%08x\n", context->exception_stack_frame.lr);
486 rt_kprintf(" pc: 0x%08x\n", context->exception_stack_frame.pc);
487
488 if (exception_info->exc_return & (1 << 2))
489 {
490 rt_kprintf("hard fault on thread: %s\r\n\r\n", rt_thread_self()->parent.name);
491
492 #if defined(RT_USING_FINSH) && defined(MSH_USING_BUILT_IN_COMMANDS)
493 list_thread();
494 #endif
495 }
496 else
497 {
498 rt_kprintf("hard fault on handler\r\n\r\n");
499 }
500
501 if ( (exception_info->exc_return & 0x10) == 0)
502 {
503 rt_kprintf("FPU active!\r\n");
504 }
505
506 #ifdef RT_USING_FINSH
507 hard_fault_track();
508 #endif /* RT_USING_FINSH */
509
510 while (1);
511 }
512
513 /**
514 * reset CPU
515 */
rt_hw_cpu_reset(void)516 void rt_hw_cpu_reset(void)
517 {
518 SCB_AIRCR = SCB_RESET_VALUE;
519 }
520
521 #ifdef RT_USING_CPU_FFS
522 /**
523 * This function finds the first bit set (beginning with the least significant bit)
524 * in value and return the index of that bit.
525 *
526 * Bits are numbered starting at 1 (the least significant bit). A return value of
527 * zero from any of these functions means that the argument was zero.
528 *
529 * @return return the index of the first bit set. If value is 0, then this function
530 * shall return 0.
531 */
532 #if defined(__CC_ARM)
__rt_ffs(int value)533 __asm int __rt_ffs(int value)
534 {
535 CMP r0, #0x00
536 BEQ exit
537
538 RBIT r0, r0
539 CLZ r0, r0
540 ADDS r0, r0, #0x01
541
542 exit
543 BX lr
544 }
545 #elif defined(__clang__)
__rt_ffs(int value)546 int __rt_ffs(int value)
547 {
548 if (value == 0) return value;
549
550 __asm volatile(
551 "RBIT r0, r0 \n"
552 "CLZ r0, r0 \n"
553 "ADDS r0, r0, #0x01 \n"
554
555 : "=r"(value)
556 : "r"(value)
557 );
558 return value;
559 }
560 #elif defined(__IAR_SYSTEMS_ICC__)
__rt_ffs(int value)561 int __rt_ffs(int value)
562 {
563 if (value == 0) return value;
564
565 asm("RBIT %0, %1" : "=r"(value) : "r"(value));
566 asm("CLZ %0, %1" : "=r"(value) : "r"(value));
567 asm("ADDS %0, %1, #0x01" : "=r"(value) : "r"(value));
568
569 return value;
570 }
571 #elif defined(__GNUC__)
__rt_ffs(int value)572 int __rt_ffs(int value)
573 {
574 return __builtin_ffs(value);
575 }
576 #endif
577
578 #endif
579