demo_stack_check.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // See LICENSE for license details.
  2. #include <stdio.h>
  3. #include "nuclei_sdk_soc.h"
  4. #ifndef CFG_HAS_STACK_CHECK
  5. #error "This example require CPU Stack Check feature"
  6. #endif
  7. // Reserve 0x200 bytes for exception stack push/pop
  8. #ifndef __ICCRISCV__
  9. extern char __StackTop[];
  10. extern char __StackBottom[];
  11. #define STACK_TOP (unsigned long)(__StackTop)
  12. #define STACK_BOTTOM ((unsigned long)(__StackBottom) + 0x200)
  13. #define STACK_SIZE ((STACK_TOP) - (STACK_BOTTOM))
  14. #else
  15. extern char CSTACK$$Base[];
  16. extern char CSTACK$$Limit[];
  17. #define STACK_TOP (unsigned long)(CSTACK$$Limit)
  18. #define STACK_BOTTOM ((unsigned long)(CSTACK$$Base) + 0x200)
  19. #define STACK_SIZE ((STACK_TOP) - (STACK_BOTTOM))
  20. #endif
  21. #define STACK_CHECK_OVF_UDF_MODE 0
  22. #define STACK_TRACK_MODE 1
  23. #define STACK_CHECK_DIS_MODE 0xFF
  24. __STATIC_FORCEINLINE void set_stack_check_mode(uint8_t mode);
  25. static unsigned long stack_check_detected = 0;
  26. static unsigned long factorial_iter = 0;
  27. static void stack_corrupt_exception_handler(unsigned long mcause, unsigned long sp)
  28. {
  29. EXC_Frame_Type *exc_frame = (EXC_Frame_Type *)sp;
  30. // Note that the sp has grown downwardly 0x50 bytes in the exception entry saving context
  31. // In this demo, add sp by 0x50 is the sp value that triggered overflow/underflow
  32. set_stack_check_mode(STACK_CHECK_DIS_MODE);
  33. switch (mcause & MCAUSE_CAUSE) {
  34. case StackOverflow_EXCn:
  35. stack_check_detected = 1;
  36. printf("Stack overflow fault occurs at iteration %ld, cause: 0x%lx, epc: 0x%lx, sp: 0x%lx\r\n", factorial_iter, exc_frame->cause, exc_frame->epc, sp);
  37. break;
  38. case StackUnderflow_EXCn:
  39. stack_check_detected = 2;
  40. printf("Stack underflow fault occurs at iteration %ld, cause: 0x%lx, epc: 0x%lx, sp: 0x%lx\r\n", factorial_iter, exc_frame->cause, exc_frame->epc, sp);
  41. break;
  42. default: break;
  43. }
  44. factorial_iter = 0;
  45. // Comment it on purpose, continue to execute
  46. // while(1);
  47. }
  48. // Must use __STATIC_FORCEINLINE, to avoid stack use in underflow check,
  49. // or else it will cause underflow itself after decreasing the stack base value
  50. __STATIC_FORCEINLINE void set_stack_check_mode(uint8_t mode)
  51. {
  52. if (STACK_TRACK_MODE == mode) {
  53. // Track the stack top mode
  54. __RV_CSR_SET(CSR_MSTACK_CTRL, MSTACK_CTRL_MODE);
  55. __RV_CSR_SET(CSR_MSTACK_CTRL, MSTACK_CTRL_OVF_TRACK_EN);
  56. } else if (STACK_CHECK_OVF_UDF_MODE == mode) {
  57. // Overflow and Underflow check (Default) mode
  58. __RV_CSR_CLEAR(CSR_MSTACK_CTRL, MSTACK_CTRL_MODE);
  59. // Enable underflow and overflow check
  60. __RV_CSR_SET(CSR_MSTACK_CTRL, MSTACK_CTRL_UDF_EN | MSTACK_CTRL_OVF_TRACK_EN);
  61. } else {
  62. __RV_CSR_CLEAR(CSR_MSTACK_CTRL, 0xFF);
  63. }
  64. }
  65. // User should set the mstack_bound register before mstack_ctrl to check stack overflow.
  66. __STATIC_FORCEINLINE void set_stack_bound(unsigned long bound)
  67. {
  68. __RV_CSR_WRITE(CSR_MSTACK_BOUND, bound);
  69. printf("BOUND register set to: 0x%lx\r\n", (unsigned long)__RV_CSR_READ(CSR_MSTACK_BOUND));
  70. }
  71. // User should set the mstack_base register before mstack_ctrl to check stack underflow.
  72. __STATIC_FORCEINLINE void set_stack_base(unsigned long base)
  73. {
  74. __RV_CSR_WRITE(CSR_MSTACK_BASE, base);
  75. printf("BASE register set to: 0x%lx\r\n", (unsigned long)__RV_CSR_READ(CSR_MSTACK_BASE));
  76. }
  77. // A simple recursive function of calculating factorial
  78. // It will cause the stack to grow downwards, and overflow if only n is big enough
  79. static long factorial(int n, int log_en, int change_base)
  80. {
  81. static int iter_cnt = 0;
  82. factorial_iter = (unsigned long)n;
  83. if (1 == log_en) {
  84. // In stack track mode, BOUND register will update to sp value
  85. printf("Iterations: %d, stack bound: 0x%lx\n", ++iter_cnt, (unsigned long)__RV_CSR_READ(CSR_MSTACK_BOUND));
  86. }
  87. if ((1 == n) || stack_check_detected != 0) {
  88. if (1 == change_base) {
  89. // Decrease the stack base value to make the underflow condition on purpose
  90. set_stack_base((unsigned long)STACK_TOP - ((unsigned long)(STACK_SIZE) >> 1));
  91. set_stack_check_mode(STACK_CHECK_OVF_UDF_MODE);
  92. }
  93. iter_cnt = 0;
  94. stack_check_detected = 0;
  95. return 1;
  96. } else {
  97. return factorial(n - 1, log_en, change_base) * n;
  98. }
  99. }
  100. int main(void)
  101. {
  102. unsigned long stack_bound = 0;
  103. unsigned long stack_base = 0;
  104. /* In runtime, when download mode is ILM in evalsoc, stack top value(high address) is 0x90010000,
  105. stack bottom value(low address) is 0x9000f800 by default when using gnu gcc */
  106. printf("Stack's top high address: 0x%lx, stack's bottom low address: 0x%lx, stack size: 0x%lx\n", STACK_TOP, STACK_BOTTOM, STACK_SIZE);
  107. /* register corresponding exception */
  108. Exception_Register_EXC(StackOverflow_EXCn, (unsigned long)stack_corrupt_exception_handler);
  109. Exception_Register_EXC(StackUnderflow_EXCn, (unsigned long)stack_corrupt_exception_handler);
  110. printf("\n--------OVERFLOW CHECK MODE--------\r\n");
  111. /* set the stack bound for overflow check to the default */
  112. stack_bound = (unsigned long)STACK_BOTTOM;
  113. /* set the stack base for underflow check to the default */
  114. stack_base = (unsigned long)STACK_TOP;
  115. // Must set the BOUND and BASE before setting the check mode
  116. set_stack_bound(stack_bound);
  117. set_stack_base(stack_base);
  118. /* enable overflow and underflow check */
  119. set_stack_check_mode(STACK_CHECK_OVF_UDF_MODE);
  120. // In practice, n=100's factorial will trigger overflow when stack pushes
  121. stack_check_detected = 0;
  122. factorial(100, 0, 0);
  123. printf("\n--------UNDERFLOW CHECK MODE--------\r\n");
  124. // Will trigger underflow when stack pops
  125. stack_check_detected = 0;
  126. factorial(8, 0, 1);
  127. // Restore the BASE
  128. set_stack_base((unsigned long)STACK_TOP);
  129. printf("\n--------TRACK SP MODE--------\r\n");
  130. // Change to track mode first
  131. set_stack_check_mode(STACK_TRACK_MODE);
  132. // If the sp is smaller than BOUND, the BOUND's value will be updated to sp
  133. // So here set it to the default top high address
  134. set_stack_bound((unsigned long)STACK_TOP);
  135. // You will see the BOUND register track the sp value
  136. stack_check_detected = 0;
  137. factorial(18, 1, 0);
  138. printf("Calculate factorial over, the max stack used downwards to: 0x%lx\r\n", (unsigned long)__RV_CSR_READ(CSR_MSTACK_BOUND));
  139. printf("\nRerun it. The BOUND won't track sp if sp is bigger: \r\n");
  140. stack_check_detected = 0;
  141. factorial(5, 1, 0);
  142. printf("\nStack check demo over!\r\n");
  143. return 0;
  144. }