My Marlin configs for Fabrikator Mini and CTC i3 Pro B
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

exception_arm.cpp 17KB

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