Contiki-NG
Loading...
Searching...
No Matches
hardfault-handler.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * \addtogroup nrf
33 * @{
34 *
35 * \addtogroup nrf-arm ARM Handler
36 * @{
37 *
38 * \addtogroup nrf-hardfault Hardfault Handler
39 * @{
40 *
41 * \file
42 * Hardfault Handler implementation for the nRF.
43 * \author
44 * Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
45 *
46 */
47/*---------------------------------------------------------------------------*/
48#include "contiki.h"
49
50#include "cmsis_compiler.h"
51#include "nrf.h"
52
53/*---------------------------------------------------------------------------*/
54#if NRF_HARDFAULT_HANDLER_EXTENDED
55/*---------------------------------------------------------------------------*/
56typedef struct HardFault_stack { /**< HardFault Stack */
57 uint32_t r0; /**< R0 register. */
58 uint32_t r1; /**< R1 register. */
59 uint32_t r2; /**< R2 register. */
60 uint32_t r3; /**< R3 register. */
61 uint32_t r12; /**< R12 register. */
62 uint32_t lr; /**< Link register. */
63 uint32_t pc; /**< Program counter. */
64 uint32_t psr; /**< Program status register. */
65} HardFault_stack_t;
66/*---------------------------------------------------------------------------*/
67/*
68 * Crash info saved to .noinit RAM — survives NVIC_SystemReset().
69 * The startup code does not zero .noinit, so the magic value persists.
70 * At boot, the radio driver checks this and prints the crash dump.
71 */
72#define CRASH_MAGIC 0xDEADF007UL
73#define HF_CANARY_VALUE 0xFA17FA17UL
74
75typedef struct {
76 uint32_t magic;
77 uint32_t pc;
78 uint32_t lr;
79 uint32_t psr;
80 uint32_t cfsr;
81 uint32_t hfsr;
82 uint32_t r0;
83 uint32_t r1;
84 uint32_t r2;
85 uint32_t r3;
86 uint32_t r12;
87 uint32_t mmfar;
88 uint32_t bfar;
89} crash_info_t;
90
91__attribute__((section(".noinit"))) crash_info_t crash_info;
92/*---------------------------------------------------------------------------*/
93/**
94 * @brief Check and print any saved crash info from a previous fault.
95 * Call this early in boot (after UART is initialized).
96 */
97void
98hardfault_print_saved_crash(void)
99{
100 extern int dbg_putchar(int c);
101 static const char hx[] = "0123456789abcdef";
102 extern volatile uint32_t hf_canary;
103
104 /* Stay quiet on normal boots: .noinit contains arbitrary stale RAM. */
105 if(hf_canary == HF_CANARY_VALUE) {
106 dbg_putchar('C');
107 dbg_putchar('=');
108 { uint32_t c = hf_canary;
109 for(int s = 28; s >= 0; s -= 4) dbg_putchar(hx[(c >> s) & 0xf]);
110 }
111 dbg_putchar(' ');
112 dbg_putchar('M');
113 dbg_putchar('=');
114 { uint32_t m = crash_info.magic;
115 for(int s = 28; s >= 0; s -= 4) dbg_putchar(hx[(m >> s) & 0xf]);
116 }
117 dbg_putchar('\n');
118 dbg_putchar('!'); dbg_putchar('H'); dbg_putchar('F'); dbg_putchar('\n');
119 hf_canary = 0; /* Clear canary */
120 } else if(crash_info.magic != CRASH_MAGIC) {
121 return;
122 } else {
123 dbg_putchar('C');
124 dbg_putchar('=');
125 { uint32_t c = hf_canary;
126 for(int s = 28; s >= 0; s -= 4) dbg_putchar(hx[(c >> s) & 0xf]);
127 }
128 dbg_putchar(' ');
129 dbg_putchar('M');
130 dbg_putchar('=');
131 { uint32_t m = crash_info.magic;
132 for(int s = 28; s >= 0; s -= 4) dbg_putchar(hx[(m >> s) & 0xf]);
133 }
134 dbg_putchar('\n');
135 }
136
137 /* Clear magic so we don't print again on next boot */
138 crash_info.magic = 0;
139
140 static const char hex[] = "0123456789abcdef";
141
142 /* Helper: print a 32-bit hex value using dbg_putchar */
143#define FAULT_PUTHEX(val) do { \
144 uint32_t _v = (val); \
145 for(int _s = 28; _s >= 0; _s -= 4) \
146 dbg_putchar(hex[(_v >> _s) & 0xf]); \
147 } while(0)
148#define FAULT_PUTS(s) do { \
149 const char *_p = (s); \
150 while(*_p) dbg_putchar(*_p++); \
151 } while(0)
152
153 FAULT_PUTS("\n*** PREVIOUS CRASH ***\n");
154 FAULT_PUTS("PC="); FAULT_PUTHEX(crash_info.pc);
155 FAULT_PUTS(" LR="); FAULT_PUTHEX(crash_info.lr);
156 FAULT_PUTS(" PSR="); FAULT_PUTHEX(crash_info.psr);
157 dbg_putchar('\n');
158 FAULT_PUTS("R0="); FAULT_PUTHEX(crash_info.r0);
159 FAULT_PUTS(" R1="); FAULT_PUTHEX(crash_info.r1);
160 FAULT_PUTS(" R2="); FAULT_PUTHEX(crash_info.r2);
161 FAULT_PUTS(" R3="); FAULT_PUTHEX(crash_info.r3);
162 dbg_putchar('\n');
163 FAULT_PUTS("R12="); FAULT_PUTHEX(crash_info.r12);
164 FAULT_PUTS(" CFSR="); FAULT_PUTHEX(crash_info.cfsr);
165 FAULT_PUTS(" HFSR="); FAULT_PUTHEX(crash_info.hfsr);
166 dbg_putchar('\n');
167
168 if(crash_info.cfsr & (1 << 7)) {
169 FAULT_PUTS("MMFAR="); FAULT_PUTHEX(crash_info.mmfar); dbg_putchar('\n');
170 }
171 if(crash_info.cfsr & (1 << 15)) {
172 FAULT_PUTS("BFAR="); FAULT_PUTHEX(crash_info.bfar); dbg_putchar('\n');
173 }
174 FAULT_PUTS("*** END CRASH ***\n");
175
176#undef FAULT_PUTHEX
177#undef FAULT_PUTS
178}
179/*---------------------------------------------------------------------------*/
180/**
181 * @brief Hard fault final handling
182 *
183 */
184__WEAK void
185HardFault_process()
186{
187 NVIC_SystemReset();
188}
189/*---------------------------------------------------------------------------*/
190/**
191 * @brief Hard fault c handler — saves crash info to .noinit RAM and resets.
192 *
193 * @param p_stack_address Pointer to hard fault stack
194 */
195/* Canary to detect if HardFault_c_handler ever runs */
196__attribute__((section(".noinit"))) volatile uint32_t hf_canary;
197
198void
199HardFault_c_handler(uint32_t *p_stack_address)
200{
201 extern int dbg_putchar(int c);
202 static const char hex[] = "0123456789abcdef";
203
204 /* Write canary FIRST */
205 hf_canary = HF_CANARY_VALUE;
206
207 /* Immediately print "HF!" via the platform debug putchar so this
208 * works on every nrf board, not just those with a uarte console. */
209 dbg_putchar('\n');
210 dbg_putchar('H');
211 dbg_putchar('F');
212 dbg_putchar('!');
213 dbg_putchar(' ');
214
215 HardFault_stack_t *p_stack = (HardFault_stack_t *)p_stack_address;
216
217 crash_info.cfsr = SCB->CFSR;
218 crash_info.hfsr = SCB->HFSR;
219 crash_info.mmfar = SCB->MMFAR;
220 crash_info.bfar = SCB->BFAR;
221
222 if(p_stack != NULL) {
223 crash_info.pc = p_stack->pc;
224 crash_info.lr = p_stack->lr;
225 crash_info.psr = p_stack->psr;
226 crash_info.r0 = p_stack->r0;
227 crash_info.r1 = p_stack->r1;
228 crash_info.r2 = p_stack->r2;
229 crash_info.r3 = p_stack->r3;
230 crash_info.r12 = p_stack->r12;
231 }
232
233 /* Write magic last — signals that crash_info is valid */
234 crash_info.magic = CRASH_MAGIC;
235
236 /* Print crash info via dbg_putchar so this works on every nrf board. */
237 {
238#define HF_PUTHEX(val) do { \
239 uint32_t _v = (val); \
240 for(int _s = 28; _s >= 0; _s -= 4) { \
241 dbg_putchar(hex[(_v >> _s) & 0xf]); \
242 } \
243 } while(0)
244 dbg_putchar('P');
245 dbg_putchar('C');
246 dbg_putchar('=');
247 HF_PUTHEX(crash_info.pc);
248 dbg_putchar(' ');
249 dbg_putchar('L');
250 dbg_putchar('R');
251 dbg_putchar('=');
252 HF_PUTHEX(crash_info.lr);
253 dbg_putchar(' ');
254 dbg_putchar('C');
255 dbg_putchar('F');
256 dbg_putchar('=');
257 HF_PUTHEX(crash_info.cfsr);
258 dbg_putchar('\n');
259#undef HF_PUTHEX
260 }
261
262 HardFault_process();
263}
264/*---------------------------------------------------------------------------*/
265/**
266 * @brief Hardfault handler
267 *
268 */
269void HardFault_Handler(void) __attribute__((naked));
270/*---------------------------------------------------------------------------*/
271/**
272 * @brief Hardfault handler
273 *
274 */
275void
277{
278 __ASM volatile (
279 " .syntax unified \n"
280
281 " ldr r0, =0xFFFFFFFD \n"
282 " cmp r0, lr \n"
283 " bne HardFault_Handler_ChooseMSP \n"
284 /* Reading PSP into R0 */
285 " mrs r0, PSP \n"
286 " b HardFault_Handler_Continue \n"
287 "HardFault_Handler_ChooseMSP: \n"
288 /* Reading MSP into R0 */
289 " mrs r0, MSP \n"
290 /* -----------------------------------------------------------------
291 * If we have selected MSP check if we may use stack safetly.
292 * If not - reset the stack to the initial value. */
293 " ldr r1, =__StackTop \n"
294 " ldr r2, =__StackLimit \n"
295
296 /* MSP is in the range of the stack area */
297 " cmp r0, r1 \n"
298 " bhi HardFault_MoveSP \n"
299 " cmp r0, r2 \n"
300 " bhi HardFault_Handler_Continue \n"
301 /* ----------------------------------------------------------------- */
302 "HardFault_MoveSP: \n"
303 " mov SP, r1 \n"
304 " movs r0, #0 \n"
305
306 "HardFault_Handler_Continue: \n"
307 " ldr r3, =%0 \n"
308 " bx r3 \n"
309
310 " .ltorg \n"
311 : : "X" (HardFault_c_handler)
312 );
313}
314/*---------------------------------------------------------------------------*/
315#else /* NRF_HARDFAULT_HANDLER_EXTENDED */
316/*---------------------------------------------------------------------------*/
317/**
318 * @brief Hardfault handler
319 *
320 */
321void HardFault_Handler(void);
322/*---------------------------------------------------------------------------*/
323/**
324 * @brief Hardfault handler
325 *
326 */
327void
329{
330 NVIC_SystemReset();
331}
332#endif /* NRF_HARDFAULT_HANDLER_EXTENDED */
333/*---------------------------------------------------------------------------*/
334/*
335 * Override default fault handlers that loop forever (b .) in the startup
336 * assembly. Without these overrides, a BusFault/UsageFault/MemManage/
337 * SecureFault silently freezes the CPU.
338 *
339 * On Cortex-M33 these faults escalate to HardFault by default (unless
340 * individually enabled in SCB->SHCSR). However, if any code enables them
341 * (or if TrustZone routing sends SecureFault directly), these handlers
342 * ensure the system resets with a diagnostic message instead of hanging.
343 */
344/*---------------------------------------------------------------------------*/
345static void
346fault_print_and_reset(char f1, char f2)
347{
348 extern int dbg_putchar(int c);
349 dbg_putchar('\n');
350 dbg_putchar(f1);
351 dbg_putchar(f2);
352 dbg_putchar('!');
353 dbg_putchar('\n');
354 /* Brief delay for the debug output to flush */
355 for(volatile int i = 0; i < 50000; i++) {
356 }
357 NVIC_SystemReset();
358}
359/*---------------------------------------------------------------------------*/
360void BusFault_Handler(void) { fault_print_and_reset('B', 'F'); }
361void UsageFault_Handler(void) { fault_print_and_reset('U', 'F'); }
362void MemoryManagement_Handler(void) { fault_print_and_reset('M', 'M'); }
363#ifndef TRUSTZONE_SECURE
364/* In a TrustZone secure build, tz-fault.c owns SecureFault_Handler. */
365void SecureFault_Handler(void) { fault_print_and_reset('S', 'F'); }
366#endif
367/*---------------------------------------------------------------------------*/
368/**
369 * @}
370 * @}
371 * @}
372 */
int dbg_putchar(int c)
Print a character to debug output.
Definition dbg.c:54
void HardFault_Handler(void)
Hardfault handler.