]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/swtch.S
Merge lld trunk r321017 to contrib/llvm/tools/lld.
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / swtch.S
1 /*-
2  * Copyright (c) 2014 Andrew Turner
3  * Copyright (c) 2014 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * This software was developed by Andrew Turner under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31
32 #include "assym.s"
33 #include "opt_kstack_pages.h"
34 #include "opt_sched.h"
35
36 #include <machine/asm.h>
37
38 __FBSDID("$FreeBSD$");
39
40 .macro clear_step_flag pcbflags, tmp
41         tbz     \pcbflags, #PCB_SINGLE_STEP_SHIFT, 999f
42         mrs     \tmp, mdscr_el1
43         bic     \tmp, \tmp, #1
44         msr     mdscr_el1, \tmp
45         isb
46 999:
47 .endm
48
49 .macro set_step_flag pcbflags, tmp
50         tbz     \pcbflags, #PCB_SINGLE_STEP_SHIFT, 999f
51         mrs     \tmp, mdscr_el1
52         orr     \tmp, \tmp, #1
53         msr     mdscr_el1, \tmp
54         isb
55 999:
56 .endm
57
58 /*
59  * void cpu_throw(struct thread *old, struct thread *new)
60  */
61 ENTRY(cpu_throw)
62         /* Of old == NULL skip disabling stepping */
63         cbz     x0, 1f
64
65         /* If we were single stepping, disable it */
66         ldr     x4, [x0, #TD_PCB]
67         ldr     w5, [x4, #PCB_FLAGS]
68         clear_step_flag w5, x6
69 1:
70
71 #ifdef VFP
72         /* Backup the new thread pointer around a call to C code */
73         mov     x19, x1
74         bl      vfp_discard
75         mov     x1, x19
76 #endif
77
78         /* Store the new curthread */
79         str     x1, [x18, #PC_CURTHREAD]
80         /* And the new pcb */
81         ldr     x4, [x1, #TD_PCB]
82         str     x4, [x18, #PC_CURPCB]
83
84         /*
85          * TODO: We may need to flush the cache here.
86          */
87
88         /* Switch to the new pmap */
89         ldr     x28, [x1, #TD_PROC]
90         ldr     x5, [x28, #(P_MD + MD_L0ADDR)]
91         msr     ttbr0_el1, x5
92         isb
93
94         /* Invalidate the TLB */
95         dsb     ishst
96         tlbi    vmalle1
97         dsb     ish
98         isb
99
100         /* If we are single stepping, enable it */
101         ldr     w5, [x4, #PCB_FLAGS]
102         set_step_flag w5, x6
103
104         /* Restore the registers */
105         ldp     x5, x6, [x4, #PCB_SP]
106         mov     sp, x5
107         msr     tpidr_el0, x6
108         ldr     x6, [x4, #PCB_TPIDRRO]
109         msr     tpidrro_el0, x6
110         ldp     x8, x9, [x4, #PCB_REGS + 8 * 8]
111         ldp     x10, x11, [x4, #PCB_REGS + 10 * 8]
112         ldp     x12, x13, [x4, #PCB_REGS + 12 * 8]
113         ldp     x14, x15, [x4, #PCB_REGS + 14 * 8]
114         ldp     x16, x17, [x4, #PCB_REGS + 16 * 8]
115         ldr          x19, [x4, #PCB_REGS + 19 * 8]
116         ldp     x20, x21, [x4, #PCB_REGS + 20 * 8]
117         ldp     x22, x23, [x4, #PCB_REGS + 22 * 8]
118         ldp     x24, x25, [x4, #PCB_REGS + 24 * 8]
119         ldp     x26, x27, [x4, #PCB_REGS + 26 * 8]
120         ldp     x28, x29, [x4, #PCB_REGS + 28 * 8]
121         ldr     x30, [x4, #PCB_REGS + 30 * 8]
122
123         ret
124 END(cpu_throw)
125
126 /*
127  * void cpu_switch(struct thread *old, struct thread *new, struct mtx *mtx)
128  *
129  * x0 = old
130  * x1 = new
131  * x2 = mtx
132  * x3 to x7, x16 and x17 are caller saved
133  */
134 ENTRY(cpu_switch)
135         /*
136          * Save the old context.
137          */
138         ldr     x4, [x0, #TD_PCB]
139
140         /* Store the callee-saved registers */
141         stp     x8, x9, [x4, #PCB_REGS + 8 * 8]
142         stp     x10, x11, [x4, #PCB_REGS + 10 * 8]
143         stp     x12, x13, [x4, #PCB_REGS + 12 * 8]
144         stp     x14, x15, [x4, #PCB_REGS + 14 * 8]
145         stp     x16, x17, [x4, #PCB_REGS + 16 * 8]
146         stp     x18, x19, [x4, #PCB_REGS + 18 * 8]
147         stp     x20, x21, [x4, #PCB_REGS + 20 * 8]
148         stp     x22, x23, [x4, #PCB_REGS + 22 * 8]
149         stp     x24, x25, [x4, #PCB_REGS + 24 * 8]
150         stp     x26, x27, [x4, #PCB_REGS + 26 * 8]
151         stp     x28, x29, [x4, #PCB_REGS + 28 * 8]
152         str     x30, [x4, #PCB_REGS + 30 * 8]
153         /* And the old stack pointer */
154         mov     x5, sp
155         mrs     x6, tpidrro_el0
156         str     x6, [x4, #PCB_TPIDRRO]
157         mrs     x6, tpidr_el0
158         stp     x5, x6, [x4, #PCB_SP]
159
160         /* If we were single stepping, disable it */
161         ldr     w5, [x4, #PCB_FLAGS]
162         clear_step_flag w5, x6
163
164 #ifdef VFP
165         mov     x19, x0
166         mov     x20, x1
167         mov     x21, x2
168         /* Load the pcb address */
169         mov     x1, x4
170         bl      vfp_save_state
171         mov     x2, x21
172         mov     x1, x20
173         mov     x0, x19
174 #endif
175
176         /* Store the new curthread */
177         str     x1, [x18, #PC_CURTHREAD]
178
179         /*
180          * Restore the saved context and set it as curpcb.
181          */
182         ldr     x4, [x1, #TD_PCB]
183         str     x4, [x18, #PC_CURPCB]
184
185         /*
186          * TODO: We may need to flush the cache here if switching
187          * to a user process.
188          */
189
190         /* Load the new proc address */
191         ldr     x28, [x1, #TD_PROC]
192
193         /* Switch to the new pmap */
194         ldr     x5, [x28, #(P_MD + MD_L0ADDR)]
195         msr     ttbr0_el1, x5
196         isb
197
198         /* Invalidate the TLB */
199         dsb     ishst
200         tlbi    vmalle1
201         dsb     ish
202         isb
203
204         /*
205          * Release the old thread. This doesn't need to be a store-release
206          * as the above dsb instruction will provide release semantics.
207          */
208         str     x2, [x0, #TD_LOCK]
209 #if defined(SCHED_ULE) && defined(SMP)
210         /* Spin if TD_LOCK points to a blocked_lock */
211         ldr     x2, =_C_LABEL(blocked_lock)
212 1:
213         ldar    x3, [x1, #TD_LOCK]
214         cmp     x3, x2
215         b.eq    1b
216 #endif
217
218         /* If we are single stepping, enable it */
219         ldr     w5, [x4, #PCB_FLAGS]
220         set_step_flag w5, x6
221
222         /* Restore the registers */
223         ldp     x5, x6, [x4, #PCB_SP]
224         mov     sp, x5
225         msr     tpidr_el0, x6
226         ldr     x6, [x4, #PCB_TPIDRRO]
227         msr     tpidrro_el0, x6
228         ldp     x8, x9, [x4, #PCB_REGS + 8 * 8]
229         ldp     x10, x11, [x4, #PCB_REGS + 10 * 8]
230         ldp     x12, x13, [x4, #PCB_REGS + 12 * 8]
231         ldp     x14, x15, [x4, #PCB_REGS + 14 * 8]
232         ldp     x16, x17, [x4, #PCB_REGS + 16 * 8]
233         ldr          x19, [x4, #PCB_REGS + 19 * 8]
234         ldp     x20, x21, [x4, #PCB_REGS + 20 * 8]
235         ldp     x22, x23, [x4, #PCB_REGS + 22 * 8]
236         ldp     x24, x25, [x4, #PCB_REGS + 24 * 8]
237         ldp     x26, x27, [x4, #PCB_REGS + 26 * 8]
238         ldp     x28, x29, [x4, #PCB_REGS + 28 * 8]
239         ldr     x30, [x4, #PCB_REGS + 30 * 8]
240
241         str     xzr, [x4, #PCB_REGS + 18 * 8]
242         ret
243 .Lcpu_switch_panic_str:
244         .asciz "cpu_switch: %p\0"
245 END(cpu_switch)
246
247 ENTRY(fork_trampoline)
248         mov     x0, x8
249         mov     x1, x9
250         mov     x2, sp
251         mov     fp, #0  /* Stack traceback stops here. */
252         bl      _C_LABEL(fork_exit)
253
254         /* Restore the registers other than x0 and x1 */
255         ldp     x2, x3, [sp, #TF_X + 2 * 8]
256         ldp     x4, x5, [sp, #TF_X + 4 * 8]
257         ldp     x6, x7, [sp, #TF_X + 6 * 8]
258         ldp     x8, x9, [sp, #TF_X + 8 * 8]
259         ldp     x10, x11, [sp, #TF_X + 10 * 8]
260         ldp     x12, x13, [sp, #TF_X + 12 * 8]
261         ldp     x14, x15, [sp, #TF_X + 14 * 8]
262         ldp     x16, x17, [sp, #TF_X + 16 * 8]
263         ldr          x19, [sp, #TF_X + 19 * 8]
264         ldp     x20, x21, [sp, #TF_X + 20 * 8]
265         ldp     x22, x23, [sp, #TF_X + 22 * 8]
266         ldp     x24, x25, [sp, #TF_X + 24 * 8]
267         ldp     x26, x27, [sp, #TF_X + 26 * 8]
268         ldp     x28, x29, [sp, #TF_X + 28 * 8]
269
270         /*
271          * Disable interrupts to avoid
272          * overwriting spsr_el1 and sp_el0 by an IRQ exception.
273          */
274         msr     daifset, #2
275
276         /* Restore sp and lr */
277         ldp     x0, x1, [sp]
278         msr     sp_el0, x0
279         mov     lr, x1
280
281         /* Restore elr and spsr */
282         ldp     x0, x1, [sp, #16]
283         msr     elr_el1, x0
284         msr     spsr_el1, x1
285
286         /* Finally x0 and x1 */
287         ldp     x0, x1, [sp, #TF_X + 0 * 8]
288         ldr     x18, [sp, #TF_X + 18 * 8]
289
290         /*
291          * No need for interrupts reenabling since PSR
292          * will be set to the desired value anyway.
293          */
294         eret
295         
296 END(fork_trampoline)
297
298 ENTRY(savectx)
299         /* Store the callee-saved registers */
300         stp     x8,  x9,  [x0, #PCB_REGS + 8 * 8]
301         stp     x10, x11, [x0, #PCB_REGS + 10 * 8]
302         stp     x12, x13, [x0, #PCB_REGS + 12 * 8]
303         stp     x14, x15, [x0, #PCB_REGS + 14 * 8]
304         stp     x16, x17, [x0, #PCB_REGS + 16 * 8]
305         stp     x18, x19, [x0, #PCB_REGS + 18 * 8]
306         stp     x20, x21, [x0, #PCB_REGS + 20 * 8]
307         stp     x22, x23, [x0, #PCB_REGS + 22 * 8]
308         stp     x24, x25, [x0, #PCB_REGS + 24 * 8]
309         stp     x26, x27, [x0, #PCB_REGS + 26 * 8]
310         stp     x28, x29, [x0, #PCB_REGS + 28 * 8]
311         str     x30, [x0, #PCB_REGS + 30 * 8]
312         /* And the old stack pointer */
313         mov     x5, sp
314         mrs     x6, tpidrro_el0
315         str     x6, [x0, #PCB_TPIDRRO]
316         mrs     x6, tpidr_el0
317         stp     x5, x6, [x0, #PCB_SP]
318
319         /* Store the VFP registers */
320 #ifdef VFP
321         mov     x28, lr
322         mov     x1, x0                  /* move pcb to the correct register */
323         mov     x0, xzr                 /* td = NULL */
324         bl      vfp_save_state
325         mov     lr, x28
326 #endif
327
328         ret
329 END(savectx)
330