0006-cortex-m0-Use-assembly-exception-handlers-for-task-s.patch 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. From 9dd7ae82d3f3fa9dae31a442365e233a0b44cce3 Mon Sep 17 00:00:00 2001
  2. From: Paul Kocialkowski <contact@paulk.fr>
  3. Date: Sat, 23 Jul 2016 14:17:32 +0200
  4. Subject: [PATCH 6/6] cortex-m0: Use assembly exception handlers for task
  5. switching
  6. The way Cortex processors handle exceptions allows writing exception
  7. routines directly in C, as return from exception is handled by providing
  8. a special value for the link register.
  9. However, it is not safe to do this when doing context switching. In
  10. particular, C handlers may push some general-purpose registers that
  11. are used by the handler and pop them later, even when context switch
  12. has happened in the meantime. While the processor will restore {r0-r3}
  13. from the stack when returning from an exception, the C handler code
  14. may push, use and pop another register, such as r4.
  15. It turns out that GCC 4.8 would generally only use r3 in svc_handler and
  16. pendsv_handler, but newer versions tend to use r4, thus clobbering r4
  17. that was restored from the context switch and leading up to a fault
  18. when r4 is used by the task code.
  19. An occurrence of this behaviour takes place with GCC > 4.8 in __wait_evt,
  20. where "me" is stored in r4, which gets clobbered after an exception
  21. triggers pendsv_handler. The exception handler uses r4 internally, does
  22. a context switch and then restores the previous value of r4, which is
  23. not restored by the processor's internal, thus clobbering r4.
  24. This ends up with the following assertion failure:
  25. 'tskid < TASK_ID_COUNT' in timer_cancel() at common/timer.c:137
  26. For this reason, it is safer to have assembly routines for exception
  27. handlers that do context switching.
  28. BUG=chromium:631514
  29. BRANCH=None
  30. TEST=Build and run speedy EC with a recent GCC version
  31. Change-Id: Ib068bc12ce2204aee3e0f563efcb94f15aa87013
  32. Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
  33. ---
  34. core/cortex-m0/switch.S | 81 ++++++++++++++++++++++++++++++++++---------------
  35. core/cortex-m0/task.c | 27 +----------------
  36. 2 files changed, 58 insertions(+), 50 deletions(-)
  37. diff --git a/core/cortex-m0/switch.S b/core/cortex-m0/switch.S
  38. index 95ea29e..d4b47cd 100644
  39. --- a/core/cortex-m0/switch.S
  40. +++ b/core/cortex-m0/switch.S
  41. @@ -7,12 +7,52 @@
  42. #include "config.h"
  43. +#define CPU_SCB_ICSR 0xe000ed04
  44. +
  45. .text
  46. .syntax unified
  47. .code 16
  48. /**
  49. + * Start the task scheduling. r0 is a pointer to task_stack_ready, which is
  50. + * set to 1 after the task stack is set up.
  51. + */
  52. +.global __task_start
  53. +.thumb_func
  54. +__task_start:
  55. + ldr r2,=scratchpad @ area used as dummy thread stack for the first switch
  56. + movs r3, #2 @ use : priv. mode / thread stack / no floating point
  57. + adds r2, #17*4 @ put the pointer at the top of the stack
  58. + movs r1, #0 @ __Schedule parameter : re-schedule nothing
  59. + msr psp, r2 @ setup a thread stack up to the first context switch
  60. + movs r2, #1
  61. + isb @ ensure the write is done
  62. + msr control, r3
  63. + movs r3, r0
  64. + movs r0, #0 @ __Schedule parameter : de-schedule nothing
  65. + isb @ ensure the write is done
  66. + str r2, [r3] @ Task scheduling is now active
  67. + bl __schedule @ execute the task with the highest priority
  68. + /* we should never return here */
  69. + movs r0, #1 @ set to EC_ERROR_UNKNOWN
  70. + bx lr
  71. +
  72. +/**
  73. + * SVC exception handler
  74. + */
  75. +.global svc_handler
  76. +.thumb_func
  77. +svc_handler:
  78. + push {lr} @ save link register
  79. + bl __svc_handler @ call svc handler helper
  80. + ldr r3,=current_task @ load the current task's address
  81. + ldr r1, [r3] @ load the current task
  82. + cmp r0, r1 @ compare with previous task returned by helper
  83. + beq svc_handler_return @ return if they are the same
  84. + /* continue to __switchto to switch to the new task */
  85. +
  86. +/**
  87. * Task context switching
  88. *
  89. * Change the task scheduled after returning from the exception.
  90. @@ -30,8 +70,6 @@
  91. * r8, r9, r10, r11, r4, r5, r6, r7, r0, r1, r2, r3, r12, lr, pc, psr
  92. * additional registers <|> exception frame
  93. */
  94. -.global __switchto
  95. -.thumb_func
  96. __switchto:
  97. mrs r2, psp @ get the task stack where the context has been saved
  98. mov r3, sp
  99. @@ -53,29 +91,24 @@ __switchto:
  100. mov r11, r7
  101. ldmia r2!, {r4-r7} @ restore r4-r7 for the next task context
  102. msr psp, r2 @ set the process stack pointer to exception context
  103. - bx lr @ return from exception
  104. +
  105. +svc_handler_return:
  106. + pop {pc} @ return from exception or return to caller
  107. /**
  108. - * Start the task scheduling. r0 is a pointer to task_stack_ready, which is
  109. - * set to 1 after the task stack is set up.
  110. + * PendSVC exception handler
  111. */
  112. -.global __task_start
  113. +.global pendsv_handler
  114. .thumb_func
  115. -__task_start:
  116. - ldr r2,=scratchpad @ area used as dummy thread stack for the first switch
  117. - movs r3, #2 @ use : priv. mode / thread stack / no floating point
  118. - adds r2, #17*4 @ put the pointer at the top of the stack
  119. - movs r1, #0 @ __Schedule parameter : re-schedule nothing
  120. - msr psp, r2 @ setup a thread stack up to the first context switch
  121. - movs r2, #1
  122. - isb @ ensure the write is done
  123. - msr control, r3
  124. - movs r3, r0
  125. - movs r0, #0 @ __Schedule parameter : de-schedule nothing
  126. - isb @ ensure the write is done
  127. - str r2, [r3] @ Task scheduling is now active
  128. - bl __schedule @ execute the task with the highest priority
  129. - /* we should never return here */
  130. - movs r0, #1 @ set to EC_ERROR_UNKNOWN
  131. - bx lr
  132. -
  133. +pendsv_handler:
  134. + push {lr} @ save link register
  135. + ldr r0, =#CPU_SCB_ICSR @ load CPU_SCB_ICSR's address
  136. + movs r1, #1 @ prepare left shift (1 << 27)
  137. + lsls r1, #27 @ shift the bit
  138. + str r1, [r0] @ clear pending flag
  139. + cpsid i @ ensure we have priority 0 during re-scheduling
  140. + movs r1, #0 @ desched nothing
  141. + movs r0, #0 @ resched nothing
  142. + bl svc_handler @ re-schedule the highest priority task
  143. + cpsie i @ leave priority 0
  144. + pop {pc} @ return from exception
  145. diff --git a/core/cortex-m0/task.c b/core/cortex-m0/task.c
  146. index e51621b..f96ccf8 100644
  147. --- a/core/cortex-m0/task.c
  148. +++ b/core/cortex-m0/task.c
  149. @@ -57,7 +57,6 @@ static uint32_t task_switches; /* Number of times active task changed */
  150. static uint32_t irq_dist[CONFIG_IRQ_COUNT]; /* Distribution of IRQ calls */
  151. #endif
  152. -extern void __switchto(task_ *from, task_ *to);
  153. extern int __task_start(int *task_stack_ready);
  154. #ifndef CONFIG_LOW_POWER_IDLE
  155. @@ -120,7 +119,7 @@ uint8_t task_stacks[0
  156. /* Reserve space to discard context on first context switch. */
  157. uint32_t scratchpad[17];
  158. -static task_ *current_task = (task_ *)scratchpad;
  159. +task_ *current_task = (task_ *)scratchpad;
  160. /*
  161. * Bitmap of all tasks ready to be run.
  162. @@ -242,18 +241,6 @@ task_ *__svc_handler(int desched, task_id_t resched)
  163. return current;
  164. }
  165. -void svc_handler(int desched, task_id_t resched)
  166. -{
  167. - /*
  168. - * The layout of the this routine (and the __svc_handler companion one)
  169. - * ensures that we are getting the right tail call optimization from
  170. - * the compiler.
  171. - */
  172. - task_ *prev = __svc_handler(desched, resched);
  173. - if (current_task != prev)
  174. - __switchto(prev, current_task);
  175. -}
  176. -
  177. void __schedule(int desched, int resched)
  178. {
  179. register int p0 asm("r0") = desched;
  180. @@ -262,18 +249,6 @@ void __schedule(int desched, int resched)
  181. asm("svc 0" : : "r"(p0), "r"(p1));
  182. }
  183. -void pendsv_handler(void)
  184. -{
  185. - /* Clear pending flag */
  186. - CPU_SCB_ICSR = (1 << 27);
  187. -
  188. - /* ensure we have priority 0 during re-scheduling */
  189. - __asm__ __volatile__("cpsid i");
  190. - /* re-schedule the highest priority task */
  191. - svc_handler(0, 0);
  192. - __asm__ __volatile__("cpsie i");
  193. -}
  194. -
  195. #ifdef CONFIG_TASK_PROFILING
  196. void task_start_irq_handler(void *excep_return)
  197. {
  198. --
  199. 2.9.0