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