You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

armv8m.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*
  2. * Copyright (c) 2025, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2025-08-16 Rbb666 first version
  9. */
  10. #include "coredump.h"
  11. #include "registers.h"
  12. #define FPU_CPACR 0xE000ED88
  13. int is_vfp_addressable(void)
  14. {
  15. uint32_t reg_cpacr = *((volatile uint32_t *)FPU_CPACR);
  16. if (reg_cpacr & 0x00F00000)
  17. return 1;
  18. else
  19. return 0;
  20. }
  21. #if defined(__CC_ARM)
  22. /* clang-format off */
  23. __asm void mcd_mini_dump()
  24. {
  25. extern get_cur_core_regset_address;
  26. extern get_cur_fp_regset_address;
  27. extern mcd_mini_dump_ops;
  28. extern mcd_gen_coredump;
  29. extern is_vfp_addressable;
  30. PRESERVE8
  31. push {r7, lr}
  32. sub sp, sp, #24
  33. add r7, sp, #0
  34. get_regset
  35. bl get_cur_core_regset_address
  36. str r0, [r0, #0]
  37. add r0, r0, #4
  38. stmia r0!, {r1 - r12}
  39. mov r1, sp
  40. add r1, #32
  41. str r1, [r0, #0]
  42. ldr r1, [sp, #28]
  43. str r1, [r0, #4]
  44. mov r1, pc
  45. str r1, [r0, #8]
  46. mrs r1, xpsr
  47. str r1, [r0, #12]
  48. bl is_vfp_addressable
  49. cmp r0, #0
  50. beq get_reg_done
  51. bl get_cur_fp_regset_address
  52. vstmia r0!, {d0 - d15}
  53. vmrs r1, fpscr
  54. str r1, [r0, #0]
  55. get_reg_done
  56. mov r0, r7
  57. bl mcd_mini_dump_ops
  58. mov r0, r7
  59. bl mcd_gen_coredump
  60. nop
  61. adds r7, r7, #24
  62. mov sp, r7
  63. pop {r7, pc}
  64. nop
  65. nop
  66. }
  67. __asm void mcd_multi_dump(void)
  68. {
  69. extern get_cur_core_regset_address;
  70. extern get_cur_fp_regset_address;
  71. extern mcd_rtos_thread_ops;
  72. extern mcd_gen_coredump;
  73. extern is_vfp_addressable;
  74. PRESERVE8
  75. push {r7, lr}
  76. sub sp, sp, #24
  77. add r7, sp, #0
  78. get_regset1
  79. bl get_cur_core_regset_address
  80. str r0, [r0, #0]
  81. add r0, r0, #4
  82. stmia r0!, {r1 - r12}
  83. mov r1, sp
  84. add r1, #32
  85. str r1, [r0, #0]
  86. ldr r1, [sp, #28]
  87. str r1, [r0, #4]
  88. mov r1, pc
  89. str r1, [r0, #8]
  90. mrs r1, xpsr
  91. str r1, [r0, #12]
  92. bl is_vfp_addressable
  93. cmp r0, #0
  94. beq get_reg_done
  95. bl get_cur_fp_regset_address
  96. vstmia r0!, {d0 - d15}
  97. vmrs r1, fpscr
  98. str r1, [r0, #0]
  99. get_reg_done1
  100. mov r0, r7
  101. bl mcd_rtos_thread_ops
  102. mov r0, r7
  103. bl mcd_gen_coredump
  104. nop
  105. adds r7, r7, #24
  106. mov sp, r7
  107. pop {r7, pc}
  108. nop
  109. nop
  110. }
  111. #elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) || defined(__GNUC__)
  112. #define mcd_get_regset(regset) \
  113. __asm volatile(" mov r0, %0 \n" \
  114. " str r0, [r0 , #0] \n" \
  115. " add r0, r0, #4 \n" \
  116. " stmia r0!, {r1 - r12} \n" \
  117. " mov r1, sp \n" \
  118. " str r1, [r0, #0] \n" \
  119. " mov r1, lr \n" \
  120. " str r1, [r0, #4] \n" \
  121. " mov r1, pc \n" \
  122. " str r1, [r0, #8] \n" \
  123. " mrs r1, xpsr \n" \
  124. " str r1, [r0, #12] \n" ::"r"(regset) \
  125. : "memory", "cc");
  126. #define mcd_get_fpregset(regset) \
  127. __asm volatile(" mov r0, %0 \n" \
  128. " vstmia r0!, {d0 - d15} \n" \
  129. " vmrs r1, fpscr \n" \
  130. " str r1, [r0, #0] \n" ::"r"(regset) \
  131. : "memory", "cc");
  132. void mcd_mini_dump(void)
  133. {
  134. struct thread_info_ops ops;
  135. mcd_get_regset((uint32_t *)get_cur_core_regset_address());
  136. #if MCD_FPU_SUPPORT
  137. if (is_vfp_addressable())
  138. mcd_get_fpregset((uint32_t *)get_cur_fp_regset_address());
  139. #endif
  140. mcd_mini_dump_ops(&ops);
  141. mcd_gen_coredump(&ops);
  142. }
  143. void mcd_multi_dump(void)
  144. {
  145. struct thread_info_ops ops;
  146. mcd_get_regset((uint32_t *)get_cur_core_regset_address());
  147. #if MCD_FPU_SUPPORT
  148. if (is_vfp_addressable())
  149. mcd_get_fpregset((uint32_t *)get_cur_fp_regset_address());
  150. #endif
  151. mcd_rtos_thread_ops(&ops);
  152. mcd_gen_coredump(&ops);
  153. }
  154. #endif
  155. /**
  156. * @brief Collect ARM Cortex-M33 registers from RT-Thread stack frame
  157. *
  158. * This function extracts register values from the stack frame created by
  159. * RT-Thread's HardFault_Handler for ARMv8-M architecture.
  160. *
  161. * ARMv8-M Stack Frame Layout (based on HardFault_Handler in context_gcc.S):
  162. * +-------------------+ <- stack_top (input parameter)
  163. * | EXC_RETURN | (4 bytes, contains exception return information)
  164. * +-------------------+
  165. * | tz | (4 bytes, TrustZone context)
  166. * | lr | (4 bytes, saved link register)
  167. * | psplim | (4 bytes, stack pointer limit)
  168. * | control | (4 bytes, control register)
  169. * +-------------------+
  170. * | r4 | (4 bytes, software saved)
  171. * | r5 | (4 bytes, software saved)
  172. * | r6 | (4 bytes, software saved)
  173. * | r7 | (4 bytes, software saved)
  174. * | r8 | (4 bytes, software saved)
  175. * | r9 | (4 bytes, software saved)
  176. * | r10 | (4 bytes, software saved)
  177. * | r11 | (4 bytes, software saved)
  178. * +-------------------+
  179. * | FPU d8-d15 | (64 bytes, if FPU context active)
  180. * +-------------------+
  181. * | r0 | (4 bytes, hardware saved)
  182. * | r1 | (4 bytes, hardware saved)
  183. * | r2 | (4 bytes, hardware saved)
  184. * | r3 | (4 bytes, hardware saved)
  185. * | r12 | (4 bytes, hardware saved)
  186. * | lr | (4 bytes, hardware saved)
  187. * | pc | (4 bytes, hardware saved)
  188. * | xpsr | (4 bytes, hardware saved)
  189. * +-------------------+
  190. * | FPU d0-d7 | (64 bytes, if FPU context active)
  191. * | FPSCR | (4 bytes, if FPU context active)
  192. * | NO_NAME | (4 bytes, if FPU context active)
  193. * +-------------------+ <- current SP after context save
  194. *
  195. * @param stack_top Pointer to the beginning of the stack frame (EXC_RETURN position)
  196. * @param core_regset Pointer to structure for storing ARM core registers
  197. * @param fp_regset Pointer to structure for storing FPU registers
  198. */
  199. void collect_registers_armv8m(uint32_t *stack_top,
  200. core_regset_type *core_regset,
  201. fp_regset_type *fp_regset)
  202. {
  203. /*
  204. * ARMv8-M has two different stack layouts:
  205. * 1. Exception context (HardFault_Handler): [EXC_RETURN] -> [tz,lr,psplim,control] -> [r4-r11] -> [FPU d8-d15] -> [exception frame] -> [FPU d0-d7,FPSCR]
  206. * 2. Normal thread context (PendSV_Handler): [tz,lr,psplim,control] -> [r4-r11] -> [hardware exception frame]
  207. */
  208. uint32_t *current_ptr = stack_top;
  209. /* Clear both register sets first to ensure clean state */
  210. mcd_memset(core_regset, 0, sizeof(core_regset_type));
  211. mcd_memset(fp_regset, 0, sizeof(fp_regset_type));
  212. /* Read first word to determine context type */
  213. uint32_t first_word = *current_ptr;
  214. uint32_t fpu_flag = 0; // Default to no FPU context
  215. /* Check if this is a valid EXC_RETURN value (starts with 0xFF) */
  216. if ((first_word & 0xFF000000) == 0xFF000000)
  217. {
  218. /* Valid EXC_RETURN - this is an exception context */
  219. uint32_t exc_return = *current_ptr++;
  220. fpu_flag = !(exc_return & 0x10);
  221. /* Skip tz, lr, psplim, control fields (4 words) */
  222. current_ptr += 4;
  223. }
  224. else
  225. {
  226. /* Not a valid EXC_RETURN - this is normal thread context from PendSV_Handler */
  227. fpu_flag = 0;
  228. /* For normal thread context, stack_top points to [tz,lr,psplim,control] */
  229. /* Skip tz, lr, psplim, control fields (4 words) */
  230. current_ptr += 4;
  231. }
  232. /* Extract core registers r4-r11 (software saved by RT-Thread) */
  233. core_regset->r4 = *current_ptr++;
  234. core_regset->r5 = *current_ptr++;
  235. core_regset->r6 = *current_ptr++;
  236. core_regset->r7 = *current_ptr++;
  237. core_regset->r8 = *current_ptr++;
  238. core_regset->r9 = *current_ptr++;
  239. core_regset->r10 = *current_ptr++;
  240. core_regset->r11 = *current_ptr++;
  241. #if MCD_FPU_SUPPORT
  242. /* If FPU context is active, d8-d15 registers are saved after r4-r11 */
  243. if (fpu_flag)
  244. {
  245. /* Copy FPU d8-d15 registers (software saved by HardFault_Handler) */
  246. /* Each double precision register is 64 bits = 2 words */
  247. uint64_t *fp_d_ptr = (uint64_t *)current_ptr;
  248. for (int i = 0; i < 8; i++) /* d8-d15 = 8 registers */
  249. {
  250. (&fp_regset->d8)[i] = *fp_d_ptr++;
  251. }
  252. current_ptr = (uint32_t *)fp_d_ptr;
  253. }
  254. #endif
  255. /* Extract hardware exception frame (automatically saved by ARM Cortex-M) */
  256. core_regset->r0 = *current_ptr++;
  257. core_regset->r1 = *current_ptr++;
  258. core_regset->r2 = *current_ptr++;
  259. core_regset->r3 = *current_ptr++;
  260. core_regset->r12 = *current_ptr++;
  261. core_regset->lr = *current_ptr++;
  262. core_regset->pc = *current_ptr++;
  263. core_regset->xpsr = *current_ptr++;
  264. #if MCD_FPU_SUPPORT
  265. /* If FPU context is active, d0-d7 and FPSCR are saved after exception frame */
  266. if (fpu_flag)
  267. {
  268. /* Copy FPU d0-d7 registers (hardware saved by ARM Cortex-M) */
  269. /* Each double precision register is 64 bits = 2 words */
  270. uint64_t *fp_d_ptr = (uint64_t *)current_ptr;
  271. for (int i = 0; i < 8; i++) /* d0-d7 = 8 registers */
  272. {
  273. (&fp_regset->d0)[i] = *fp_d_ptr++;
  274. }
  275. current_ptr = (uint32_t *)fp_d_ptr;
  276. /* Copy FPSCR register (FPU status and control) */
  277. fp_regset->fpscr = *current_ptr++;
  278. /* Skip NO_NAME field (reserved/alignment) */
  279. current_ptr++;
  280. }
  281. #endif
  282. /* SP should point to the current stack pointer position after all saved data */
  283. core_regset->sp = (uintptr_t)current_ptr;
  284. }
  285. /**
  286. * @brief ARM Cortex-M33 specific hard fault exception handler for MCoreDump
  287. *
  288. * This function handles ARM Cortex-M33 specific stack frame processing when a
  289. * hard fault occurs. It follows the armv7m approach for simple and reliable
  290. * stack pointer calculation.
  291. *
  292. * HardFault Stack Frame Layout (created by HardFault_Handler):
  293. * +-------------------+ <- Exception occurs here
  294. * | Hardware Exception| (32 bytes: r0,r1,r2,r3,r12,lr,pc,xpsr)
  295. * | Stack Frame | (+ optional 72 bytes FPU: s0-s15,FPSCR,NO_NAME)
  296. * +-------------------+ <- context parameter points here
  297. * | r11 | (4 bytes, software saved in HardFault_Handler)
  298. * | r10 | (4 bytes, software saved in HardFault_Handler)
  299. * | ... | (...)
  300. * | r4 | (4 bytes, software saved in HardFault_Handler)
  301. * +-------------------+
  302. * | FPU s31 | (4 bytes, if FPU context active)
  303. * | FPU s30 | (4 bytes, if FPU context active)
  304. * | ... | (...)
  305. * | FPU s16 | (4 bytes, if FPU context active)
  306. * +-------------------+
  307. * | FPU flag | (4 bytes, if MCD_FPU_SUPPORT enabled)
  308. * +-------------------+ <- Target position for collect_registers_armv8m
  309. *
  310. * @param context Pointer to exception_stack_frame from HardFault_Handler
  311. * @return int Always returns 0 after processing coredump
  312. */
  313. int armv8m_hard_fault_exception_hook(void *context)
  314. {
  315. /* Add debug output to confirm we reach this function */
  316. mcd_print("armv8m_hard_fault_exception_hook called, context = 0x%08x\n", (uint32_t)context);
  317. /*
  318. * Based on HardFault_Handler implementation in context_gcc.S:
  319. *
  320. * For EXCEPTION context (HardFault_Handler):
  321. * Stack layout from context (exception_stack_frame) backwards:
  322. * context -> [r0,r1,r2,r3,r12,lr,pc,xpsr] (exception frame)
  323. * context-32 -> [r11,r10,r9,r8,r7,r6,r5,r4] (8 words, STMFD r0!, {r4-r11})
  324. * context-48 -> [tz,lr,psplim,control] (4 words, STMFD r0!, {r2-r5})
  325. * context-52 -> [EXC_RETURN] (1 word, STMFD r0!, {lr})
  326. *
  327. * We need to point to EXC_RETURN for collect_registers_armv8m
  328. */
  329. uint32_t *stack_ptr = (uint32_t *)context;
  330. /* Move backwards to find EXC_RETURN position */
  331. stack_ptr -= 8; /* r4-r11 (8 registers) */
  332. stack_ptr -= 4; /* tz, lr, psplim, control (4 fields) */
  333. stack_ptr -= 1; /* EXC_RETURN */
  334. #ifdef RT_USING_FINSH
  335. extern long list_thread(void);
  336. list_thread();
  337. #endif
  338. /* Now stack_ptr points to EXC_RETURN, which is what collect_registers_armv8m expects */
  339. collect_registers_armv8m(stack_ptr,
  340. get_cur_core_regset_address(),
  341. get_cur_fp_regset_address());
  342. /* Generate coredump using memory mode */
  343. mcd_faultdump(MCD_OUTPUT_MEMORY);
  344. return 0;
  345. }