Browse Source

Postmortem Debugging to serial port (#20492)

X-Ryl669 3 years ago
parent
commit
8d28853774
No account linked to committer's email address
34 changed files with 1286 additions and 740 deletions
  1. 7
    0
      Marlin/Configuration_adv.h
  2. 7
    0
      Marlin/src/HAL/AVR/inc/SanityCheck.h
  3. 0
    342
      Marlin/src/HAL/DUE/DebugMonitor.cpp
  4. 3
    0
      Marlin/src/HAL/DUE/HAL.cpp
  5. 91
    0
      Marlin/src/HAL/DUE/HAL_MinSerial.cpp
  6. 1
    1
      Marlin/src/HAL/DUE/inc/SanityCheck.h
  7. 5
    1
      Marlin/src/HAL/ESP32/inc/SanityCheck.h
  8. 5
    1
      Marlin/src/HAL/LINUX/inc/SanityCheck.h
  9. 0
    322
      Marlin/src/HAL/LPC1768/DebugMonitor.cpp
  10. 50
    0
      Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp
  11. 2
    2
      Marlin/src/HAL/LPC1768/inc/SanityCheck.h
  12. 5
    3
      Marlin/src/HAL/LPC1768/main.cpp
  13. 4
    0
      Marlin/src/HAL/STM32/HAL.cpp
  14. 152
    0
      Marlin/src/HAL/STM32/HAL_MinSerial.cpp
  15. 2
    2
      Marlin/src/HAL/STM32/inc/SanityCheck.h
  16. 3
    0
      Marlin/src/HAL/STM32F1/HAL.cpp
  17. 118
    0
      Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp
  18. 2
    2
      Marlin/src/HAL/STM32F1/inc/SanityCheck.h
  19. 5
    1
      Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h
  20. 5
    1
      Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h
  21. 5
    1
      Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h
  22. 33
    0
      Marlin/src/HAL/shared/HAL_MinSerial.cpp
  23. 79
    0
      Marlin/src/HAL/shared/HAL_MinSerial.h
  24. 18
    10
      Marlin/src/HAL/shared/backtrace/backtrace.cpp
  25. 3
    0
      Marlin/src/HAL/shared/backtrace/backtrace.h
  26. 49
    23
      Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp
  27. 379
    0
      Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp
  28. 28
    0
      Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp
  29. 54
    0
      Marlin/src/HAL/shared/cpu_exception/exception_hook.h
  30. 3
    0
      Marlin/src/MarlinCore.cpp
  31. 19
    2
      Marlin/src/gcode/gcode_d.cpp
  32. 104
    0
      buildroot/share/PlatformIO/scripts/exc.S
  33. 29
    0
      buildroot/share/PlatformIO/scripts/fix_framework_weakness.py
  34. 16
    26
      platformio.ini

+ 7
- 0
Marlin/Configuration_adv.h View File

@@ -3732,3 +3732,10 @@
3732 3732
 
3733 3733
 // Enable Marlin dev mode which adds some special commands
3734 3734
 //#define MARLIN_DEV_MODE
3735
+
3736
+/**
3737
+ * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial.
3738
+ * When running in the debugger it will break for debugging. This is useful to help understand
3739
+ * a crash from a remote location. Requires ~400 bytes of SRAM and 5Kb of flash.
3740
+ */
3741
+//#define POSTMORTEM_DEBUGGING

+ 7
- 0
Marlin/src/HAL/AVR/inc/SanityCheck.h View File

@@ -56,3 +56,10 @@
56 56
 #if BOTH(HAS_TMC_SW_SERIAL, MONITOR_DRIVER_STATUS)
57 57
   #error "MONITOR_DRIVER_STATUS causes performance issues when used with SoftwareSerial-connected drivers. Disable MONITOR_DRIVER_STATUS or use hardware serial to continue."
58 58
 #endif
59
+
60
+/**
61
+ * Postmortem debugging
62
+ */
63
+#if ENABLED(POSTMORTEM_DEBUGGING)
64
+  #error "POSTMORTEM_DEBUGGING is not supported on AVR boards."
65
+#endif

+ 0
- 342
Marlin/src/HAL/DUE/DebugMonitor.cpp View File

@@ -1,342 +0,0 @@
1
-/**
2
- * Marlin 3D Printer Firmware
3
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
- *
5
- * Based on Sprinter and grbl.
6
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
- *
8
- * This program is free software: you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation, either version 3 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * This program is distributed in the hope that it will be useful,
14
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
- * GNU General Public License for more details.
17
- *
18
- * You should have received a copy of the GNU General Public License
19
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
- *
21
- */
22
-#ifdef ARDUINO_ARCH_SAM
23
-
24
-#include "../../core/macros.h"
25
-#include "../../core/serial.h"
26
-
27
-#include "../shared/backtrace/unwinder.h"
28
-#include "../shared/backtrace/unwmemaccess.h"
29
-
30
-#include <stdarg.h>
31
-
32
-// Debug monitor that dumps to the Programming port all status when
33
-// an exception or WDT timeout happens - And then resets the board
34
-
35
-// All the Monitor routines must run with interrupts disabled and
36
-// under an ISR execution context. That is why we cannot reuse the
37
-// Serial interrupt routines or any C runtime, as we don't know the
38
-// state we are when running them
39
-
40
-// A SW memory barrier, to ensure GCC does not overoptimize loops
41
-#define sw_barrier() __asm__ volatile("": : :"memory");
42
-
43
-// (re)initialize UART0 as a monitor output to 250000,n,8,1
44
-static void TXBegin() {
45
-
46
-  // Disable UART interrupt in NVIC
47
-  NVIC_DisableIRQ( UART_IRQn );
48
-
49
-  // We NEED memory barriers to ensure Interrupts are actually disabled!
50
-  // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
51
-  __DSB();
52
-  __ISB();
53
-
54
-  // Disable clock
55
-  pmc_disable_periph_clk( ID_UART );
56
-
57
-  // Configure PMC
58
-  pmc_enable_periph_clk( ID_UART );
59
-
60
-  // Disable PDC channel
61
-  UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
62
-
63
-  // Reset and disable receiver and transmitter
64
-  UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
65
-
66
-  // Configure mode: 8bit, No parity, 1 bit stop
67
-  UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO;
68
-
69
-  // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds
70
-  UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4));
71
-
72
-  // Enable receiver and transmitter
73
-  UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
74
-}
75
-
76
-// Send character through UART with no interrupts
77
-static void TX(char c) {
78
-  while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); };
79
-  UART->UART_THR = c;
80
-}
81
-
82
-// Send String through UART
83
-static void TX(const char* s) {
84
-  while (*s) TX(*s++);
85
-}
86
-
87
-static void TXDigit(uint32_t d) {
88
-  if (d < 10) TX((char)(d+'0'));
89
-  else if (d < 16) TX((char)(d+'A'-10));
90
-  else TX('?');
91
-}
92
-
93
-// Send Hex number thru UART
94
-static void TXHex(uint32_t v) {
95
-  TX("0x");
96
-  for (uint8_t i = 0; i < 8; i++, v <<= 4)
97
-    TXDigit((v >> 28) & 0xF);
98
-}
99
-
100
-// Send Decimal number thru UART
101
-static void TXDec(uint32_t v) {
102
-  if (!v) {
103
-    TX('0');
104
-    return;
105
-  }
106
-
107
-  char nbrs[14];
108
-  char *p = &nbrs[0];
109
-  while (v != 0) {
110
-    *p++ = '0' + (v % 10);
111
-    v /= 10;
112
-  }
113
-  do {
114
-    p--;
115
-    TX(*p);
116
-  } while (p != &nbrs[0]);
117
-}
118
-
119
-// Dump a backtrace entry
120
-static bool UnwReportOut(void* ctx, const UnwReport* bte) {
121
-  int* p = (int*)ctx;
122
-
123
-  (*p)++;
124
-  TX('#'); TXDec(*p); TX(" : ");
125
-  TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function);
126
-  TX('+'); TXDec(bte->address - bte->function);
127
-  TX(" PC:");TXHex(bte->address); TX('\n');
128
-  return true;
129
-}
130
-
131
-#ifdef UNW_DEBUG
132
-  void UnwPrintf(const char* format, ...) {
133
-    char dest[256];
134
-    va_list argptr;
135
-    va_start(argptr, format);
136
-    vsprintf(dest, format, argptr);
137
-    va_end(argptr);
138
-    TX(&dest[0]);
139
-  }
140
-#endif
141
-
142
-/* Table of function pointers for passing to the unwinder */
143
-static const UnwindCallbacks UnwCallbacks = {
144
-  UnwReportOut,
145
-  UnwReadW,
146
-  UnwReadH,
147
-  UnwReadB
148
-  #ifdef UNW_DEBUG
149
-   , UnwPrintf
150
-  #endif
151
-};
152
-
153
-/**
154
- * HardFaultHandler_C:
155
- * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
156
- * as the parameter. We can then read the values from the stack and place them
157
- * into local variables for ease of reading.
158
- * We then read the various Fault Status and Address Registers to help decode
159
- * cause of the fault.
160
- * The function ends with a BKPT instruction to force control back into the debugger
161
- */
162
-extern "C"
163
-void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) {
164
-
165
-  static const char* causestr[] = {
166
-    "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC"
167
-  };
168
-
169
-  UnwindFrame btf;
170
-
171
-  // Dump report to the Programming port (interrupts are DISABLED)
172
-  TXBegin();
173
-  TX("\n\n## Software Fault detected ##\n");
174
-  TX("Cause: "); TX(causestr[cause]); TX('\n');
175
-
176
-  TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n');
177
-  TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n');
178
-  TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n');
179
-  TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n');
180
-  TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n');
181
-  TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n');
182
-  TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n');
183
-  TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n');
184
-
185
-  // Configurable Fault Status Register
186
-  // Consists of MMSR, BFSR and UFSR
187
-  TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n');
188
-
189
-  // Hard Fault Status Register
190
-  TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n');
191
-
192
-  // Debug Fault Status Register
193
-  TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n');
194
-
195
-  // Auxiliary Fault Status Register
196
-  TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n');
197
-
198
-  // Read the Fault Address Registers. These may not contain valid values.
199
-  // Check BFARVALID/MMARVALID to see if they are valid values
200
-  // MemManage Fault Address Register
201
-  TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n');
202
-
203
-  // Bus Fault Address Register
204
-  TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n');
205
-
206
-  TX("ExcLR: "); TXHex(lr); TX('\n');
207
-  TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n');
208
-
209
-  btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer
210
-  btf.fp = btf.sp;
211
-  btf.lr = ((unsigned long)sp[5]);
212
-  btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it
213
-
214
-  // Perform a backtrace
215
-  TX("\nBacktrace:\n\n");
216
-  int ctr = 0;
217
-  UnwindStart(&btf, &UnwCallbacks, &ctr);
218
-
219
-  // Disable all NVIC interrupts
220
-  NVIC->ICER[0] = 0xFFFFFFFF;
221
-  NVIC->ICER[1] = 0xFFFFFFFF;
222
-
223
-  // Relocate VTOR table to default position
224
-  SCB->VTOR = 0;
225
-
226
-  // Disable USB
227
-  otg_disable();
228
-
229
-  // Restart watchdog
230
-  WDT_Restart(WDT);
231
-
232
-  // Reset controller
233
-  NVIC_SystemReset();
234
-  for (;;) WDT_Restart(WDT);
235
-}
236
-
237
-__attribute__((naked)) void NMI_Handler() {
238
-  __asm__ __volatile__ (
239
-    ".syntax unified" "\n\t"
240
-    A("tst lr, #4")
241
-    A("ite eq")
242
-    A("mrseq r0, msp")
243
-    A("mrsne r0, psp")
244
-    A("mov r1,lr")
245
-    A("mov r2,#0")
246
-    A("b HardFault_HandlerC")
247
-  );
248
-}
249
-
250
-__attribute__((naked)) void HardFault_Handler() {
251
-  __asm__ __volatile__ (
252
-    ".syntax unified" "\n\t"
253
-    A("tst lr, #4")
254
-    A("ite eq")
255
-    A("mrseq r0, msp")
256
-    A("mrsne r0, psp")
257
-    A("mov r1,lr")
258
-    A("mov r2,#1")
259
-    A("b HardFault_HandlerC")
260
-  );
261
-}
262
-
263
-__attribute__((naked)) void MemManage_Handler() {
264
-  __asm__ __volatile__ (
265
-    ".syntax unified" "\n\t"
266
-    A("tst lr, #4")
267
-    A("ite eq")
268
-    A("mrseq r0, msp")
269
-    A("mrsne r0, psp")
270
-    A("mov r1,lr")
271
-    A("mov r2,#2")
272
-    A("b HardFault_HandlerC")
273
-  );
274
-}
275
-
276
-__attribute__((naked)) void BusFault_Handler() {
277
-  __asm__ __volatile__ (
278
-    ".syntax unified" "\n\t"
279
-    A("tst lr, #4")
280
-    A("ite eq")
281
-    A("mrseq r0, msp")
282
-    A("mrsne r0, psp")
283
-    A("mov r1,lr")
284
-    A("mov r2,#3")
285
-    A("b HardFault_HandlerC")
286
-  );
287
-}
288
-
289
-__attribute__((naked)) void UsageFault_Handler() {
290
-  __asm__ __volatile__ (
291
-    ".syntax unified" "\n\t"
292
-    A("tst lr, #4")
293
-    A("ite eq")
294
-    A("mrseq r0, msp")
295
-    A("mrsne r0, psp")
296
-    A("mov r1,lr")
297
-    A("mov r2,#4")
298
-    A("b HardFault_HandlerC")
299
-  );
300
-}
301
-
302
-__attribute__((naked)) void DebugMon_Handler() {
303
-  __asm__ __volatile__ (
304
-    ".syntax unified" "\n\t"
305
-    A("tst lr, #4")
306
-    A("ite eq")
307
-    A("mrseq r0, msp")
308
-    A("mrsne r0, psp")
309
-    A("mov r1,lr")
310
-    A("mov r2,#5")
311
-    A("b HardFault_HandlerC")
312
-  );
313
-}
314
-
315
-/* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */
316
-__attribute__((naked)) void WDT_Handler() {
317
-  __asm__ __volatile__ (
318
-    ".syntax unified" "\n\t"
319
-    A("tst lr, #4")
320
-    A("ite eq")
321
-    A("mrseq r0, msp")
322
-    A("mrsne r0, psp")
323
-    A("mov r1,lr")
324
-    A("mov r2,#6")
325
-    A("b HardFault_HandlerC")
326
-  );
327
-}
328
-
329
-__attribute__((naked)) void RSTC_Handler() {
330
-  __asm__ __volatile__ (
331
-    ".syntax unified" "\n\t"
332
-    A("tst lr, #4")
333
-    A("ite eq")
334
-    A("mrseq r0, msp")
335
-    A("mrsne r0, psp")
336
-    A("mov r1,lr")
337
-    A("mov r2,#7")
338
-    A("b HardFault_HandlerC")
339
-  );
340
-}
341
-
342
-#endif // ARDUINO_ARCH_SAM

+ 3
- 0
Marlin/src/HAL/DUE/HAL.cpp View File

@@ -40,6 +40,8 @@ uint16_t HAL_adc_result;
40 40
 // Public functions
41 41
 // ------------------------
42 42
 
43
+TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial());
44
+
43 45
 // HAL initialization task
44 46
 void HAL_init() {
45 47
   // Initialize the USB stack
@@ -47,6 +49,7 @@ void HAL_init() {
47 49
     OUT_WRITE(SDSS, HIGH);  // Try to set SDSS inactive before any other SPI users start up
48 50
   #endif
49 51
   usb_task_init();
52
+  TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler
50 53
 }
51 54
 
52 55
 // HAL idle task

+ 91
- 0
Marlin/src/HAL/DUE/HAL_MinSerial.cpp View File

@@ -0,0 +1,91 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#ifdef ARDUINO_ARCH_SAM
23
+
24
+#include "../../inc/MarlinConfigPre.h"
25
+
26
+#if ENABLED(POSTMORTEM_DEBUGGING)
27
+
28
+#include "../shared/HAL_MinSerial.h"
29
+
30
+#include <stdarg.h>
31
+
32
+static void TXBegin() {
33
+  // Disable UART interrupt in NVIC
34
+  NVIC_DisableIRQ( UART_IRQn );
35
+
36
+  // We NEED memory barriers to ensure Interrupts are actually disabled!
37
+  // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
38
+  __DSB();
39
+  __ISB();
40
+
41
+  // Disable clock
42
+  pmc_disable_periph_clk( ID_UART );
43
+
44
+  // Configure PMC
45
+  pmc_enable_periph_clk( ID_UART );
46
+
47
+  // Disable PDC channel
48
+  UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
49
+
50
+  // Reset and disable receiver and transmitter
51
+  UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
52
+
53
+  // Configure mode: 8bit, No parity, 1 bit stop
54
+  UART->UART_MR = UART_MR_CHMODE_NORMAL | US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO;
55
+
56
+  // Configure baudrate (asynchronous, no oversampling) to BAUDRATE bauds
57
+  UART->UART_BRGR = (SystemCoreClock / (BAUDRATE << 4));
58
+
59
+  // Enable receiver and transmitter
60
+  UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
61
+}
62
+
63
+// A SW memory barrier, to ensure GCC does not overoptimize loops
64
+#define sw_barrier() __asm__ volatile("": : :"memory");
65
+static void TX(char c) {
66
+  while (!(UART->UART_SR & UART_SR_TXRDY)) { WDT_Restart(WDT); sw_barrier(); };
67
+  UART->UART_THR = c;
68
+}
69
+
70
+void install_min_serial() {
71
+  HAL_min_serial_init = &TXBegin;
72
+  HAL_min_serial_out = &TX;
73
+}
74
+
75
+#if DISABLED(DYNAMIC_VECTORTABLE)
76
+extern "C" {
77
+  __attribute__((naked)) void JumpHandler_ASM() {
78
+    __asm__ __volatile__ (
79
+      "b CommonHandler_ASM\n"
80
+    );
81
+  }
82
+  void __attribute__((naked, alias("JumpHandler_ASM"))) HardFault_Handler();
83
+  void __attribute__((naked, alias("JumpHandler_ASM"))) BusFault_Handler();
84
+  void __attribute__((naked, alias("JumpHandler_ASM"))) UsageFault_Handler();
85
+  void __attribute__((naked, alias("JumpHandler_ASM"))) MemManage_Handler();
86
+  void __attribute__((naked, alias("JumpHandler_ASM"))) NMI_Handler();
87
+}
88
+#endif
89
+
90
+#endif // POSTMORTEM_DEBUGGING
91
+#endif // ARDUINO_ARCH_SAM

+ 1
- 1
Marlin/src/HAL/DUE/inc/SanityCheck.h View File

@@ -57,5 +57,5 @@
57 57
 #endif
58 58
 
59 59
 #if HAS_TMC_SW_SERIAL
60
-  #error "TMC220x Software Serial is not supported on this platform."
60
+  #error "TMC220x Software Serial is not supported on the DUE platform."
61 61
 #endif

+ 5
- 1
Marlin/src/HAL/ESP32/inc/SanityCheck.h View File

@@ -30,9 +30,13 @@
30 30
 #endif
31 31
 
32 32
 #if HAS_TMC_SW_SERIAL
33
-  #error "TMC220x Software Serial is not supported on this platform."
33
+  #error "TMC220x Software Serial is not supported on ESP32."
34 34
 #endif
35 35
 
36 36
 #if BOTH(WIFISUPPORT, ESP3D_WIFISUPPORT)
37 37
   #error "Only enable one WiFi option, either WIFISUPPORT or ESP3D_WIFISUPPORT."
38 38
 #endif
39
+
40
+#if ENABLED(POSTMORTEM_DEBUGGING)
41
+  #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32."
42
+#endif

+ 5
- 1
Marlin/src/HAL/LINUX/inc/SanityCheck.h View File

@@ -35,5 +35,9 @@
35 35
 #endif
36 36
 
37 37
 #if HAS_TMC_SW_SERIAL
38
-  #error "TMC220x Software Serial is not supported on this platform."
38
+  #error "TMC220x Software Serial is not supported on LINUX."
39
+#endif
40
+
41
+#if ENABLED(POSTMORTEM_DEBUGGING)
42
+  #error "POSTMORTEM_DEBUGGING is not yet supported on LINUX."
39 43
 #endif

+ 0
- 322
Marlin/src/HAL/LPC1768/DebugMonitor.cpp View File

@@ -1,322 +0,0 @@
1
-/**
2
- * Marlin 3D Printer Firmware
3
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
- *
5
- * Based on Sprinter and grbl.
6
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
- *
8
- * This program is free software: you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation, either version 3 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * This program is distributed in the hope that it will be useful,
14
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
- * GNU General Public License for more details.
17
- *
18
- * You should have received a copy of the GNU General Public License
19
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
- *
21
- */
22
-#ifdef TARGET_LPC1768
23
-
24
-#include "../../core/macros.h"
25
-#include "../../core/serial.h"
26
-#include <stdarg.h>
27
-
28
-#include "../shared/backtrace/unwinder.h"
29
-#include "../shared/backtrace/unwmemaccess.h"
30
-#include "watchdog.h"
31
-#include <debug_frmwrk.h>
32
-
33
-
34
-// Debug monitor that dumps to the Programming port all status when
35
-// an exception or WDT timeout happens - And then resets the board
36
-
37
-// All the Monitor routines must run with interrupts disabled and
38
-// under an ISR execution context. That is why we cannot reuse the
39
-// Serial interrupt routines or any C runtime, as we don't know the
40
-// state we are when running them
41
-
42
-// A SW memory barrier, to ensure GCC does not overoptimize loops
43
-#define sw_barrier() __asm__ volatile("": : :"memory");
44
-
45
-// (re)initialize UART0 as a monitor output to 250000,n,8,1
46
-static void TXBegin() {
47
-}
48
-
49
-// Send character through UART with no interrupts
50
-static void TX(char c) {
51
-  _DBC(c);
52
-}
53
-
54
-// Send String through UART
55
-static void TX(const char* s) {
56
-  while (*s) TX(*s++);
57
-}
58
-
59
-static void TXDigit(uint32_t d) {
60
-  if (d < 10) TX((char)(d+'0'));
61
-  else if (d < 16) TX((char)(d+'A'-10));
62
-  else TX('?');
63
-}
64
-
65
-// Send Hex number thru UART
66
-static void TXHex(uint32_t v) {
67
-  TX("0x");
68
-  for (uint8_t i = 0; i < 8; i++, v <<= 4)
69
-    TXDigit((v >> 28) & 0xF);
70
-}
71
-
72
-// Send Decimal number thru UART
73
-static void TXDec(uint32_t v) {
74
-  if (!v) {
75
-    TX('0');
76
-    return;
77
-  }
78
-
79
-  char nbrs[14];
80
-  char *p = &nbrs[0];
81
-  while (v != 0) {
82
-    *p++ = '0' + (v % 10);
83
-    v /= 10;
84
-  }
85
-  do {
86
-    p--;
87
-    TX(*p);
88
-  } while (p != &nbrs[0]);
89
-}
90
-
91
-// Dump a backtrace entry
92
-static bool UnwReportOut(void* ctx, const UnwReport* bte) {
93
-  int* p = (int*)ctx;
94
-
95
-  (*p)++;
96
-  TX('#'); TXDec(*p); TX(" : ");
97
-  TX(bte->name?bte->name:"unknown"); TX('@'); TXHex(bte->function);
98
-  TX('+'); TXDec(bte->address - bte->function);
99
-  TX(" PC:");TXHex(bte->address); TX('\n');
100
-  return true;
101
-}
102
-
103
-#ifdef UNW_DEBUG
104
-  void UnwPrintf(const char* format, ...) {
105
-    char dest[256];
106
-    va_list argptr;
107
-    va_start(argptr, format);
108
-    vsprintf(dest, format, argptr);
109
-    va_end(argptr);
110
-    TX(&dest[0]);
111
-  }
112
-#endif
113
-
114
-/* Table of function pointers for passing to the unwinder */
115
-static const UnwindCallbacks UnwCallbacks = {
116
-  UnwReportOut,
117
-  UnwReadW,
118
-  UnwReadH,
119
-  UnwReadB
120
-  #ifdef UNW_DEBUG
121
-   ,UnwPrintf
122
-  #endif
123
-};
124
-
125
-
126
-/**
127
- * HardFaultHandler_C:
128
- * This is called from the HardFault_HandlerAsm with a pointer the Fault stack
129
- * as the parameter. We can then read the values from the stack and place them
130
- * into local variables for ease of reading.
131
- * We then read the various Fault Status and Address Registers to help decode
132
- * cause of the fault.
133
- * The function ends with a BKPT instruction to force control back into the debugger
134
- */
135
-extern "C"
136
-void HardFault_HandlerC(unsigned long *sp, unsigned long lr, unsigned long cause) {
137
-
138
-  static const char* causestr[] = {
139
-    "NMI","Hard","Mem","Bus","Usage","Debug","WDT","RSTC"
140
-  };
141
-
142
-  UnwindFrame btf;
143
-
144
-  // Dump report to the Programming port (interrupts are DISABLED)
145
-  TXBegin();
146
-  TX("\n\n## Software Fault detected ##\n");
147
-  TX("Cause: "); TX(causestr[cause]); TX('\n');
148
-
149
-  TX("R0   : "); TXHex(((unsigned long)sp[0])); TX('\n');
150
-  TX("R1   : "); TXHex(((unsigned long)sp[1])); TX('\n');
151
-  TX("R2   : "); TXHex(((unsigned long)sp[2])); TX('\n');
152
-  TX("R3   : "); TXHex(((unsigned long)sp[3])); TX('\n');
153
-  TX("R12  : "); TXHex(((unsigned long)sp[4])); TX('\n');
154
-  TX("LR   : "); TXHex(((unsigned long)sp[5])); TX('\n');
155
-  TX("PC   : "); TXHex(((unsigned long)sp[6])); TX('\n');
156
-  TX("PSR  : "); TXHex(((unsigned long)sp[7])); TX('\n');
157
-
158
-  // Configurable Fault Status Register
159
-  // Consists of MMSR, BFSR and UFSR
160
-  TX("CFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED28)))); TX('\n');
161
-
162
-  // Hard Fault Status Register
163
-  TX("HFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED2C)))); TX('\n');
164
-
165
-  // Debug Fault Status Register
166
-  TX("DFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED30)))); TX('\n');
167
-
168
-  // Auxiliary Fault Status Register
169
-  TX("AFSR : "); TXHex((*((volatile unsigned long *)(0xE000ED3C)))); TX('\n');
170
-
171
-  // Read the Fault Address Registers. These may not contain valid values.
172
-  // Check BFARVALID/MMARVALID to see if they are valid values
173
-  // MemManage Fault Address Register
174
-  TX("MMAR : "); TXHex((*((volatile unsigned long *)(0xE000ED34)))); TX('\n');
175
-
176
-  // Bus Fault Address Register
177
-  TX("BFAR : "); TXHex((*((volatile unsigned long *)(0xE000ED38)))); TX('\n');
178
-
179
-  TX("ExcLR: "); TXHex(lr); TX('\n');
180
-  TX("ExcSP: "); TXHex((unsigned long)sp); TX('\n');
181
-
182
-  btf.sp = ((unsigned long)sp) + 8*4; // The original stack pointer
183
-  btf.fp = btf.sp;
184
-  btf.lr = ((unsigned long)sp[5]);
185
-  btf.pc = ((unsigned long)sp[6]) | 1; // Force Thumb, as CORTEX only support it
186
-
187
-  // Perform a backtrace
188
-  TX("\nBacktrace:\n\n");
189
-  int ctr = 0;
190
-  UnwindStart(&btf, &UnwCallbacks, &ctr);
191
-
192
-  // Disable all NVIC interrupts
193
-  NVIC->ICER[0] = 0xFFFFFFFF;
194
-  NVIC->ICER[1] = 0xFFFFFFFF;
195
-
196
-  // Relocate VTOR table to default position
197
-  SCB->VTOR = 0;
198
-
199
-  // Clear cause of reset to prevent entering smoothie bootstrap
200
-  HAL_clear_reset_source();
201
-
202
-  // Restart watchdog
203
-  #if ENABLED(USE_WATCHDOG)
204
-    //WDT_Restart(WDT);
205
-    watchdog_init();
206
-  #endif
207
-
208
-  // Reset controller
209
-  NVIC_SystemReset();
210
-
211
-  // Nothing below here is compiled because NVIC_SystemReset loops forever
212
-
213
-  for (;;) { TERN_(USE_WATCHDOG, watchdog_init()); }
214
-}
215
-
216
-extern "C" {
217
-__attribute__((naked)) void NMI_Handler() {
218
-  __asm__ __volatile__ (
219
-    ".syntax unified" "\n\t"
220
-    A("tst lr, #4")
221
-    A("ite eq")
222
-    A("mrseq r0, msp")
223
-    A("mrsne r0, psp")
224
-    A("mov r1,lr")
225
-    A("mov r2,#0")
226
-    A("b HardFault_HandlerC")
227
-  );
228
-}
229
-
230
-__attribute__((naked)) void HardFault_Handler() {
231
-  __asm__ __volatile__ (
232
-    ".syntax unified" "\n\t"
233
-    A("tst lr, #4")
234
-    A("ite eq")
235
-    A("mrseq r0, msp")
236
-    A("mrsne r0, psp")
237
-    A("mov r1,lr")
238
-    A("mov r2,#1")
239
-    A("b HardFault_HandlerC")
240
-  );
241
-}
242
-
243
-__attribute__((naked)) void MemManage_Handler() {
244
-  __asm__ __volatile__ (
245
-    ".syntax unified" "\n\t"
246
-    A("tst lr, #4")
247
-    A("ite eq")
248
-    A("mrseq r0, msp")
249
-    A("mrsne r0, psp")
250
-    A("mov r1,lr")
251
-    A("mov r2,#2")
252
-    A("b HardFault_HandlerC")
253
-  );
254
-}
255
-
256
-__attribute__((naked)) void BusFault_Handler() {
257
-  __asm__ __volatile__ (
258
-    ".syntax unified" "\n\t"
259
-    A("tst lr, #4")
260
-    A("ite eq")
261
-    A("mrseq r0, msp")
262
-    A("mrsne r0, psp")
263
-    A("mov r1,lr")
264
-    A("mov r2,#3")
265
-    A("b HardFault_HandlerC")
266
-  );
267
-}
268
-
269
-__attribute__((naked)) void UsageFault_Handler() {
270
-  __asm__ __volatile__ (
271
-    ".syntax unified" "\n\t"
272
-    A("tst lr, #4")
273
-    A("ite eq")
274
-    A("mrseq r0, msp")
275
-    A("mrsne r0, psp")
276
-    A("mov r1,lr")
277
-    A("mov r2,#4")
278
-    A("b HardFault_HandlerC")
279
-  );
280
-}
281
-
282
-__attribute__((naked)) void DebugMon_Handler() {
283
-  __asm__ __volatile__ (
284
-    ".syntax unified" "\n\t"
285
-    A("tst lr, #4")
286
-    A("ite eq")
287
-    A("mrseq r0, msp")
288
-    A("mrsne r0, psp")
289
-    A("mov r1,lr")
290
-    A("mov r2,#5")
291
-    A("b HardFault_HandlerC")
292
-  );
293
-}
294
-
295
-/* This is NOT an exception, it is an interrupt handler - Nevertheless, the framing is the same */
296
-__attribute__((naked)) void WDT_IRQHandler() {
297
-  __asm__ __volatile__ (
298
-    ".syntax unified" "\n\t"
299
-    A("tst lr, #4")
300
-    A("ite eq")
301
-    A("mrseq r0, msp")
302
-    A("mrsne r0, psp")
303
-    A("mov r1,lr")
304
-    A("mov r2,#6")
305
-    A("b HardFault_HandlerC")
306
-  );
307
-}
308
-
309
-__attribute__((naked)) void RSTC_Handler() {
310
-  __asm__ __volatile__ (
311
-    ".syntax unified" "\n\t"
312
-    A("tst lr, #4")
313
-    A("ite eq")
314
-    A("mrseq r0, msp")
315
-    A("mrsne r0, psp")
316
-    A("mov r1,lr")
317
-    A("mov r2,#7")
318
-    A("b HardFault_HandlerC")
319
-  );
320
-}
321
-}
322
-#endif // TARGET_LPC1768

+ 50
- 0
Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp View File

@@ -0,0 +1,50 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#ifdef TARGET_LPC1768
23
+
24
+#include "HAL.h"
25
+
26
+#if ENABLED(POSTMORTEM_DEBUGGING)
27
+
28
+#include "../shared/HAL_MinSerial.h"
29
+#include <debug_frmwrk.h>
30
+
31
+static void TX(char c) { _DBC(c); }
32
+void install_min_serial() { HAL_min_serial_out = &TX; }
33
+
34
+#if DISABLED(DYNAMIC_VECTORTABLE)
35
+extern "C" {
36
+  __attribute__((naked)) void JumpHandler_ASM() {
37
+    __asm__ __volatile__ (
38
+      "b CommonHandler_ASM\n"
39
+    );
40
+  }
41
+  void __attribute__((naked, alias("JumpHandler_ASM"))) HardFault_Handler();
42
+  void __attribute__((naked, alias("JumpHandler_ASM"))) BusFault_Handler();
43
+  void __attribute__((naked, alias("JumpHandler_ASM"))) UsageFault_Handler();
44
+  void __attribute__((naked, alias("JumpHandler_ASM"))) MemManage_Handler();
45
+  void __attribute__((naked, alias("JumpHandler_ASM"))) NMI_Handler();
46
+}
47
+#endif
48
+
49
+#endif // POSTMORTEM_DEBUGGING
50
+#endif // TARGET_LPC1768

+ 2
- 2
Marlin/src/HAL/LPC1768/inc/SanityCheck.h View File

@@ -270,7 +270,7 @@ static_assert(DISABLED(BAUD_RATE_GCODE), "BAUD_RATE_GCODE is not yet supported o
270 270
 #endif
271 271
 
272 272
 #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
273
-  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform."
273
+  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on LPC176x."
274 274
 #elif ENABLED(SERIAL_STATS_DROPPED_RX)
275
-  #error "SERIAL_STATS_DROPPED_RX is not supported on this platform."
275
+  #error "SERIAL_STATS_DROPPED_RX is not supported on LPX176x."
276 276
 #endif

+ 5
- 3
Marlin/src/HAL/LPC1768/main.cpp View File

@@ -46,6 +46,8 @@ extern "C" {
46 46
 
47 47
 void SysTick_Callback() { disk_timerproc(); }
48 48
 
49
+TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial());
50
+
49 51
 void HAL_init() {
50 52
 
51 53
   // Init LEDs
@@ -123,9 +125,7 @@ void HAL_init() {
123 125
   delay(1000);                              // Give OS time to notice
124 126
   USB_Connect(true);
125 127
 
126
-  #if HAS_SD_HOST_DRIVE
127
-    MSC_SD_Init(0);                         // Enable USB SD card access
128
-  #endif
128
+  TERN_(HAS_SD_HOST_DRIVE, MSC_SD_Init(0)); // Enable USB SD card access
129 129
 
130 130
   const millis_t usb_timeout = millis() + 2000;
131 131
   while (!USB_Configuration && PENDING(millis(), usb_timeout)) {
@@ -137,6 +137,8 @@ void HAL_init() {
137 137
   }
138 138
 
139 139
   HAL_timer_init();
140
+
141
+  TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler
140 142
 }
141 143
 
142 144
 // HAL idle task

+ 4
- 0
Marlin/src/HAL/STM32/HAL.cpp View File

@@ -57,6 +57,8 @@ uint16_t HAL_adc_result;
57 57
 // Public functions
58 58
 // ------------------------
59 59
 
60
+TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial());
61
+
60 62
 // HAL initialization task
61 63
 void HAL_init() {
62 64
   FastIO_init();
@@ -83,6 +85,8 @@ void HAL_init() {
83 85
     USB_Hook_init();
84 86
   #endif
85 87
 
88
+  TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler
89
+
86 90
   #if HAS_SD_HOST_DRIVE
87 91
     MSC_SD_init();                         // Enable USB SD card access
88 92
   #endif

+ 152
- 0
Marlin/src/HAL/STM32/HAL_MinSerial.cpp View File

@@ -0,0 +1,152 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ * Copyright (c) 2017 Victor Perez
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
+ *
22
+ */
23
+#if defined(ARDUINO_ARCH_STM32) && !defined(STM32GENERIC)
24
+
25
+#include "../../inc/MarlinConfigPre.h"
26
+
27
+#if ENABLED(POSTMORTEM_DEBUGGING)
28
+
29
+#include "../shared/HAL_MinSerial.h"
30
+#include "watchdog.h"
31
+
32
+/* Instruction Synchronization Barrier */
33
+#define isb() __asm__ __volatile__ ("isb" : : : "memory")
34
+
35
+/* Data Synchronization Barrier */
36
+#define dsb() __asm__ __volatile__ ("dsb" : : : "memory")
37
+
38
+// Dumb mapping over the registers of a USART device on STM32
39
+struct USARTMin {
40
+  volatile uint32_t SR;
41
+  volatile uint32_t DR;
42
+  volatile uint32_t BRR;
43
+  volatile uint32_t CR1;
44
+  volatile uint32_t CR2;
45
+};
46
+
47
+#if WITHIN(SERIAL_PORT, 1, 6)
48
+  // Depending on the CPU, the serial port is different for USART1
49
+  static const uintptr_t regsAddr[] = {
50
+    TERN(STM32F1xx, 0x40013800, 0x40011000), // USART1
51
+    0x40004400, // USART2
52
+    0x40004800, // USART3
53
+    0x40004C00, // UART4_BASE
54
+    0x40005000, // UART5_BASE
55
+    0x40011400  // USART6
56
+  };
57
+  static USARTMin * regs = (USARTMin*)regsAddr[SERIAL_PORT - 1];
58
+#endif
59
+
60
+static void TXBegin() {
61
+  #if !WITHIN(SERIAL_PORT, 1, 6)
62
+    #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error."
63
+    #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port."
64
+  #else
65
+    // This is common between STM32F1/STM32F2 and STM32F4
66
+    const int nvicUART[] = { /* NVIC_USART1 */ 37, /* NVIC_USART2 */ 38, /* NVIC_USART3 */ 39, /* NVIC_UART4 */ 52, /* NVIC_UART5 */ 53, /* NVIC_USART6 */ 71 };
67
+    int nvicIndex = nvicUART[SERIAL_PORT - 1];
68
+
69
+    struct NVICMin {
70
+      volatile uint32_t ISER[32];
71
+      volatile uint32_t ICER[32];
72
+    };
73
+
74
+    NVICMin * nvicBase = (NVICMin*)0xE000E100;
75
+    nvicBase->ICER[nvicIndex / 32] |= _BV32(nvicIndex % 32);
76
+
77
+    // We NEED memory barriers to ensure Interrupts are actually disabled!
78
+    // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
79
+    dsb();
80
+    isb();
81
+
82
+    // Example for USART1 disable:  (RCC->APB2ENR &= ~(RCC_APB2ENR_USART1EN))
83
+    // Too difficult to reimplement here, let's query the STM32duino macro here
84
+    #if SERIAL_PORT == 1
85
+      __HAL_RCC_USART1_CLK_DISABLE();
86
+      __HAL_RCC_USART1_CLK_ENABLE();
87
+    #elif SERIAL_PORT == 2
88
+      __HAL_RCC_USART2_CLK_DISABLE();
89
+      __HAL_RCC_USART2_CLK_ENABLE();
90
+    #elif SERIAL_PORT == 3
91
+      __HAL_RCC_USART3_CLK_DISABLE();
92
+      __HAL_RCC_USART3_CLK_ENABLE();
93
+    #elif SERIAL_PORT == 4
94
+      __HAL_RCC_UART4_CLK_DISABLE(); // BEWARE: UART4 and not USART4 here
95
+      __HAL_RCC_UART4_CLK_ENABLE();
96
+    #elif SERIAL_PORT == 5
97
+      __HAL_RCC_UART5_CLK_DISABLE(); // BEWARE: UART5 and not USART5 here
98
+      __HAL_RCC_UART5_CLK_ENABLE();
99
+    #elif SERIAL_PORT == 6
100
+      __HAL_RCC_USART6_CLK_DISABLE();
101
+      __HAL_RCC_USART6_CLK_ENABLE();
102
+    #endif
103
+
104
+    uint32_t brr = regs->BRR;
105
+    regs->CR1 = 0; // Reset the USART
106
+    regs->CR2 = 0; // 1 stop bit
107
+
108
+    // If we don't touch the BRR (baudrate register), we don't need to recompute.
109
+    regs->BRR = brr;
110
+
111
+    regs->CR1 = _BV(3) | _BV(13); // 8 bits, no parity, 1 stop bit (TE | UE)
112
+  #endif
113
+}
114
+
115
+// A SW memory barrier, to ensure GCC does not overoptimize loops
116
+#define sw_barrier() __asm__ volatile("": : :"memory");
117
+static void TX(char c) {
118
+  #if WITHIN(SERIAL_PORT, 1, 6)
119
+    constexpr uint32_t usart_sr_txe = _BV(7);
120
+    while (!(regs->SR & usart_sr_txe)) {
121
+      TERN_(USE_WATCHDOG, HAL_watchdog_refresh());
122
+      sw_barrier();
123
+    }
124
+    regs->DR = c;
125
+  #else
126
+    // Let's hope a mystical guru will fix this, one day by writting interrupt-free USB CDC ACM code (or, at least, by polling the registers since interrupt will be queued but will never trigger)
127
+    // For now, it's completely lost to oblivion.
128
+  #endif
129
+}
130
+
131
+void install_min_serial() {
132
+  HAL_min_serial_init = &TXBegin;
133
+  HAL_min_serial_out = &TX;
134
+}
135
+
136
+#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't jump to a symbol that's too far from the current function, so we work around this in exception_arm.cpp
137
+extern "C" {
138
+  __attribute__((naked)) void JumpHandler_ASM() {
139
+    __asm__ __volatile__ (
140
+      "b CommonHandler_ASM\n"
141
+    );
142
+  }
143
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) HardFault_Handler();
144
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) BusFault_Handler();
145
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) UsageFault_Handler();
146
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) MemManage_Handler();
147
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) NMI_Handler();
148
+}
149
+#endif
150
+
151
+#endif // POSTMORTEM_DEBUGGING
152
+#endif // ARDUINO_ARCH_STM32

+ 2
- 2
Marlin/src/HAL/STM32/inc/SanityCheck.h View File

@@ -47,9 +47,9 @@
47 47
 #endif
48 48
 
49 49
 #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
50
-  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform."
50
+  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on STM32."
51 51
 #elif ENABLED(SERIAL_STATS_DROPPED_RX)
52
-  #error "SERIAL_STATS_DROPPED_RX is not supported on this platform."
52
+  #error "SERIAL_STATS_DROPPED_RX is not supported on STM32."
53 53
 #endif
54 54
 
55 55
 #if ANY(TFT_COLOR_UI, TFT_LVGL_UI, TFT_CLASSIC_UI) && NOT_TARGET(STM32F4xx, STM32F1xx)

+ 3
- 0
Marlin/src/HAL/STM32F1/HAL.cpp View File

@@ -272,6 +272,8 @@ static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) {
272 272
   } }
273 273
 #endif
274 274
 
275
+TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial());
276
+
275 277
 void HAL_init() {
276 278
   NVIC_SetPriorityGrouping(0x3);
277 279
   #if PIN_EXISTS(LED)
@@ -287,6 +289,7 @@ void HAL_init() {
287 289
     delay(1000);                                         // Give OS time to notice
288 290
     OUT_WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING);
289 291
   #endif
292
+  TERN_(POSTMORTEM_DEBUGGING, install_min_serial());    // Install the minimal serial handler
290 293
 }
291 294
 
292 295
 // HAL idle task

+ 118
- 0
Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp View File

@@ -0,0 +1,118 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ * Copyright (c) 2017 Victor Perez
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
+ *
22
+ */
23
+#ifdef __STM32F1__
24
+
25
+#include "../../inc/MarlinConfigPre.h"
26
+
27
+#if ENABLED(POSTMORTEM_DEBUGGING)
28
+
29
+#include "../shared/HAL_MinSerial.h"
30
+#include "watchdog.h"
31
+
32
+#include <libmaple/usart.h>
33
+#include <libmaple/rcc.h>
34
+#include <libmaple/nvic.h>
35
+
36
+/* Instruction Synchronization Barrier */
37
+#define isb() __asm__ __volatile__ ("isb" : : : "memory")
38
+
39
+/* Data Synchronization Barrier */
40
+#define dsb() __asm__ __volatile__ ("dsb" : : : "memory")
41
+
42
+static void TXBegin() {
43
+  #if !WITHIN(SERIAL_PORT, 1, 6)
44
+    #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error."
45
+    #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port."
46
+  #else
47
+    // We use MYSERIAL0 here, so we need to figure out how to get the linked register
48
+    struct usart_dev* dev = MYSERIAL0.c_dev();
49
+
50
+    // Or use this if removing libmaple
51
+    // int irq = dev->irq_num;
52
+    // int nvicUART[] = { NVIC_USART1 /* = 37 */, NVIC_USART2 /* = 38 */, NVIC_USART3 /* = 39 */, NVIC_UART4 /* = 52 */, NVIC_UART5 /* = 53 */ };
53
+    // Disabling irq means setting the bit in the NVIC ICER register located at
54
+    // Disable UART interrupt in NVIC
55
+    nvic_irq_disable(dev->irq_num);
56
+
57
+    // Use this if removing libmaple
58
+    //NVIC_BASE->ICER[1] |= _BV(irq - 32);
59
+
60
+    // We NEED memory barriers to ensure Interrupts are actually disabled!
61
+    // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
62
+    dsb();
63
+    isb();
64
+
65
+    rcc_clk_disable(dev->clk_id);
66
+    rcc_clk_enable(dev->clk_id);
67
+
68
+    usart_reg_map *regs = dev->regs;
69
+    regs->CR1 = 0; // Reset the USART
70
+    regs->CR2 = 0; // 1 stop bit
71
+
72
+    // If we don't touch the BRR (baudrate register), we don't need to recompute. Else we would need to call
73
+    usart_set_baud_rate(dev, 0, BAUDRATE);
74
+
75
+    regs->CR1 = (USART_CR1_TE | USART_CR1_UE); // 8 bits, no parity, 1 stop bit
76
+  #endif
77
+}
78
+
79
+// A SW memory barrier, to ensure GCC does not overoptimize loops
80
+#define sw_barrier() __asm__ volatile("": : :"memory");
81
+static void TX(char c) {
82
+  #if WITHIN(SERIAL_PORT, 1, 6)
83
+    struct usart_dev* dev = MYSERIAL0.c_dev();
84
+    while (!(dev->regs->SR & USART_SR_TXE)) {
85
+      TERN_(USE_WATCHDOG, HAL_watchdog_refresh());
86
+      sw_barrier();
87
+    }
88
+    dev->regs->DR = c;
89
+  #endif
90
+}
91
+
92
+void install_min_serial() {
93
+  HAL_min_serial_init = &TXBegin;
94
+  HAL_min_serial_out = &TX;
95
+}
96
+
97
+#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them
98
+extern "C" {
99
+  __attribute__((naked)) void JumpHandler_ASM() {
100
+    __asm__ __volatile__ (
101
+      "b CommonHandler_ASM\n"
102
+    );
103
+  }
104
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_hardfault();
105
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_busfault();
106
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_usagefault();
107
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_memmanage();
108
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __exc_nmi();
109
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception7();
110
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception8();
111
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception9();
112
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception10();
113
+  void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) __stm32reservedexception13();
114
+}
115
+#endif
116
+
117
+#endif // POSTMORTEM_DEBUGGING
118
+#endif // __STM32F1__

+ 2
- 2
Marlin/src/HAL/STM32F1/inc/SanityCheck.h View File

@@ -34,9 +34,9 @@
34 34
 #endif
35 35
 
36 36
 #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
37
-  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on this platform."
37
+  #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on the STM32F1 platform."
38 38
 #elif ENABLED(SERIAL_STATS_DROPPED_RX)
39
-  #error "SERIAL_STATS_DROPPED_RX is not supported on this platform."
39
+  #error "SERIAL_STATS_DROPPED_RX is not supported on the STM32F1 platform."
40 40
 #endif
41 41
 
42 42
 #if ENABLED(NEOPIXEL_LED)

+ 5
- 1
Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h View File

@@ -34,5 +34,9 @@
34 34
 #endif
35 35
 
36 36
 #if HAS_TMC_SW_SERIAL
37
-  #error "TMC220x Software Serial is not supported on this platform."
37
+  #error "TMC220x Software Serial is not supported on Teensy 3.1/3.2."
38
+#endif
39
+
40
+#if ENABLED(POSTMORTEM_DEBUGGING)
41
+  #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.1/3.2."
38 42
 #endif

+ 5
- 1
Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h View File

@@ -34,5 +34,9 @@
34 34
 #endif
35 35
 
36 36
 #if HAS_TMC_SW_SERIAL
37
-  #error "TMC220x Software Serial is not supported on this platform."
37
+  #error "TMC220x Software Serial is not supported on Teensy 3.5/3.6."
38
+#endif
39
+
40
+#if ENABLED(POSTMORTEM_DEBUGGING)
41
+  #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.5/3.6."
38 42
 #endif

+ 5
- 1
Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h View File

@@ -34,5 +34,9 @@
34 34
 #endif
35 35
 
36 36
 #if HAS_TMC_SW_SERIAL
37
-  #error "TMC220x Software Serial is not supported on this platform."
37
+  #error "TMC220x Software Serial is not supported on Teensy 4.0/4.1."
38
+#endif
39
+
40
+#if ENABLED(POSTMORTEM_DEBUGGING)
41
+  #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 4.0/4.1."
38 42
 #endif

+ 33
- 0
Marlin/src/HAL/shared/HAL_MinSerial.cpp View File

@@ -0,0 +1,33 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#include "HAL_MinSerial.h"
23
+
24
+#if ENABLED(POSTMORTEM_DEBUGGING)
25
+
26
+void HAL_min_serial_init_default() {}
27
+void HAL_min_serial_out_default(char ch) { SERIAL_CHAR(ch); }
28
+void (*HAL_min_serial_init)() = &HAL_min_serial_init_default;
29
+void (*HAL_min_serial_out)(char) = &HAL_min_serial_out_default;
30
+
31
+bool MinSerial::force_using_default_output = false;
32
+
33
+#endif

+ 79
- 0
Marlin/src/HAL/shared/HAL_MinSerial.h View File

@@ -0,0 +1,79 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#pragma once
23
+
24
+#include "../../core/serial.h"
25
+#include <stdint.h>
26
+
27
+// Serial stuff here
28
+// Inside an exception handler, the CPU state is not safe, we can't expect the handler to resume
29
+// and the software to continue. UART communication can't rely on later callback/interrupt as it might never happen.
30
+// So, you need to provide some method to send one byte to the usual UART with the interrupts disabled
31
+// By default, the method uses SERIAL_CHAR but it's 100% guaranteed to break (couldn't be worse than nothing...)7
32
+extern void (*HAL_min_serial_init)();
33
+extern void (*HAL_min_serial_out)(char ch);
34
+
35
+struct MinSerial {
36
+  static bool force_using_default_output;
37
+  // Serial output
38
+  static void TX(char ch) {
39
+    if (force_using_default_output)
40
+      SERIAL_CHAR(ch);
41
+    else
42
+      HAL_min_serial_out(ch);
43
+  }
44
+  // Send String through UART
45
+  static void TX(const char* s) { while (*s) TX(*s++); }
46
+  // Send a digit through UART
47
+  static void TXDigit(uint32_t d) {
48
+    if (d < 10) TX((char)(d+'0'));
49
+    else if (d < 16) TX((char)(d+'A'-10));
50
+    else TX('?');
51
+  }
52
+
53
+  // Send Hex number through UART
54
+  static void TXHex(uint32_t v) {
55
+    TX("0x");
56
+    for (uint8_t i = 0; i < 8; i++, v <<= 4)
57
+      TXDigit((v >> 28) & 0xF);
58
+  }
59
+
60
+  // Send Decimal number through UART
61
+  static void TXDec(uint32_t v) {
62
+    if (!v) {
63
+      TX('0');
64
+      return;
65
+    }
66
+
67
+    char nbrs[14];
68
+    char *p = &nbrs[0];
69
+    while (v != 0) {
70
+      *p++ = '0' + (v % 10);
71
+      v /= 10;
72
+    }
73
+    do {
74
+      p--;
75
+      TX(*p);
76
+    } while (p != &nbrs[0]);
77
+  }
78
+  static void init() { if (!force_using_default_output) HAL_min_serial_init(); }
79
+};

+ 18
- 10
Marlin/src/HAL/shared/backtrace/backtrace.cpp View File

@@ -25,7 +25,7 @@
25 25
 #include "unwinder.h"
26 26
 #include "unwmemaccess.h"
27 27
 
28
-#include "../../../core/serial.h"
28
+#include "../HAL_MinSerial.h"
29 29
 #include <stdarg.h>
30 30
 
31 31
 // Dump a backtrace entry
@@ -34,10 +34,12 @@ static bool UnwReportOut(void* ctx, const UnwReport* bte) {
34 34
 
35 35
   (*p)++;
36 36
 
37
-  SERIAL_CHAR('#'); SERIAL_ECHO(*p); SERIAL_ECHOPGM(" : ");
38
-  SERIAL_ECHOPGM(bte->name ? bte->name : "unknown"); SERIAL_ECHOPGM("@0x"); SERIAL_PRINT(bte->function, PrintBase::Hex);
39
-  SERIAL_CHAR('+'); SERIAL_ECHO(bte->address - bte->function);
40
-  SERIAL_ECHOPGM(" PC:"); SERIAL_PRINT(bte->address, PrintBase::Hex); SERIAL_CHAR('\n');
37
+  const uint32_t a = bte->address, f = bte->function;
38
+  MinSerial::TX('#');    MinSerial::TXDec(*p);    MinSerial::TX(" : ");
39
+  MinSerial::TX(bte->name?:"unknown");            MinSerial::TX('@');   MinSerial::TXHex(f);
40
+  MinSerial::TX('+');    MinSerial::TXDec(a - f);
41
+  MinSerial::TX(" PC:"); MinSerial::TXHex(a);
42
+  MinSerial::TX('\n');
41 43
   return true;
42 44
 }
43 45
 
@@ -48,7 +50,7 @@ static bool UnwReportOut(void* ctx, const UnwReport* bte) {
48 50
     va_start(argptr, format);
49 51
     vsprintf(dest, format, argptr);
50 52
     va_end(argptr);
51
-    TX(&dest[0]);
53
+    MinSerial::TX(&dest[0]);
52 54
   }
53 55
 #endif
54 56
 
@@ -63,10 +65,10 @@ static const UnwindCallbacks UnwCallbacks = {
63 65
   #endif
64 66
 };
65 67
 
68
+// Perform a backtrace to the serial port
66 69
 void backtrace() {
67 70
 
68
-  UnwindFrame btf;
69
-  uint32_t sp = 0, lr = 0, pc = 0;
71
+  unsigned long sp = 0, lr = 0, pc = 0;
70 72
 
71 73
   // Capture the values of the registers to perform the traceback
72 74
   __asm__ __volatile__ (
@@ -79,6 +81,12 @@ void backtrace() {
79 81
     ::
80 82
   );
81 83
 
84
+  backtrace_ex(sp, lr, pc);
85
+}
86
+
87
+void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc) {
88
+  UnwindFrame btf;
89
+
82 90
   // Fill the traceback structure
83 91
   btf.sp = sp;
84 92
   btf.fp = btf.sp;
@@ -86,7 +94,7 @@ void backtrace() {
86 94
   btf.pc = pc | 1; // Force Thumb, as CORTEX only support it
87 95
 
88 96
   // Perform a backtrace
89
-  SERIAL_ERROR_MSG("Backtrace:");
97
+  MinSerial::TX("Backtrace:");
90 98
   int ctr = 0;
91 99
   UnwindStart(&btf, &UnwCallbacks, &ctr);
92 100
 }
@@ -95,4 +103,4 @@ void backtrace() {
95 103
 
96 104
 void backtrace() {}
97 105
 
98
-#endif
106
+#endif // __arm__ || __thumb__

+ 3
- 0
Marlin/src/HAL/shared/backtrace/backtrace.h View File

@@ -23,3 +23,6 @@
23 23
 
24 24
 // Perform a backtrace to the serial port
25 25
 void backtrace();
26
+
27
+// Perform a backtrace to the serial port
28
+void backtrace_ex(unsigned long sp, unsigned long lr, unsigned long pc);

+ 49
- 23
Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp View File

@@ -41,27 +41,16 @@
41 41
   #define START_FLASH_ADDR  0x00000000
42 42
   #define END_FLASH_ADDR    0x00080000
43 43
 
44
-#elif 0
45
-
46
-  // For STM32F103CBT6
47
-  //  SRAM  (0x20000000 - 0x20005000) (20kb)
48
-  //  FLASH (0x00000000 - 0x00020000) (128kb)
49
-  //
50
-  #define START_SRAM_ADDR   0x20000000
51
-  #define END_SRAM_ADDR     0x20005000
52
-  #define START_FLASH_ADDR  0x00000000
53
-  #define END_FLASH_ADDR    0x00020000
54
-
55 44
 #elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx)
56 45
 
57 46
   // For STM32F103ZET6/STM32F103VET6/STM32F0xx
58 47
   //  SRAM  (0x20000000 - 0x20010000) (64kb)
59
-  //  FLASH (0x00000000 - 0x00080000) (512kb)
48
+  //  FLASH (0x08000000 - 0x08080000) (512kb)
60 49
   //
61 50
   #define START_SRAM_ADDR   0x20000000
62 51
   #define END_SRAM_ADDR     0x20010000
63
-  #define START_FLASH_ADDR  0x00000000
64
-  #define END_FLASH_ADDR    0x00080000
52
+  #define START_FLASH_ADDR  0x08000000
53
+  #define END_FLASH_ADDR    0x08080000
65 54
 
66 55
 #elif defined(STM32F4) || defined(STM32F4xx)
67 56
 
@@ -142,20 +131,57 @@
142 131
   #define START_FLASH_ADDR  0x00000000
143 132
   #define END_FLASH_ADDR    0x00100000
144 133
 
134
+#else
135
+  // Generic ARM code, that's testing if an access to the given address would cause a fault or not
136
+  // It can't guarantee an address is in RAM or Flash only, but we usually don't care
137
+
138
+  #define NVIC_FAULT_STAT         0xE000ED28  // Configurable Fault Status Reg.
139
+  #define NVIC_CFG_CTRL           0xE000ED14  // Configuration Control Register
140
+  #define NVIC_FAULT_STAT_BFARV   0x00008000  // BFAR is valid
141
+  #define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100  // Ignore bus fault in NMI/fault
142
+  #define HW_REG(X)  (*((volatile unsigned long *)(X)))
143
+
144
+  static bool validate_addr(uint32_t read_address) {
145
+    bool     works = true;
146
+    uint32_t intDisabled = 0;
147
+    // Read current interrupt state
148
+    __asm__ __volatile__ ("MRS %[result], PRIMASK\n\t" : [result]"=r"(intDisabled) :: ); // 0 is int enabled, 1 for disabled
149
+
150
+    // Clear bus fault indicator first (write 1 to clear)
151
+    HW_REG(NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
152
+    // Ignore bus fault interrupt
153
+    HW_REG(NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
154
+    // Disable interrupts if not disabled previously
155
+    if (!intDisabled) __asm__ __volatile__ ("CPSID f");
156
+    // Probe address
157
+    *(volatile uint32_t*)read_address;
158
+    // Check if a fault happened
159
+    if ((HW_REG(NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
160
+      works = false;
161
+    // Enable interrupts again if previously disabled
162
+    if (!intDisabled) __asm__ __volatile__ ("CPSIE f");
163
+    // Enable fault interrupt flag
164
+    HW_REG(NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
165
+
166
+    return works;
167
+  }
168
+
145 169
 #endif
146 170
 
147
-static bool validate_addr(uint32_t addr) {
171
+#ifdef START_SRAM_ADDR
172
+  static bool validate_addr(uint32_t addr) {
148 173
 
149
-  // Address must be in SRAM range
150
-  if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR)
151
-    return true;
174
+    // Address must be in SRAM range
175
+    if (addr >= START_SRAM_ADDR && addr < END_SRAM_ADDR)
176
+      return true;
152 177
 
153
-  // Or in FLASH range
154
-  if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR)
155
-    return true;
178
+    // Or in FLASH range
179
+    if (addr >= START_FLASH_ADDR && addr < END_FLASH_ADDR)
180
+      return true;
156 181
 
157
-  return false;
158
-}
182
+    return false;
183
+  }
184
+#endif
159 185
 
160 186
 bool UnwReadW(const uint32_t a, uint32_t *v) {
161 187
   if (!validate_addr(a))

+ 379
- 0
Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp View File

@@ -0,0 +1,379 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ * Copyright (c) 2020 Cyril Russo
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
+ *
22
+ */
23
+
24
+/***************************************************************************
25
+ * ARM CPU Exception handler
26
+ ***************************************************************************/
27
+
28
+#if defined(__arm__) || defined(__thumb__)
29
+
30
+
31
+/*
32
+  On ARM CPUs exception handling is quite powerful.
33
+
34
+  By default, upon a crash, the CPU enters the handlers that have a higher priority than any other interrupts,
35
+  so, in effect, no (real) interrupt can "interrupt" the handler (it's acting like if interrupts were disabled).
36
+
37
+  If the handler is not called as re-entrant (that is, if the crash is not happening inside an interrupt or an handler),
38
+  then it'll patch the return address to a dumping function (resume_from_fault) and save the crash state.
39
+  The CPU will exit the handler and, as such, re-allow the other interrupts, and jump to the dumping function.
40
+  In this function, the usual serial port (USB / HW) will be used to dump the crash (no special configuration required).
41
+
42
+  The only case where it requires hardware UART is when it's crashing in an interrupt or a crash handler.
43
+  In that case, instead of returning to the resume_from_fault function (and thus, re-enabling interrupts),
44
+  it jumps to this function directly (so with interrupts disabled), after changing the behavior of the serial output
45
+  wrapper to use the HW uart (and in effect, calling MinSerial::init which triggers a warning if you are using
46
+  a USB serial port).
47
+
48
+  In the case you have a USB serial port, this part will be disabled, and only that part (so that's the reason for
49
+  the warning).
50
+  This means that you can't have a crash report if the crash happens in an interrupt or an handler if you are using
51
+  a USB serial port since it's physically impossible.
52
+  You will get a crash report in all other cases.
53
+*/
54
+
55
+#include "exception_hook.h"
56
+#include "../backtrace/backtrace.h"
57
+#include "../HAL_MinSerial.h"
58
+
59
+#define HW_REG(X)  (*((volatile unsigned long *)(X)))
60
+
61
+// Default function use the CPU VTOR register to get the vector table.
62
+// Accessing the CPU VTOR register is done in assembly since it's the only way that's common to all current tool
63
+unsigned long get_vtor() { return HW_REG(0xE000ED08); } // Even if it looks like an error, it is not an error
64
+void * hook_get_hardfault_vector_address(unsigned vtor)  { return (void*)(vtor + 0x03); }
65
+void * hook_get_memfault_vector_address(unsigned vtor)   { return (void*)(vtor + 0x04); }
66
+void * hook_get_busfault_vector_address(unsigned vtor)   { return (void*)(vtor + 0x05); }
67
+void * hook_get_usagefault_vector_address(unsigned vtor) { return (void*)(vtor + 0x06); }
68
+void * hook_get_reserved_vector_address(unsigned vtor)   { return (void*)(vtor + 0x07); }
69
+
70
+// Common exception frame for ARM, should work for all ARM CPU
71
+// Described here (modified for convenience): https://interrupt.memfault.com/blog/cortex-m-fault-debug
72
+struct __attribute__((packed)) ContextStateFrame {
73
+  uint32_t r0;
74
+  uint32_t r1;
75
+  uint32_t r2;
76
+  uint32_t r3;
77
+  uint32_t r12;
78
+  uint32_t lr;
79
+  uint32_t pc;
80
+  uint32_t xpsr;
81
+};
82
+
83
+struct __attribute__((packed)) ContextSavedFrame {
84
+  uint32_t R0;
85
+  uint32_t R1;
86
+  uint32_t R2;
87
+  uint32_t R3;
88
+  uint32_t R12;
89
+  uint32_t LR;
90
+  uint32_t PC;
91
+  uint32_t XPSR;
92
+
93
+  uint32_t CFSR;
94
+  uint32_t HFSR;
95
+  uint32_t DFSR;
96
+  uint32_t AFSR;
97
+  uint32_t MMAR;
98
+  uint32_t BFAR;
99
+
100
+  uint32_t ESP;
101
+  uint32_t ELR;
102
+};
103
+
104
+#if DISABLED(STM32F0xx)
105
+  extern "C"
106
+  __attribute__((naked)) void CommonHandler_ASM() {
107
+    __asm__ __volatile__ (
108
+      // Bit 2 of LR tells which stack pointer to use (either main or process, only main should be used anyway)
109
+      "tst lr, #4\n"
110
+      "ite eq\n"
111
+      "mrseq r0, msp\n"
112
+      "mrsne r0, psp\n"
113
+      // Save the LR in use when being interrupted
114
+      "mov r1, lr\n"
115
+      // Get the exception number from the ICSR register
116
+      "ldr r2, =0xE000ED00\n"
117
+      "ldr r2, [r2, #4]\n"
118
+      "b CommonHandler_C\n"
119
+    );
120
+  }
121
+#else // Cortex M0 does not support conditional mov and testing with a constant, so let's have a specific handler for it
122
+  extern "C"
123
+  __attribute__((naked)) void CommonHandler_ASM() {
124
+    __asm__ __volatile__ (
125
+      ".syntax unified\n"
126
+      // Save the LR in use when being interrupted
127
+      "mov  r1, lr\n"
128
+      // Get the exception number from the ICSR register
129
+      "ldr  r2, =0xE000ED00\n"
130
+      "ldr  r2, [r2, #4]\n"
131
+      "movs r0, #4\n"
132
+      "tst  r1, r0\n"
133
+      "beq _MSP\n"
134
+      "mrs  r0, psp\n"
135
+      "b CommonHandler_C\n"
136
+      "_MSP:\n"
137
+      "mrs  r0, msp\n"
138
+      "b CommonHandler_C\n"
139
+    );
140
+  }
141
+
142
+  #if DISABLED(DYNAMIC_VECTORTABLE) // Cortex M0 requires the handler's address to be within 32kB to the actual function to be able to branch to it
143
+    extern "C" {
144
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_hardfault();
145
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_busfault();
146
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_usagefault();
147
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_memmanage();
148
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __exc_nmi();
149
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception7();
150
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception8();
151
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception9();
152
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception10();
153
+      void __attribute__((naked, alias("CommonHandler_ASM"), nothrow)) __stm32reservedexception13();
154
+    }
155
+    //TODO When going off from libmaple, you'll need to replace those by the one from STM32/HAL_MinSerial.cpp
156
+  #endif
157
+#endif
158
+
159
+// Must be a macro to avoid creating a function frame
160
+#define HALT_IF_DEBUGGING()             \
161
+  do {                                  \
162
+    if (HW_REG(0xE000EDF0) & _BV(0)) {  \
163
+      __asm("bkpt 1");                  \
164
+    }                                   \
165
+} while (0)
166
+
167
+// Resume from a fault (if possible) so we can still use interrupt based code for serial output
168
+// In that case, we will not jump back to the faulty code, but instead to a dumping code and then a
169
+// basic loop with watchdog calling or manual resetting
170
+static ContextSavedFrame savedFrame;
171
+static uint8_t           lastCause;
172
+bool resume_from_fault() {
173
+  static const char* causestr[] = { "Thread", "Rsvd", "NMI", "Hard", "Mem", "Bus", "Usage", "7", "8", "9", "10", "SVC", "Dbg", "13", "PendSV", "SysTk", "IRQ" };
174
+  // Reinit the serial link (might only work if implemented in each of your boards)
175
+  MinSerial::init();
176
+
177
+  MinSerial::TX("\n\n## Software Fault detected ##\n");
178
+  MinSerial::TX("Cause: "); MinSerial::TX(causestr[min(lastCause, (uint8_t)16)]); MinSerial::TX('\n');
179
+
180
+  MinSerial::TX("R0   : "); MinSerial::TXHex(savedFrame.R0);   MinSerial::TX('\n');
181
+  MinSerial::TX("R1   : "); MinSerial::TXHex(savedFrame.R1);   MinSerial::TX('\n');
182
+  MinSerial::TX("R2   : "); MinSerial::TXHex(savedFrame.R2);   MinSerial::TX('\n');
183
+  MinSerial::TX("R3   : "); MinSerial::TXHex(savedFrame.R3);   MinSerial::TX('\n');
184
+  MinSerial::TX("R12  : "); MinSerial::TXHex(savedFrame.R12);  MinSerial::TX('\n');
185
+  MinSerial::TX("LR   : "); MinSerial::TXHex(savedFrame.LR);   MinSerial::TX('\n');
186
+  MinSerial::TX("PC   : "); MinSerial::TXHex(savedFrame.PC);   MinSerial::TX('\n');
187
+  MinSerial::TX("PSR  : "); MinSerial::TXHex(savedFrame.XPSR); MinSerial::TX('\n');
188
+
189
+  // Configurable Fault Status Register
190
+  // Consists of MMSR, BFSR and UFSR
191
+  MinSerial::TX("CFSR : "); MinSerial::TXHex(savedFrame.CFSR); MinSerial::TX('\n');
192
+
193
+  // Hard Fault Status Register
194
+  MinSerial::TX("HFSR : "); MinSerial::TXHex(savedFrame.HFSR); MinSerial::TX('\n');
195
+
196
+  // Debug Fault Status Register
197
+  MinSerial::TX("DFSR : "); MinSerial::TXHex(savedFrame.DFSR); MinSerial::TX('\n');
198
+
199
+  // Auxiliary Fault Status Register
200
+  MinSerial::TX("AFSR : "); MinSerial::TXHex(savedFrame.AFSR); MinSerial::TX('\n');
201
+
202
+  // Read the Fault Address Registers. These may not contain valid values.
203
+  // Check BFARVALID/MMARVALID to see if they are valid values
204
+  // MemManage Fault Address Register
205
+  MinSerial::TX("MMAR : "); MinSerial::TXHex(savedFrame.MMAR); MinSerial::TX('\n');
206
+
207
+  // Bus Fault Address Register
208
+  MinSerial::TX("BFAR : "); MinSerial::TXHex(savedFrame.BFAR); MinSerial::TX('\n');
209
+
210
+  MinSerial::TX("ExcLR: "); MinSerial::TXHex(savedFrame.ELR); MinSerial::TX('\n');
211
+  MinSerial::TX("ExcSP: "); MinSerial::TXHex(savedFrame.ESP); MinSerial::TX('\n');
212
+
213
+  // The stack pointer is pushed by 8 words upon entering an exception, so we need to revert this
214
+  backtrace_ex(savedFrame.ESP + 8*4, savedFrame.LR, savedFrame.PC);
215
+
216
+  // Call the last resort function here
217
+  hook_last_resort_func();
218
+
219
+  const uint32_t start = millis(), end = start + 100; // 100ms should be enough
220
+  // We need to wait for the serial buffers to be output but we don't know for how long
221
+  // So we'll just need to refresh the watchdog for a while and then stop for the system to reboot
222
+  uint32_t last = start;
223
+  while (PENDING(last, end)) {
224
+    watchdog_refresh();
225
+    while (millis() == last) { /* nada */ }
226
+    last = millis();
227
+    MinSerial::TX('.');
228
+  }
229
+
230
+  // Reset now by reinstantiating the bootloader's vector table
231
+  HW_REG(0xE000ED08) = 0;
232
+  // Restart watchdog
233
+  #if DISABLED(USE_WATCHDOG)
234
+    // No watchdog, let's perform ARMv7 reset instead by writing to AIRCR register with VECTKEY set to SYSRESETREQ
235
+    HW_REG(0xE000ED0C) = (HW_REG(0xE000ED0C) & 0x0000FFFF) | 0x05FA0004;
236
+  #endif
237
+
238
+  while(1) {} // Bad luck, nothing worked
239
+}
240
+
241
+// Make sure the compiler does not optimize the frame argument away
242
+extern "C"
243
+__attribute__((optimize("O0")))
244
+void CommonHandler_C(ContextStateFrame * frame, unsigned long lr, unsigned long cause) {
245
+
246
+  // If you are using it'll stop here
247
+  HALT_IF_DEBUGGING();
248
+
249
+  // Save the state to backtrace later on (don't call memcpy here since the stack can be corrupted)
250
+  savedFrame.R0  = frame->r0;
251
+  savedFrame.R1  = frame->r1;
252
+  savedFrame.R2  = frame->r2;
253
+  savedFrame.R3  = frame->r3;
254
+  savedFrame.R12 = frame->r12;
255
+  savedFrame.LR  = frame->lr;
256
+  savedFrame.PC  = frame->pc;
257
+  savedFrame.XPSR= frame->xpsr;
258
+  lastCause = cause & 0x1FF;
259
+
260
+  volatile uint32_t &CFSR = HW_REG(0xE000ED28);
261
+  savedFrame.CFSR = CFSR;
262
+  savedFrame.HFSR = HW_REG(0xE000ED2C);
263
+  savedFrame.DFSR = HW_REG(0xE000ED30);
264
+  savedFrame.AFSR = HW_REG(0xE000ED3C);
265
+  savedFrame.MMAR = HW_REG(0xE000ED34);
266
+  savedFrame.BFAR = HW_REG(0xE000ED38);
267
+  savedFrame.ESP  = (unsigned long)frame; // Even on return, this should not be overwritten by the CPU
268
+  savedFrame.ELR  = lr;
269
+
270
+  // First check if we can resume from this exception to our own handler safely
271
+  // If we can, then we don't need to disable interrupts and the usual serial code
272
+  // can be used
273
+
274
+  //const uint32_t non_usage_fault_mask = 0x0000FFFF;
275
+  //const bool non_usage_fault_occurred = (CFSR & non_usage_fault_mask) != 0;
276
+  // the bottom 8 bits of the xpsr hold the exception number of the
277
+  // executing exception or 0 if the processor is in Thread mode
278
+  const bool faulted_from_exception = ((frame->xpsr & 0xFF) != 0);
279
+  if (!faulted_from_exception) { // Not sure about the non_usage_fault, we want to try anyway, don't we ? && !non_usage_fault_occurred)
280
+    // Try to resume to our handler here
281
+    CFSR |= CFSR; // The ARM programmer manual says you must write to 1 all fault bits to clear them so this instruction is correct
282
+    // The frame will not be valid when returning anymore, let's clean it
283
+    savedFrame.CFSR = 0;
284
+
285
+    frame->pc = (uint32_t)resume_from_fault; // Patch where to return to
286
+    frame->lr = 0xdeadbeef;  // If our handler returns (it shouldn't), let's make it trigger an exception immediately
287
+    frame->xpsr = _BV(24);   // Need to clean the PSR register to thumb II only
288
+    MinSerial::force_using_default_output = true;
289
+    return; // The CPU will resume in our handler hopefully, and we'll try to use default serial output
290
+  }
291
+
292
+  // Sorry, we need to emergency code here since the fault is too dangerous to recover from
293
+  MinSerial::force_using_default_output = false;
294
+  resume_from_fault();
295
+}
296
+
297
+void hook_cpu_exceptions() {
298
+  #if ENABLED(DYNAMIC_VECTORTABLE)
299
+    // On ARM 32bits CPU, the vector table is like this:
300
+    // 0x0C => Hardfault
301
+    // 0x10 => MemFault
302
+    // 0x14 => BusFault
303
+    // 0x18 => UsageFault
304
+
305
+    // Unfortunately, it's usually run from flash, and we can't write to flash here directly to hook our instruction
306
+    // We could set an hardware breakpoint, and hook on the fly when it's being called, but this
307
+    // is hard to get right and would probably break debugger when attached
308
+
309
+    // So instead, we'll allocate a new vector table filled with the previous value except
310
+    // for the fault we are interested in.
311
+    // Now, comes the issue to figure out what is the current vector table size
312
+    // There is nothing telling us what is the vector table as it's per-cpu vendor specific.
313
+    // BUT: we are being called at the end of the setup, so we assume the setup is done
314
+    // Thus, we can read the current vector table until we find an address that's not in flash, and it would mark the
315
+    // end of the vector table (skipping the fist entry obviously)
316
+    // The position of the program in flash is expected to be at 0x08xxx xxxx on all known platform for ARM and the
317
+    // flash size is available via register 0x1FFFF7E0 on STM32 family, but it's not the case for all ARM boards
318
+    // (accessing this register might trigger a fault if it's not implemented).
319
+
320
+    // So we'll simply mask the top 8 bits of the first handler as an hint of being in the flash or not -that's poor and will
321
+    // probably break if the flash happens to be more than 128MB, but in this case, we are not magician, we need help from outside.
322
+
323
+    unsigned long * vecAddr = (unsigned long*)get_vtor();
324
+    SERIAL_ECHO("Vector table addr: ");
325
+    SERIAL_PRINTLN(get_vtor(), HEX);
326
+
327
+    #ifdef VECTOR_TABLE_SIZE
328
+      uint32_t vec_size = VECTOR_TABLE_SIZE;
329
+      alignas(128) static unsigned long vectable[VECTOR_TABLE_SIZE] ;
330
+    #else
331
+      #ifndef IS_IN_FLASH
332
+        #define IS_IN_FLASH(X) (((unsigned long)X & 0xFF000000) == 0x08000000)
333
+      #endif
334
+
335
+      // When searching for the end of the vector table, this acts as a limit not to overcome
336
+      #ifndef VECTOR_TABLE_SENTINEL
337
+        #define VECTOR_TABLE_SENTINEL 80
338
+      #endif
339
+
340
+      // Find the vector table size
341
+      uint32_t vec_size = 1;
342
+      while (IS_IN_FLASH(vecAddr[vec_size]) && vec_size < VECTOR_TABLE_SENTINEL)
343
+        vec_size++;
344
+
345
+      // We failed to find a valid vector table size, let's abort hooking up
346
+      if (vec_size == VECTOR_TABLE_SENTINEL) return;
347
+      // Poor method that's wasting RAM here, but allocating with malloc and alignment would be worst
348
+      // 128 bytes alignement is required for writing the VTOR register
349
+      alignas(128) static unsigned long vectable[VECTOR_TABLE_SENTINEL];
350
+
351
+      SERIAL_ECHO("Detected vector table size: ");
352
+      SERIAL_PRINTLN(vec_size, HEX);
353
+    #endif
354
+
355
+    uint32_t defaultFaultHandler = vecAddr[(unsigned)7];
356
+    // Copy the current vector table into the new table
357
+    for (uint32_t i = 0; i < vec_size; i++) {
358
+      vectable[i] = vecAddr[i];
359
+      // Replace all default handler by our own handler
360
+      if (vectable[i] == defaultFaultHandler)
361
+        vectable[i] = (unsigned long)&CommonHandler_ASM;
362
+    }
363
+
364
+    // Let's hook now with our functions
365
+    vectable[(unsigned long)hook_get_hardfault_vector_address(0)]  = (unsigned long)&CommonHandler_ASM;
366
+    vectable[(unsigned long)hook_get_memfault_vector_address(0)]   = (unsigned long)&CommonHandler_ASM;
367
+    vectable[(unsigned long)hook_get_busfault_vector_address(0)]   = (unsigned long)&CommonHandler_ASM;
368
+    vectable[(unsigned long)hook_get_usagefault_vector_address(0)] = (unsigned long)&CommonHandler_ASM;
369
+
370
+    // Finally swap with our own vector table
371
+    // This is supposed to be atomic, but let's do that with interrupt disabled
372
+
373
+    HW_REG(0xE000ED08) = (unsigned long)vectable | _BV32(29); // 29th bit is for telling the CPU the table is now in SRAM (should be present already)
374
+
375
+    SERIAL_ECHOLN("Installed fault handlers");
376
+  #endif
377
+}
378
+
379
+#endif // __arm__ || __thumb__

+ 28
- 0
Marlin/src/HAL/shared/cpu_exception/exception_hook.cpp View File

@@ -0,0 +1,28 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#include "exception_hook.h"
23
+
24
+void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned) { return 0; }
25
+void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned) { return 0; }
26
+void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned) { return 0; }
27
+void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned) { return 0; }
28
+void __attribute__((weak)) hook_last_resort_func() {}

+ 54
- 0
Marlin/src/HAL/shared/cpu_exception/exception_hook.h View File

@@ -0,0 +1,54 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+#pragma once
23
+
24
+/* Here is the expected behavior of a system producing a CPU exception with this hook installed:
25
+   1. Before the system is crashed
26
+     1.1 Upon validation (not done yet in this code, but we could be using DEBUG flags here to allow/disallow hooking)
27
+     1.2 Install the hook by overwriting the vector table exception handler with the hooked function
28
+   2. Upon system crash (for example, by a dereference of a NULL pointer or anything else)
29
+     2.1 The CPU triggers its exception and jump into the vector table for the exception type
30
+     2.2 Instead of finding the default handler, it finds the updated pointer to our hook
31
+     2.3 The CPU jumps into our hook function (likely a naked function to keep all information about crash point intact)
32
+     2.4 The hook (naked) function saves the important registers (stack pointer, program counter, current mode) and jumps to a common exception handler (in C)
33
+     2.5 The common exception handler dumps the registers on the serial link and perform a backtrace around the crashing point
34
+     2.6 Once the backtrace is performed the last resort function is called (platform specific).
35
+         On some platform with a LCD screen, this might display the crash information as a QR code or as text for the
36
+         user to capture by taking a picture
37
+     2.7 The CPU is reset and/or halted by triggering a debug breakpoint if a debugger is attached */
38
+
39
+// Hook into CPU exception interrupt table to call the backtracing code upon an exception
40
+// Most platform will simply do nothing here, but those who can will install/overwrite the default exception handler
41
+// with a more performant exception handler
42
+void hook_cpu_exceptions();
43
+
44
+// Some platform might deal without a hard fault handler, in that case, return 0 in your platform here or skip implementing it
45
+void * __attribute__((weak)) hook_get_hardfault_vector_address(unsigned base_address);
46
+// Some platform might deal without a memory management fault handler, in that case, return 0 in your platform here or skip implementing it
47
+void * __attribute__((weak)) hook_get_memfault_vector_address(unsigned base_address);
48
+// Some platform might deal without a bus fault handler, in that case, return 0 in your platform here or skip implementing it
49
+void * __attribute__((weak)) hook_get_busfault_vector_address(unsigned base_address);
50
+// Some platform might deal without a usage fault handler, in that case, return 0 in your platform here or skip implementing it
51
+void * __attribute__((weak)) hook_get_usagefault_vector_address(unsigned base_address);
52
+
53
+// Last resort function that can be called after the exception handler was called.
54
+void __attribute__((weak)) hook_last_resort_func();

+ 3
- 0
Marlin/src/MarlinCore.cpp View File

@@ -36,6 +36,7 @@
36 36
 
37 37
 #include "HAL/shared/Delay.h"
38 38
 #include "HAL/shared/esp_wifi.h"
39
+#include "HAL/shared/cpu_exception/exception_hook.h"
39 40
 
40 41
 #ifdef ARDUINO
41 42
   #include <pins_arduino.h>
@@ -935,6 +936,8 @@ void setup() {
935 936
     while (/*!WIFISERIAL && */PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
936 937
   #endif
937 938
 
939
+  TERN_(DYNAMIC_VECTORTABLE, hook_cpu_exceptions()); // If supported, install Marlin exception handlers at runtime
940
+
938 941
   SETUP_RUN(HAL_init());
939 942
 
940 943
   // Init and disable SPI thermocouples; this is still needed

+ 19
- 2
Marlin/src/gcode/gcode_d.cpp View File

@@ -152,8 +152,7 @@
152 152
         NOMORE(len, FLASH_SIZE - addr);
153 153
         if (parser.seenval('X')) {
154 154
           // TODO: Write the hex bytes after the X
155
-          //while (len--) {
156
-          //}
155
+          //while (len--) {}
157 156
         }
158 157
         else {
159 158
           // while (len--) {
@@ -180,7 +179,25 @@
180 179
         for (int i = 10000; i--;) DELAY_US(1000UL);
181 180
         ENABLE_ISRS();
182 181
         SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset.");
182
+      } break;
183
+
184
+      #if ENABLED(POSTMORTEM_DEBUGGING)
185
+      case 451: { // Trigger all kind of faults to test exception catcher
186
+        SERIAL_ECHOLNPGM("Disabling heaters");
187
+        thermalManager.disable_all_heaters();
188
+        delay(1000); // Allow time to print
189
+        volatile uint8_t type[5] = { parser.byteval('T', 1) };
190
+
191
+        // The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code
192
+        switch (type[0]) {
193
+          case 1: default: *(int*)0 = 451; break; // Write at bad address
194
+          case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM)
195
+          case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this)
196
+          case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction
197
+        }
198
+        break;
183 199
       }
200
+      #endif
184 201
     }
185 202
   }
186 203
 

+ 104
- 0
buildroot/share/PlatformIO/scripts/exc.S View File

@@ -0,0 +1,104 @@
1
+/* *****************************************************************************
2
+ * The MIT License
3
+ *
4
+ * Copyright (c) 2010 Perry Hung.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ * ****************************************************************************/
24
+
25
+# On an exception, push a fake stack thread mode stack frame and redirect
26
+# thread execution to a thread mode error handler
27
+
28
+# From RM008:
29
+# The SP is decremented by eight words by the completion of the stack push.
30
+# Figure 5-1 shows the contents of the stack after an exception pre-empts the
31
+# current program flow.
32
+#
33
+# Old SP--> <previous>
34
+#           xPSR
35
+#           PC
36
+#           LR
37
+#           r12
38
+#           r3
39
+#           r2
40
+#           r1
41
+#    SP-->  r0
42
+
43
+.text
44
+.globl __exc_nmi
45
+.weak  __exc_nmi
46
+.globl __exc_hardfault
47
+.weak  __exc_hardfault
48
+.globl __exc_memmanage
49
+.weak  __exc_memmanage
50
+.globl __exc_busfault
51
+.weak  __exc_busfault
52
+.globl __exc_usagefault
53
+.weak  __exc_usagefault
54
+
55
+.code 16
56
+.thumb_func
57
+__exc_nmi:
58
+    mov r0, #1
59
+    b __default_exc
60
+
61
+.thumb_func
62
+__exc_hardfault:
63
+    mov r0, #2
64
+    b __default_exc
65
+
66
+.thumb_func
67
+__exc_memmanage:
68
+    mov r0, #3
69
+    b __default_exc
70
+
71
+.thumb_func
72
+__exc_busfault:
73
+    mov r0, #4
74
+    b __default_exc
75
+
76
+.thumb_func
77
+__exc_usagefault:
78
+    mov r0, #5
79
+    b __default_exc
80
+
81
+.thumb_func
82
+__default_exc:
83
+    ldr r2, NVIC_CCR            @ Enable returning to thread mode even if there are
84
+    mov r1 ,#1                  @ pending exceptions. See flag NONEBASETHRDENA.
85
+    str r1, [r2]
86
+    cpsid i                     @ Disable global interrupts
87
+    ldr r2, SYSTICK_CSR         @ Disable systick handler
88
+    mov r1, #0
89
+    str r1, [r2]
90
+    ldr r1, CPSR_MASK           @ Set default CPSR
91
+    push {r1}
92
+    ldr r1, TARGET_PC           @ Set target pc
93
+    push {r1}
94
+    sub sp, sp, #24             @ Don't care
95
+    ldr r1, EXC_RETURN          @ Return to thread mode
96
+    mov lr, r1
97
+    bx lr                       @ Exception exit
98
+
99
+.align 4
100
+CPSR_MASK:     .word 0x61000000
101
+EXC_RETURN:    .word 0xFFFFFFF9
102
+TARGET_PC:     .word __error
103
+NVIC_CCR:      .word 0xE000ED14    @ NVIC configuration control register
104
+SYSTICK_CSR:   .word 0xE000E010    @ Systick control register

+ 29
- 0
buildroot/share/PlatformIO/scripts/fix_framework_weakness.py View File

@@ -0,0 +1,29 @@
1
+from os.path import join, isfile
2
+import shutil
3
+from pprint import pprint
4
+
5
+Import("env")
6
+
7
+if env.MarlinFeatureIsEnabled("POSTMORTEM_DEBUGGING"):
8
+    FRAMEWORK_DIR = env.PioPlatform().get_package_dir("framework-arduinoststm32-maple")
9
+    patchflag_path = join(FRAMEWORK_DIR, ".exc-patching-done")
10
+
11
+    # patch file only if we didn't do it before
12
+    if not isfile(patchflag_path):
13
+        print("Patching libmaple exception handlers")
14
+        original_file = join(FRAMEWORK_DIR, "STM32F1", "cores", "maple", "libmaple", "exc.S")
15
+        backup_file = join(FRAMEWORK_DIR, "STM32F1", "cores", "maple", "libmaple", "exc.S.bak")
16
+        src_file = join("buildroot", "share", "PlatformIO", "scripts", "exc.S")
17
+
18
+        assert isfile(original_file) and isfile(src_file)
19
+        shutil.copyfile(original_file, backup_file)
20
+        shutil.copyfile(src_file, original_file);
21
+
22
+        def _touch(path):
23
+            with open(path, "w") as fp:
24
+                fp.write("")
25
+
26
+        env.Execute(lambda *args, **kwargs: _touch(patchflag_path))
27
+        print("Done patching exception handler")
28
+
29
+    print("Libmaple modified and ready for post mortem debugging")

+ 16
- 26
platformio.ini View File

@@ -64,6 +64,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
64 64
   -<src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp>
65 65
   -<src/sd/cardreader.cpp> -<src/sd/Sd2Card.cpp> -<src/sd/SdBaseFile.cpp> -<src/sd/SdFatUtil.cpp> -<src/sd/SdFile.cpp> -<src/sd/SdVolume.cpp> -<src/gcode/sd>
66 66
   -<src/HAL/shared/backtrace>
67
+  -<src/HAL/shared/cpu_exception>
67 68
   -<src/HAL/shared/eeprom_if_i2c.cpp>
68 69
   -<src/HAL/shared/eeprom_if_spi.cpp>
69 70
   -<src/feature/babystep.cpp>
@@ -223,6 +224,8 @@ YHCB2004                = red-scorp/LiquidCrystal_AIP31068@^1.0.4, red-scorp/Sof
223 224
 HAS_TFT_LVGL_UI         = lvgl=https://github.com/makerbase-mks/LVGL-6.1.1-MKS/archive/master.zip
224 225
                           src_filter=+<src/lcd/extui/lib/mks_ui>
225 226
                           extra_scripts=download_mks_assets.py
227
+POSTMORTEM_DEBUGGING    = src_filter=+<src/HAL/shared/cpu_exception> +<src/HAL/shared/backtrace>
228
+                          build_flags=-funwind-tables
226 229
 MKS_WIFI_MODULE         = QRCode=https://github.com/makerbase-mks/QRCode/archive/master.zip
227 230
 HAS_TRINAMIC_CONFIG     = TMCStepper@~0.7.1
228 231
                           src_filter=+<src/feature/tmc_util.cpp> +<src/module/stepper/trinamic.cpp> +<src/gcode/feature/trinamic/M122.cpp> +<src/gcode/feature/trinamic/M906.cpp> +<src/gcode/feature/trinamic/M911-M914.cpp>
@@ -637,14 +640,6 @@ platform = atmelsam
637 640
 extends  = env:DUE
638 641
 board    = dueUSB
639 642
 
640
-[env:DUE_debug]
641
-# Used when WATCHDOG_RESET_MANUAL is enabled
642
-platform    = atmelsam
643
-extends     = env:DUE
644
-build_flags = ${common.build_flags}
645
-  -funwind-tables
646
-  -mpoke-function-name
647
-
648 643
 #
649 644
 # Archim SAM
650 645
 #
@@ -662,12 +657,6 @@ extra_scripts            = ${common.extra_scripts}
662 657
 platform = ${common_DUE_archim.platform}
663 658
 extends  = common_DUE_archim
664 659
 
665
-# Used when WATCHDOG_RESET_MANUAL is enabled
666
-[env:DUE_archim_debug]
667
-platform    = ${common_DUE_archim.platform}
668
-extends     = common_DUE_archim
669
-build_flags = ${common_DUE_archim.build_flags} -funwind-tables -mpoke-function-name
670
-
671 660
 #################################
672 661
 #                               #
673 662
 #      SAMD51 Architecture      #
@@ -763,6 +752,8 @@ lib_ignore        = SPI, FreeRTOS701, FreeRTOS821
763 752
 lib_deps          = ${common.lib_deps}
764 753
   SoftwareSerialM
765 754
 platform_packages = tool-stm32duino
755
+extra_scripts     = ${common.extra_scripts}
756
+  buildroot/share/PlatformIO/scripts/fix_framework_weakness.py
766 757
 
767 758
 #
768 759
 # STM32F103RC
@@ -788,7 +779,7 @@ build_flags       = ${common_stm32f1.build_flags}
788 779
                     -DUSE_USB_COMPOSITE
789 780
                     -DVECT_TAB_OFFSET=0x2000
790 781
                     -DGENERIC_BOOTLOADER
791
-extra_scripts     = ${common.extra_scripts}
782
+extra_scripts     = ${common_stm32f1.extra_scripts}
792 783
   pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py
793 784
   buildroot/share/PlatformIO/scripts/STM32F103RC_MEEB_3DP.py
794 785
 lib_deps          = ${common.lib_deps}
@@ -934,7 +925,7 @@ upload_protocol   = serial
934 925
 platform      = ${common_stm32f1.platform}
935 926
 extends       = common_stm32f1
936 927
 board         = genericSTM32F103VE
937
-extra_scripts = ${common.extra_scripts}
928
+extra_scripts = ${common_stm32f1.extra_scripts}
938 929
   buildroot/share/PlatformIO/scripts/STM32F103VE_longer.py
939 930
 build_flags   = ${common_stm32f1.build_flags}
940 931
   -DMCU_STM32F103VE -DSTM32F1xx -USERIAL_USB -DU20 -DTS_V12
@@ -948,7 +939,7 @@ build_unflags = ${common_stm32f1.build_unflags}
948 939
 platform      = ${common_stm32f1.platform}
949 940
 extends       = common_stm32f1
950 941
 board         = genericSTM32F103VE
951
-extra_scripts = ${common.extra_scripts}
942
+extra_scripts = ${common_stm32f1.extra_scripts}
952 943
   buildroot/share/PlatformIO/scripts/mks_robin_mini.py
953 944
 build_flags   = ${common_stm32f1.build_flags}
954 945
   -DMCU_STM32F103VE
@@ -974,7 +965,7 @@ upload_protocol = jlink
974 965
 platform      = ${common_stm32f1.platform}
975 966
 extends       = common_stm32f1
976 967
 board         = genericSTM32F103ZE
977
-extra_scripts = ${common.extra_scripts}
968
+extra_scripts = ${common_stm32f1.extra_scripts}
978 969
   buildroot/share/PlatformIO/scripts/mks_robin.py
979 970
 build_flags   = ${common_stm32f1.build_flags}
980 971
   -DSS_TIMER=4 -DSTM32_XL_DENSITY
@@ -1008,7 +999,7 @@ lib_deps             =
1008 999
 [env:mks_robin_pro]
1009 1000
 platform      = ${common_stm32f1.platform}
1010 1001
 extends       = env:mks_robin
1011
-extra_scripts = ${common.extra_scripts}
1002
+extra_scripts = ${common_stm32f1.extra_scripts}
1012 1003
   buildroot/share/PlatformIO/scripts/mks_robin_pro.py
1013 1004
 
1014 1005
 #
@@ -1017,7 +1008,7 @@ extra_scripts = ${common.extra_scripts}
1017 1008
 [env:trigorilla_pro]
1018 1009
 platform      = ${common_stm32f1.platform}
1019 1010
 extends       = env:mks_robin
1020
-extra_scripts = ${common.extra_scripts}
1011
+extra_scripts = ${common_stm32f1.extra_scripts}
1021 1012
 
1022 1013
 #
1023 1014
 # MKS Robin E3D (STM32F103RCT6) and
@@ -1041,7 +1032,7 @@ build_flags   = ${common_stm32f1.build_flags}
1041 1032
 platform        = ${common_stm32f1.platform}
1042 1033
 extends         = common_stm32f1
1043 1034
 board           = genericSTM32F103VE
1044
-extra_scripts   = ${common.extra_scripts}
1035
+extra_scripts   = ${common_stm32f1.extra_scripts}
1045 1036
   buildroot/share/PlatformIO/scripts/mks_robin_e3p.py
1046 1037
 build_flags     = ${common_stm32f1.build_flags}
1047 1038
   -DMCU_STM32F103VE -DSS_TIMER=4
@@ -1055,7 +1046,7 @@ upload_protocol = jlink
1055 1046
 platform      = ${common_stm32f1.platform}
1056 1047
 extends       = common_stm32f1
1057 1048
 board         = genericSTM32F103RC
1058
-extra_scripts = ${common.extra_scripts}
1049
+extra_scripts = ${common_stm32f1.extra_scripts}
1059 1050
   buildroot/share/PlatformIO/scripts/mks_robin_lite.py
1060 1051
 
1061 1052
 #
@@ -1065,7 +1056,7 @@ extra_scripts = ${common.extra_scripts}
1065 1056
 platform      = ${common_stm32f1.platform}
1066 1057
 extends       = common_stm32f1
1067 1058
 board         = genericSTM32F103RC
1068
-extra_scripts = ${common.extra_scripts}
1059
+extra_scripts = ${common_stm32f1.extra_scripts}
1069 1060
   buildroot/share/PlatformIO/scripts/mks_robin_lite3.py
1070 1061
 
1071 1062
 #
@@ -1075,7 +1066,7 @@ extra_scripts = ${common.extra_scripts}
1075 1066
 platform      = ${common_stm32f1.platform}
1076 1067
 extends       = common_stm32f1
1077 1068
 board         = genericSTM32F103ZE
1078
-extra_scripts = ${common.extra_scripts}
1069
+extra_scripts = ${common_stm32f1.extra_scripts}
1079 1070
   buildroot/share/PlatformIO/scripts/jgaurora_a5s_a1_with_bootloader.py
1080 1071
 build_flags   = ${common_stm32f1.build_flags}
1081 1072
   -DSTM32F1xx -DSTM32_XL_DENSITY
@@ -1230,7 +1221,7 @@ extra_scripts     = ${common.extra_scripts}
1230 1221
 platform          = ${common_stm32f1.platform}
1231 1222
 extends           = common_stm32f1
1232 1223
 board             = genericSTM32F103RC
1233
-extra_scripts     = ${common.extra_scripts}
1224
+extra_scripts     = ${common_stm32f1.extra_scripts}
1234 1225
  buildroot/share/PlatformIO/scripts/fly_mini.py
1235 1226
 build_flags       = ${common_stm32f1.build_flags}
1236 1227
  -DDEBUG_LEVEL=0 -DSS_TIMER=4
@@ -1557,7 +1548,6 @@ build_flags          = ${common_stm32.build_flags} -DENABLE_HWSERIAL3 -DTIMER_SE
1557 1548
 build_unflags        = ${common_stm32.build_unflags} -DUSBCON -DUSBD_USE_CDC
1558 1549
 extra_scripts        = ${common.extra_scripts} pre:buildroot/share/PlatformIO/scripts/generic_create_variant.py buildroot/share/PlatformIO/scripts/stm32_bootloader.py
1559 1550
 
1560
-
1561 1551
 #################################
1562 1552
 #                               #
1563 1553
 #      Other Architectures      #

Loading…
Cancel
Save