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
22 #if /* ARMCC */ ( (defined ( __CC_ARM ) && defined ( __TARGET_FPU_VFP )) \
23 /* Clang */ || (defined ( __CLANG_ARM ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) \
24 /* IAR */ || (defined ( __ICCARM__ ) && defined ( __ARMVFP__ )) \
25 /* GNU */ || (defined ( __GNUC__ ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) )
26 #define USE_FPU 1
27 #else
28 #define USE_FPU 0
29 #endif
30
31 /* exception and interrupt handler table */
32 rt_uint32_t rt_interrupt_from_thread;
33 rt_uint32_t rt_interrupt_to_thread;
34 rt_uint32_t rt_thread_switch_interrupt_flag;
35 /* exception hook */
36 static rt_err_t (*rt_exception_hook)(void *context) = RT_NULL;
37
38 struct exception_stack_frame
39 {
40 rt_uint32_t r0;
41 rt_uint32_t r1;
42 rt_uint32_t r2;
43 rt_uint32_t r3;
44 rt_uint32_t r12;
45 rt_uint32_t lr;
46 rt_uint32_t pc;
47 rt_uint32_t psr;
48 };
49
50 struct stack_frame
51 {
52 #if USE_FPU
53 rt_uint32_t flag;
54 #endif /* USE_FPU */
55
56 /* r4 ~ r11 register */
57 rt_uint32_t r4;
58 rt_uint32_t r5;
59 rt_uint32_t r6;
60 rt_uint32_t r7;
61 rt_uint32_t r8;
62 rt_uint32_t r9;
63 rt_uint32_t r10;
64 rt_uint32_t r11;
65
66 struct exception_stack_frame exception_stack_frame;
67 };
68
69 struct exception_stack_frame_fpu
70 {
71 rt_uint32_t r0;
72 rt_uint32_t r1;
73 rt_uint32_t r2;
74 rt_uint32_t r3;
75 rt_uint32_t r12;
76 rt_uint32_t lr;
77 rt_uint32_t pc;
78 rt_uint32_t psr;
79
80 #if USE_FPU
81 /* FPU register */
82 rt_uint32_t S0;
83 rt_uint32_t S1;
84 rt_uint32_t S2;
85 rt_uint32_t S3;
86 rt_uint32_t S4;
87 rt_uint32_t S5;
88 rt_uint32_t S6;
89 rt_uint32_t S7;
90 rt_uint32_t S8;
91 rt_uint32_t S9;
92 rt_uint32_t S10;
93 rt_uint32_t S11;
94 rt_uint32_t S12;
95 rt_uint32_t S13;
96 rt_uint32_t S14;
97 rt_uint32_t S15;
98 rt_uint32_t FPSCR;
99 rt_uint32_t NO_NAME;
100 #endif
101 };
102
103 struct stack_frame_fpu
104 {
105 rt_uint32_t flag;
106
107 /* r4 ~ r11 register */
108 rt_uint32_t r4;
109 rt_uint32_t r5;
110 rt_uint32_t r6;
111 rt_uint32_t r7;
112 rt_uint32_t r8;
113 rt_uint32_t r9;
114 rt_uint32_t r10;
115 rt_uint32_t r11;
116
117 #if USE_FPU
118 /* FPU register s16 ~ s31 */
119 rt_uint32_t s16;
120 rt_uint32_t s17;
121 rt_uint32_t s18;
122 rt_uint32_t s19;
123 rt_uint32_t s20;
124 rt_uint32_t s21;
125 rt_uint32_t s22;
126 rt_uint32_t s23;
127 rt_uint32_t s24;
128 rt_uint32_t s25;
129 rt_uint32_t s26;
130 rt_uint32_t s27;
131 rt_uint32_t s28;
132 rt_uint32_t s29;
133 rt_uint32_t s30;
134 rt_uint32_t s31;
135 #endif
136
137 struct exception_stack_frame_fpu exception_stack_frame;
138 };
139
rt_hw_stack_init(void * tentry,void * parameter,rt_uint8_t * stack_addr,void * texit)140 rt_uint8_t *rt_hw_stack_init(void *tentry,
141 void *parameter,
142 rt_uint8_t *stack_addr,
143 void *texit)
144 {
145 struct stack_frame *stack_frame;
146 rt_uint8_t *stk;
147 unsigned long i;
148
149 stk = stack_addr + sizeof(rt_uint32_t);
150 stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
151 stk -= sizeof(struct stack_frame);
152
153 stack_frame = (struct stack_frame *)stk;
154
155 /* init all register */
156 for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
157 {
158 ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
159 }
160
161 stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
162 stack_frame->exception_stack_frame.r1 = 0; /* r1 */
163 stack_frame->exception_stack_frame.r2 = 0; /* r2 */
164 stack_frame->exception_stack_frame.r3 = 0; /* r3 */
165 stack_frame->exception_stack_frame.r12 = 0; /* r12 */
166 stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
167 stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
168 stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */
169
170 #if USE_FPU
171 stack_frame->flag = 0;
172 #endif /* USE_FPU */
173
174 /* return task's current stack address */
175 return stk;
176 }
177
178 /**
179 * This function set the hook, which is invoked on fault exception handling.
180 *
181 * @param exception_handle the exception handling hook function.
182 */
rt_hw_exception_install(rt_err_t (* exception_handle)(void * context))183 void rt_hw_exception_install(rt_err_t (*exception_handle)(void *context))
184 {
185 rt_exception_hook = exception_handle;
186 }
187
188 #define SCB_CFSR (*(volatile const unsigned *)0xE000ED28) /* Configurable Fault Status Register */
189 #define SCB_HFSR (*(volatile const unsigned *)0xE000ED2C) /* HardFault Status Register */
190 #define SCB_MMAR (*(volatile const unsigned *)0xE000ED34) /* MemManage Fault Address register */
191 #define SCB_BFAR (*(volatile const unsigned *)0xE000ED38) /* Bus Fault Address Register */
192 #define SCB_AIRCR (*(volatile unsigned long *)0xE000ED0C) /* Reset control Address Register */
193 #define SCB_RESET_VALUE 0x05FA0004 /* Reset value, write to SCB_AIRCR can reset cpu */
194
195 #define SCB_CFSR_MFSR (*(volatile const unsigned char*)0xE000ED28) /* Memory-management Fault Status Register */
196 #define SCB_CFSR_BFSR (*(volatile const unsigned char*)0xE000ED29) /* Bus Fault Status Register */
197 #define SCB_CFSR_UFSR (*(volatile const unsigned short*)0xE000ED2A) /* Usage Fault Status Register */
198
199 #ifdef RT_USING_FINSH
usage_fault_track(void)200 static void usage_fault_track(void)
201 {
202 rt_kprintf("usage fault:\n");
203 rt_kprintf("SCB_CFSR_UFSR:0x%02X ", SCB_CFSR_UFSR);
204
205 if(SCB_CFSR_UFSR & (1<<0))
206 {
207 /* [0]:UNDEFINSTR */
208 rt_kprintf("UNDEFINSTR ");
209 }
210
211 if(SCB_CFSR_UFSR & (1<<1))
212 {
213 /* [1]:INVSTATE */
214 rt_kprintf("INVSTATE ");
215 }
216
217 if(SCB_CFSR_UFSR & (1<<2))
218 {
219 /* [2]:INVPC */
220 rt_kprintf("INVPC ");
221 }
222
223 if(SCB_CFSR_UFSR & (1<<3))
224 {
225 /* [3]:NOCP */
226 rt_kprintf("NOCP ");
227 }
228
229 if(SCB_CFSR_UFSR & (1<<8))
230 {
231 /* [8]:UNALIGNED */
232 rt_kprintf("UNALIGNED ");
233 }
234
235 if(SCB_CFSR_UFSR & (1<<9))
236 {
237 /* [9]:DIVBYZERO */
238 rt_kprintf("DIVBYZERO ");
239 }
240
241 rt_kprintf("\n");
242 }
243
bus_fault_track(void)244 static void bus_fault_track(void)
245 {
246 rt_kprintf("bus fault:\n");
247 rt_kprintf("SCB_CFSR_BFSR:0x%02X ", SCB_CFSR_BFSR);
248
249 if(SCB_CFSR_BFSR & (1<<0))
250 {
251 /* [0]:IBUSERR */
252 rt_kprintf("IBUSERR ");
253 }
254
255 if(SCB_CFSR_BFSR & (1<<1))
256 {
257 /* [1]:PRECISERR */
258 rt_kprintf("PRECISERR ");
259 }
260
261 if(SCB_CFSR_BFSR & (1<<2))
262 {
263 /* [2]:IMPRECISERR */
264 rt_kprintf("IMPRECISERR ");
265 }
266
267 if(SCB_CFSR_BFSR & (1<<3))
268 {
269 /* [3]:UNSTKERR */
270 rt_kprintf("UNSTKERR ");
271 }
272
273 if(SCB_CFSR_BFSR & (1<<4))
274 {
275 /* [4]:STKERR */
276 rt_kprintf("STKERR ");
277 }
278
279 if(SCB_CFSR_BFSR & (1<<7))
280 {
281 rt_kprintf("SCB->BFAR:%08X\n", SCB_BFAR);
282 }
283 else
284 {
285 rt_kprintf("\n");
286 }
287 }
288
mem_manage_fault_track(void)289 static void mem_manage_fault_track(void)
290 {
291 rt_kprintf("mem manage fault:\n");
292 rt_kprintf("SCB_CFSR_MFSR:0x%02X ", SCB_CFSR_MFSR);
293
294 if(SCB_CFSR_MFSR & (1<<0))
295 {
296 /* [0]:IACCVIOL */
297 rt_kprintf("IACCVIOL ");
298 }
299
300 if(SCB_CFSR_MFSR & (1<<1))
301 {
302 /* [1]:DACCVIOL */
303 rt_kprintf("DACCVIOL ");
304 }
305
306 if(SCB_CFSR_MFSR & (1<<3))
307 {
308 /* [3]:MUNSTKERR */
309 rt_kprintf("MUNSTKERR ");
310 }
311
312 if(SCB_CFSR_MFSR & (1<<4))
313 {
314 /* [4]:MSTKERR */
315 rt_kprintf("MSTKERR ");
316 }
317
318 if(SCB_CFSR_MFSR & (1<<7))
319 {
320 /* [7]:MMARVALID */
321 rt_kprintf("SCB->MMAR:%08X\n", SCB_MMAR);
322 }
323 else
324 {
325 rt_kprintf("\n");
326 }
327 }
328
hard_fault_track(void)329 static void hard_fault_track(void)
330 {
331 if(SCB_HFSR & (1UL<<1))
332 {
333 /* [1]:VECTBL, Indicates hard fault is caused by failed vector fetch. */
334 rt_kprintf("failed vector fetch\n");
335 }
336
337 if(SCB_HFSR & (1UL<<30))
338 {
339 /* [30]:FORCED, Indicates hard fault is taken because of bus fault,
340 memory management fault, or usage fault. */
341 if(SCB_CFSR_BFSR)
342 {
343 bus_fault_track();
344 }
345
346 if(SCB_CFSR_MFSR)
347 {
348 mem_manage_fault_track();
349 }
350
351 if(SCB_CFSR_UFSR)
352 {
353 usage_fault_track();
354 }
355 }
356
357 if(SCB_HFSR & (1UL<<31))
358 {
359 /* [31]:DEBUGEVT, Indicates hard fault is triggered by debug event. */
360 rt_kprintf("debug event\n");
361 }
362 }
363 #endif /* RT_USING_FINSH */
364
365 struct exception_info
366 {
367 rt_uint32_t exc_return;
368 struct stack_frame stack_frame;
369 };
370
rt_hw_hard_fault_exception(struct exception_info * exception_info)371 void rt_hw_hard_fault_exception(struct exception_info *exception_info)
372 {
373 extern long list_thread(void);
374 struct exception_stack_frame *exception_stack = &exception_info->stack_frame.exception_stack_frame;
375 struct stack_frame *context = &exception_info->stack_frame;
376
377 if (rt_exception_hook != RT_NULL)
378 {
379 rt_err_t result;
380
381 result = rt_exception_hook(exception_stack);
382 if (result == RT_EOK) return;
383 }
384
385 rt_kprintf("psr: 0x%08x\n", context->exception_stack_frame.psr);
386
387 rt_kprintf("r00: 0x%08x\n", context->exception_stack_frame.r0);
388 rt_kprintf("r01: 0x%08x\n", context->exception_stack_frame.r1);
389 rt_kprintf("r02: 0x%08x\n", context->exception_stack_frame.r2);
390 rt_kprintf("r03: 0x%08x\n", context->exception_stack_frame.r3);
391 rt_kprintf("r04: 0x%08x\n", context->r4);
392 rt_kprintf("r05: 0x%08x\n", context->r5);
393 rt_kprintf("r06: 0x%08x\n", context->r6);
394 rt_kprintf("r07: 0x%08x\n", context->r7);
395 rt_kprintf("r08: 0x%08x\n", context->r8);
396 rt_kprintf("r09: 0x%08x\n", context->r9);
397 rt_kprintf("r10: 0x%08x\n", context->r10);
398 rt_kprintf("r11: 0x%08x\n", context->r11);
399 rt_kprintf("r12: 0x%08x\n", context->exception_stack_frame.r12);
400 rt_kprintf(" lr: 0x%08x\n", context->exception_stack_frame.lr);
401 rt_kprintf(" pc: 0x%08x\n", context->exception_stack_frame.pc);
402
403 if (exception_info->exc_return & (1 << 2))
404 {
405 rt_kprintf("hard fault on thread: %s\r\n\r\n", rt_thread_self()->parent.name);
406
407 #ifdef RT_USING_FINSH
408 list_thread();
409 #endif
410 }
411 else
412 {
413 rt_kprintf("hard fault on handler\r\n\r\n");
414 }
415
416 if ( (exception_info->exc_return & 0x10) == 0)
417 {
418 rt_kprintf("FPU active!\r\n");
419 }
420
421 #ifdef RT_USING_FINSH
422 hard_fault_track();
423 #endif /* RT_USING_FINSH */
424
425 while (1);
426 }
427
428 /**
429 * shutdown CPU
430 */
rt_hw_cpu_shutdown(void)431 void rt_hw_cpu_shutdown(void)
432 {
433 rt_kprintf("shutdown...\n");
434
435 RT_ASSERT(0);
436 }
437
438 /**
439 * reset CPU
440 */
rt_hw_cpu_reset(void)441 rt_weak void rt_hw_cpu_reset(void)
442 {
443 SCB_AIRCR = SCB_RESET_VALUE;
444 }
445
TaskSwitch_StackCheck(void)446 void TaskSwitch_StackCheck(void)
447 {
448 volatile rt_uint32_t end_of_stack_val = (rt_uint32_t) rt_thread_self()->stack_addr;
449 __asm volatile ("MSR psplim, %0" : : "r" (end_of_stack_val));
450 }
451
452
453 #ifdef RT_USING_CPU_FFS
454 /**
455 * This function finds the first bit set (beginning with the least significant bit)
456 * in value and return the index of that bit.
457 *
458 * Bits are numbered starting at 1 (the least significant bit). A return value of
459 * zero from any of these functions means that the argument was zero.
460 *
461 * @return return the index of the first bit set. If value is 0, then this function
462 * shall return 0.
463 */
464 #if defined(__CC_ARM)
__rt_ffs(int value)465 __asm int __rt_ffs(int value)
466 {
467 CMP r0, #0x00
468 BEQ exit
469
470 RBIT r0, r0
471 CLZ r0, r0
472 ADDS r0, r0, #0x01
473
474 exit
475 BX lr
476 }
477 #elif defined(__CLANG_ARM)
__rt_ffs(int value)478 int __rt_ffs(int value)
479 {
480 __asm volatile(
481 "CMP r0, #0x00 \n"
482 "BEQ exit \n"
483
484 "RBIT r0, r0 \n"
485 "CLZ r0, r0 \n"
486 "ADDS r0, r0, #0x01 \n"
487
488 "exit: \n"
489
490 : "=r"(value)
491 : "r"(value)
492 );
493 return value;
494 }
495 #elif defined(__IAR_SYSTEMS_ICC__)
__rt_ffs(int value)496 int __rt_ffs(int value)
497 {
498 if (value == 0) return value;
499
500 asm("RBIT %0, %1" : "=r"(value) : "r"(value));
501 asm("CLZ %0, %1" : "=r"(value) : "r"(value));
502 asm("ADDS %0, %1, #0x01" : "=r"(value) : "r"(value));
503
504 return value;
505 }
506 #elif defined(__GNUC__)
__rt_ffs(int value)507 int __rt_ffs(int value)
508 {
509 return __builtin_ffs(value);
510 }
511 #endif
512
513 #endif
514