1/*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date           Author       Notes
8 * 2009-10-11     Bernard      first version
9 * 2012-01-01     aozima       support context switch load/store FPU register.
10 * 2013-06-18     aozima       add restore MSP feature.
11 * 2013-06-23     aozima       support lazy stack optimized.
12 * 2018-07-24     aozima       enhancement hard fault exception handler.
13 */
14
15/**
16 * @addtogroup cortex-m4
17 */
18/*@{*/
19
20#include <rtconfig.h>
21
22.cpu cortex-m4
23.syntax unified
24.thumb
25.text
26
27.equ    SCB_VTOR,           0xE000ED08              /* Vector Table Offset Register */
28.equ    NVIC_INT_CTRL,      0xE000ED04              /* interrupt control state register */
29.equ    NVIC_SYSPRI2,       0xE000ED20              /* system priority register (2) */
30.equ    NVIC_PENDSV_PRI,    0xFFFF0000              /* PendSV and SysTick priority value (lowest) */
31.equ    NVIC_PENDSVSET,     0x10000000              /* value to trigger PendSV exception */
32
33/*
34 * rt_base_t rt_hw_interrupt_disable();
35 */
36.global rt_hw_interrupt_disable
37.type rt_hw_interrupt_disable, %function
38rt_hw_interrupt_disable:
39    MRS     r0, PRIMASK
40    CPSID   I
41    BX      LR
42
43/*
44 * void rt_hw_interrupt_enable(rt_base_t level);
45 */
46.global rt_hw_interrupt_enable
47.type rt_hw_interrupt_enable, %function
48rt_hw_interrupt_enable:
49    MSR     PRIMASK, r0
50    BX      LR
51
52/*
53 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
54 * r0 --> from
55 * r1 --> to
56 */
57.global rt_hw_context_switch_interrupt
58.type rt_hw_context_switch_interrupt, %function
59.global rt_hw_context_switch
60.type rt_hw_context_switch, %function
61
62rt_hw_context_switch_interrupt:
63rt_hw_context_switch:
64    /* set rt_thread_switch_interrupt_flag to 1 */
65    LDR     r2, =rt_thread_switch_interrupt_flag
66    LDR     r3, [r2]
67    CMP     r3, #1
68    BEQ     _reswitch
69    MOV     r3, #1
70    STR     r3, [r2]
71
72    LDR     r2, =rt_interrupt_from_thread   /* set rt_interrupt_from_thread */
73    STR     r0, [r2]
74
75_reswitch:
76    LDR     r2, =rt_interrupt_to_thread     /* set rt_interrupt_to_thread */
77    STR     r1, [r2]
78
79    LDR r0, =NVIC_INT_CTRL              /* trigger the PendSV exception (causes context switch) */
80    LDR r1, =NVIC_PENDSVSET
81    STR r1, [r0]
82    BX  LR
83
84/* r0 --> switch from thread stack
85 * r1 --> switch to thread stack
86 * psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
87 */
88.global PendSV_Handler
89.type PendSV_Handler, %function
90PendSV_Handler:
91    /* disable interrupt to protect context switch */
92    MRS r2, PRIMASK
93    CPSID   I
94
95    /* get rt_thread_switch_interrupt_flag */
96    LDR     r0, =rt_thread_switch_interrupt_flag    /* r0 = &rt_thread_switch_interrupt_flag */
97    LDR     r1, [r0]                                /* r1 = *r1 */
98    CMP     r1, #0x00                               /* compare r1 == 0x00 */
99    BNE     schedule
100    MSR     PRIMASK, r2                             /* if r1 == 0x00, do msr PRIMASK, r2 */
101    BX      lr                                      /* if r1 == 0x00, do bx lr */
102
103schedule:
104    PUSH    {r2}                                    /* store interrupt state */
105
106    /* clear rt_thread_switch_interrupt_flag to 0 */
107    MOV     r1, #0x00                               /* r1 = 0x00 */
108    STR     r1, [r0]                                /* *r0 = r1 */
109
110    /* skip register save at the first time */
111    LDR     r0, =rt_interrupt_from_thread           /* r0 = &rt_interrupt_from_thread */
112    LDR     r1, [r0]                                /* r1 = *r0 */
113    CBZ     r1, switch_to_thread                    /* if r1 == 0, goto switch_to_thread */
114
115    /* Whether TrustZone thread stack exists */
116    LDR     r1,  =rt_trustzone_current_context      /* r1 = &rt_secure_current_context */
117    LDR     r1, [r1]                                /* r1 = *r1 */
118    CBZ     r1, contex_ns_store                     /* if r1 == 0, goto contex_ns_store */
119
120    /*call TrustZone fun, Save TrustZone stack */
121    STMFD   sp!, {r0-r1, lr}                        /* push register */
122    MOV     r0, r1                                  /* r0 = rt_secure_current_context */
123    BL rt_trustzone_context_store                   /* call TrustZone store fun */
124    LDMFD   sp!, {r0-r1, lr}                        /* pop register */
125
126    /* check break from TrustZone */
127    MOV     r2, lr                                  /* r2 = lr */
128    TST     r2, #0x40                               /* if EXC_RETURN[6] is 1, TrustZone stack was used */
129    BEQ     contex_ns_store                         /* if r2 & 0x40 == 0, goto contex_ns_store */
130
131    /* push PSPLIM CONTROL PSP LR current_context to stack */
132    MRS     r3, psplim                              /* r3 = psplim */
133    MRS     r4, control                             /* r4 = control */
134    MRS     r5, psp                                 /* r5 = psp */
135    STMFD   r5!, {r1-r4}                            /* push to thread stack */
136
137    /* update from thread stack pointer */
138    LDR     r0, [r0]                                /* r0 = rt_thread_switch_interrupt_flag */
139    STR     r5, [r0]                                /* *r0 = r5 */
140    b switch_to_thread                              /* goto switch_to_thread */
141
142contex_ns_store:
143
144    MRS     r1, psp                                 /* get from thread stack pointer */
145
146#if defined (__VFP_FP__) && !defined(__SOFTFP__)
147    TST     lr, #0x10                               /* if(!EXC_RETURN[4]) */
148    IT      EQ
149    VSTMDBEQ  r1!, {d8 - d15}                       /* push FPU register s16~s31 */
150#endif
151
152    STMFD   r1!, {r4 - r11}                         /* push r4 - r11 register */
153
154    LDR     r2,  =rt_trustzone_current_context      /* r2 = &rt_secure_current_context */
155    LDR     r2, [r2]                                /* r2 = *r2 */
156    MOV     r3, lr                                  /* r3 = lr */
157    MRS     r4, psplim                              /* r4 = psplim */
158    MRS     r5, control                             /* r5 = control */
159    STMFD   r1!, {r2-r5}                            /* push to thread stack */
160
161    LDR     r0, [r0]
162    STR     r1, [r0]                                /* update from thread stack pointer */
163
164switch_to_thread:
165    LDR     r1, =rt_interrupt_to_thread
166    LDR     r1, [r1]
167    LDR     r1, [r1]                                /* load thread stack pointer */
168
169    /* update current TrustZone context */
170    LDMFD   r1!, {r2-r5}                            /* pop thread stack */
171    MSR     psplim, r4                              /* psplim = r4 */
172    MSR     control, r5                             /* control = r5 */
173    MOV     lr, r3                                  /* lr = r3 */
174    LDR     r6,  =rt_trustzone_current_context      /* r6 = &rt_secure_current_context */
175    STR     r2, [r6]                                /* *r6 = r2 */
176    MOV     r0, r2                                  /* r0 = r2 */
177
178    /* Whether TrustZone thread stack exists */
179    CBZ     r0, contex_ns_load                      /* if r0 == 0, goto contex_ns_load */
180    PUSH    {r1, r3}                                /* push lr, thread_stack */
181    BL rt_trustzone_context_load                    /* call TrustZone load fun */
182    POP     {r1, r3}                                /* pop lr, thread_stack */
183    MOV     lr, r3                                  /* lr = r1 */
184    TST     r3, #0x40                               /* if EXC_RETURN[6] is 1, TrustZone stack was used */
185    BEQ     contex_ns_load                          /* if r1 & 0x40 == 0, goto contex_ns_load */
186    B pendsv_exit
187
188contex_ns_load:
189    LDMFD   r1!, {r4 - r11}                         /* pop r4 - r11 register */
190
191#if defined (__VFP_FP__) && !defined(__SOFTFP__)
192    TST     lr, #0x10                               /* if(!EXC_RETURN[4]) */
193    IT      EQ
194    VLDMIAEQ  r1!, {d8 - d15}                       /* pop FPU register s16~s31 */
195#endif
196
197#if defined (RT_USING_MEM_PROTECTION)
198    PUSH    {r0-r3, r12, lr}
199    BL      rt_thread_self
200    BL      rt_hw_mpu_table_switch
201    POP     {r0-r3, r12, lr}
202#endif
203
204pendsv_exit:
205    MSR     psp, r1                                 /* update stack pointer */
206    /* restore interrupt */
207    POP    {r2}
208    MSR     PRIMASK, r2
209
210    BX      lr
211
212/*
213 * void rt_hw_context_switch_to(rt_uint32 to);
214 * r0 --> to
215 */
216.global rt_hw_context_switch_to
217.type rt_hw_context_switch_to, %function
218rt_hw_context_switch_to:
219    LDR r1, =rt_interrupt_to_thread
220    STR r0, [r1]
221
222#if defined (__VFP_FP__) && !defined(__SOFTFP__)
223    /* CLEAR CONTROL.FPCA */
224    MRS     r2, CONTROL         /* read */
225    BIC     r2, #0x04           /* modify */
226    MSR     CONTROL, r2         /* write-back */
227#endif
228
229    /* set from thread to 0 */
230    LDR r1, =rt_interrupt_from_thread
231    MOV r0, #0x0
232    STR r0, [r1]
233
234    /* set interrupt flag to 1 */
235    LDR     r1, =rt_thread_switch_interrupt_flag
236    MOV     r0, #1
237    STR     r0, [r1]
238
239    /* set the PendSV and SysTick exception priority */
240    LDR r0, =NVIC_SYSPRI2
241    LDR r1, =NVIC_PENDSV_PRI
242    LDR.W   r2, [r0,#0x00]       /* read       */
243    ORR     r1,r1,r2             /* modify     */
244    STR     r1, [r0]             /* write-back */
245
246    LDR r0, =NVIC_INT_CTRL      /* trigger the PendSV exception (causes context switch) */
247    LDR r1, =NVIC_PENDSVSET
248    STR r1, [r0]
249
250    /* restore MSP */
251    LDR     r0, =SCB_VTOR
252    LDR     r0, [r0]
253    LDR     r0, [r0]
254    NOP
255    MSR     msp, r0
256
257    /* enable interrupts at processor level */
258    CPSIE   F
259    CPSIE   I
260
261    /* ensure PendSV exception taken place before subsequent operation */
262    DSB
263    ISB
264
265    /* never reach here! */
266
267/* compatible with old version */
268.global rt_hw_interrupt_thread_switch
269.type rt_hw_interrupt_thread_switch, %function
270rt_hw_interrupt_thread_switch:
271    BX  lr
272    NOP
273
274.global HardFault_Handler
275.type HardFault_Handler, %function
276HardFault_Handler:
277    /* get current context */
278    MRS     r0, msp                                 /* get fault context from handler. */
279    TST     lr, #0x04                               /* if(!EXC_RETURN[2]) */
280    BEQ     get_sp_done
281    MRS     r0, psp                                 /* get fault context from thread. */
282get_sp_done:
283
284#if defined (__VFP_FP__) && !defined(__SOFTFP__)
285    TST     lr, #0x10                               /* if(!EXC_RETURN[4]) */
286    IT      EQ
287    VSTMDBEQ  r0!, {d8 - d15}                       /* push FPU register s16~s31 */
288#endif
289
290    STMFD   r0!, {r4 - r11}                         /* push r4 - r11 register */
291
292    LDR     r2,  =rt_trustzone_current_context      /* r2 = &rt_secure_current_context */
293    LDR     r2, [r2]                                /* r2 = *r2 */
294    MOV     r3, lr                                  /* r3 = lr */
295    MRS     r4, psplim                              /* r4 = psplim */
296    MRS     r5, control                             /* r5 = control */
297    STMFD   r0!, {r2-r5}                            /* push to thread stack */
298
299    STMFD   r0!, {lr}                               /* push exec_return register */
300
301    TST     lr, #0x04                               /* if(!EXC_RETURN[2]) */
302    BEQ     update_msp
303    MSR     psp, r0                                 /* update stack pointer to PSP. */
304    B       update_done
305update_msp:
306    MSR     msp, r0                                 /* update stack pointer to MSP. */
307update_done:
308
309    PUSH    {LR}
310    BL      rt_hw_hard_fault_exception
311    POP     {LR}
312
313    ORR     lr, lr, #0x04
314    BX      lr
315