0004-cortex-m-Use-assembly-exception-handler-and-routine-.patch 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. From f7d7b22a9e711783c99be55b2c1c437a6808f24d Mon Sep 17 00:00:00 2001
  2. From: Paul Kocialkowski <contact@paulk.fr>
  3. Date: Sat, 23 Jul 2016 15:51:58 +0200
  4. Subject: [PATCH 4/4] cortex-m: Use assembly exception handler and routine for
  5. task 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, clobbering the value resulting
  15. from the context switch.
  16. For this reason, it is safer to have assembly routines for exception
  17. handlers that do context switching.
  18. BUG=chromium:631514
  19. BRANCH=None
  20. TEST=Build and run big EC with a recent GCC version
  21. Change-Id: Ia356321021731e6e372af152c962d8f01c065da5
  22. Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
  23. ---
  24. core/cortex-m/switch.S | 90 +++++++++++++++++++++++++++++++++++---------------
  25. core/cortex-m/task.c | 28 ++++------------
  26. 2 files changed, 69 insertions(+), 49 deletions(-)
  27. diff --git a/core/cortex-m/switch.S b/core/cortex-m/switch.S
  28. index 92c7e51..80a99c8 100644
  29. --- a/core/cortex-m/switch.S
  30. +++ b/core/cortex-m/switch.S
  31. @@ -13,6 +13,48 @@
  32. .code 16
  33. /**
  34. + * Start the task scheduling. r0 is a pointer to task_stack_ready, which is
  35. + * set to 1 after the task stack is set up.
  36. + */
  37. +.global __task_start
  38. +.thumb_func
  39. +__task_start:
  40. + ldr r2,=scratchpad @ area used as dummy thread stack for the first switch
  41. +#ifdef CONFIG_FPU
  42. + mov r3, #6 @ use : priv. mode / thread stack / floating point on
  43. +#else
  44. + mov r3, #2 @ use : priv. mode / thread stack / no floating point
  45. +#endif
  46. + add r2, #17*4 @ put the pointer at the top of the stack
  47. + mov r1, #0 @ __Schedule parameter : re-schedule nothing
  48. + msr psp, r2 @ setup a thread stack up to the first context switch
  49. + mov r2, #1
  50. + isb @ ensure the write is done
  51. + msr control, r3
  52. + mov r3, r0
  53. + mov r0, #0 @ __Schedule parameter : de-schedule nothing
  54. + isb @ ensure the write is done
  55. + str r2, [r3] @ Task scheduling is now active
  56. + bl __schedule @ execute the task with the highest priority
  57. + /* we should never return here */
  58. + mov r0, #1 @ set to EC_ERROR_UNKNOWN
  59. + bx lr
  60. +
  61. +/**
  62. + * SVC exception handler
  63. + */
  64. +.global svc_handler
  65. +.thumb_func
  66. +svc_handler:
  67. + push {lr} @ save link register
  68. + bl __svc_handler @ call svc handler helper
  69. + ldr r3,=current_task @ load the current task's address
  70. + ldr r1, [r3] @ load the current task
  71. + cmp r0, r1 @ compare with previous task returned by helper
  72. + beq svc_handler_return @ return if they are the same
  73. + /* continue to __switchto to switch to the new task */
  74. +
  75. +/**
  76. * Task context switching
  77. *
  78. * Change the task scheduled after returning from the exception.
  79. @@ -30,8 +72,6 @@
  80. * r0, r1, r2, r3, r12, lr, pc, psr, r4, r5, r6, r7, r8, r9, r10, r11
  81. * exception frame <|> additional registers
  82. */
  83. -.global __switchto
  84. -.thumb_func
  85. __switchto:
  86. mrs r3, psp @ get the task stack where the context has been saved
  87. ldr r2, [r1] @ get the new scheduled task stack pointer
  88. @@ -39,33 +79,29 @@ __switchto:
  89. ldmia r2!, {r4-r11} @ restore r4-r11 for the next task context
  90. str r3, [r0] @ save the task stack pointer in its context
  91. msr psp, r2 @ set the process stack pointer to exception context
  92. - bx lr @ return from exception
  93. +
  94. +svc_handler_return:
  95. + pop {pc} @ return from exception or return to caller
  96. /**
  97. - * Start the task scheduling. r0 is a pointer to task_stack_ready, which is
  98. - * set to 1 after the task stack is set up.
  99. + * Resched task if needed:
  100. + * Continue iff a rescheduling event happened or profiling is active,
  101. + * and we are not called from another exception.
  102. */
  103. -.global __task_start
  104. +.global task_resched_if_needed
  105. .thumb_func
  106. -__task_start:
  107. - ldr r2,=scratchpad @ area used as dummy thread stack for the first switch
  108. -#ifdef CONFIG_FPU
  109. - mov r3, #6 @ use : priv. mode / thread stack / floating point on
  110. -#else
  111. - mov r3, #2 @ use : priv. mode / thread stack / no floating point
  112. -#endif
  113. - add r2, #17*4 @ put the pointer at the top of the stack
  114. - mov r1, #0 @ __Schedule parameter : re-schedule nothing
  115. - msr psp, r2 @ setup a thread stack up to the first context switch
  116. - mov r2, #1
  117. - isb @ ensure the write is done
  118. - msr control, r3
  119. - mov r3, r0
  120. - mov r0, #0 @ __Schedule parameter : de-schedule nothing
  121. - isb @ ensure the write is done
  122. - str r2, [r3] @ Task scheduling is now active
  123. - bl __schedule @ execute the task with the highest priority
  124. - /* we should never return here */
  125. - mov r0, #1 @ set to EC_ERROR_UNKNOWN
  126. - bx lr
  127. +task_resched_if_needed:
  128. + push {lr} @ save link register
  129. + ldr r3,=need_resched_or_profiling @ load need's address
  130. + ldr r1, [r3] @ load need
  131. + cbz r1, task_resched_if_needed_return @ return if there is no need
  132. + and r0, #0xf @ called from another exception
  133. + cmp r0, #1 @ check bit
  134. + beq task_resched_if_needed_return @ return if called from exception
  135. + movs r1, #0 @ desched nothing
  136. + movs r0, #0 @ resched nothing
  137. + bl svc_handler @ re-schedule the highest priority
  138. + @ task
  139. +task_resched_if_needed_return:
  140. + pop {pc} @ return to caller
  141. diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c
  142. index bfb3a9b..9935a28 100644
  143. --- a/core/cortex-m/task.c
  144. +++ b/core/cortex-m/task.c
  145. @@ -57,7 +57,6 @@ static uint32_t task_switches; /* Number of times active task changed */
  146. static uint32_t irq_dist[CONFIG_IRQ_COUNT]; /* Distribution of IRQ calls */
  147. #endif
  148. -extern void __switchto(task_ *from, task_ *to);
  149. extern int __task_start(int *task_stack_ready);
  150. #ifndef CONFIG_LOW_POWER_IDLE
  151. @@ -124,7 +123,7 @@ uint32_t scratchpad[17+18];
  152. uint32_t scratchpad[17];
  153. #endif
  154. -static task_ *current_task = (task_ *)scratchpad;
  155. +task_ *current_task = (task_ *)scratchpad;
  156. /*
  157. * Should IRQs chain to svc_handler()? This should be set if either of the
  158. @@ -137,7 +136,7 @@ static task_ *current_task = (task_ *)scratchpad;
  159. * task unblocking. After checking for a task switch, svc_handler() will clear
  160. * the flag (unless profiling is also enabled; then the flag remains set).
  161. */
  162. -static int need_resched_or_profiling;
  163. +int need_resched_or_profiling;
  164. /*
  165. * Bitmap of all tasks ready to be run.
  166. @@ -197,7 +196,7 @@ int task_start_called(void)
  167. /**
  168. * Scheduling system call
  169. */
  170. -void svc_handler(int desched, task_id_t resched)
  171. +task_ *__svc_handler(int desched, task_id_t resched)
  172. {
  173. task_ *current, *next;
  174. #ifdef CONFIG_TASK_PROFILING
  175. @@ -264,16 +263,13 @@ void svc_handler(int desched, task_id_t resched)
  176. need_resched_or_profiling = 0;
  177. #endif
  178. - /* Nothing to do */
  179. - if (next == current)
  180. - return;
  181. -
  182. /* Switch to new task */
  183. #ifdef CONFIG_TASK_PROFILING
  184. - task_switches++;
  185. + if (next != current)
  186. + task_switches++;
  187. #endif
  188. current_task = next;
  189. - __switchto(current, next);
  190. + return current;
  191. }
  192. void __schedule(int desched, int resched)
  193. @@ -313,18 +309,6 @@ void task_start_irq_handler(void *excep_return)
  194. }
  195. #endif
  196. -void task_resched_if_needed(void *excep_return)
  197. -{
  198. - /*
  199. - * Continue iff a rescheduling event happened or profiling is active,
  200. - * and we are not called from another exception.
  201. - */
  202. - if (!need_resched_or_profiling || (((uint32_t)excep_return & 0xf) == 1))
  203. - return;
  204. -
  205. - svc_handler(0, 0);
  206. -}
  207. -
  208. static uint32_t __wait_evt(int timeout_us, task_id_t resched)
  209. {
  210. task_ *tsk = current_task;
  211. --
  212. 2.9.0