]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/cpu_switch.S
This commit was generated by cvs2svn to compensate for changes in r170242,
[FreeBSD/FreeBSD.git] / sys / amd64 / amd64 / cpu_switch.S
1 /*-
2  * Copyright (c) 2003 Peter Wemm.
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * William Jolitz.
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  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD$
34  */
35
36 #include <machine/asmacros.h>
37 #include <machine/specialreg.h>
38
39 #include "assym.s"
40
41 /*****************************************************************************/
42 /* Scheduling                                                                */
43 /*****************************************************************************/
44
45         .text
46
47 #ifdef SMP
48 #define LK      lock ;
49 #else
50 #define LK
51 #endif
52
53 /*
54  * cpu_throw()
55  *
56  * This is the second half of cpu_switch(). It is used when the current
57  * thread is either a dummy or slated to die, and we no longer care
58  * about its state.  This is only a slight optimization and is probably
59  * not worth it anymore.  Note that we need to clear the pm_active bits so
60  * we do need the old proc if it still exists.
61  * %rdi = oldtd
62  * %rsi = newtd
63  */
64 ENTRY(cpu_throw)
65         movl    PCPU(CPUID), %eax
66         testq   %rdi,%rdi                       /* no thread? */
67         jz      1f
68         /* release bit from old pm_active */
69         movq    TD_PROC(%rdi), %rdx             /* oldtd->td_proc */
70         movq    P_VMSPACE(%rdx), %rdx           /* proc->p_vmspace */
71         LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx)   /* clear old */
72 1:
73         movq    TD_PCB(%rsi),%rdx               /* newtd->td_proc */
74         movq    PCB_CR3(%rdx),%rdx
75         movq    %rdx,%cr3                       /* new address space */
76         /* set bit in new pm_active */
77         movq    TD_PROC(%rsi),%rdx
78         movq    P_VMSPACE(%rdx), %rdx
79         LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx)   /* set new */
80         jmp     sw1
81
82 /*
83  * cpu_switch(old, new)
84  *
85  * Save the current thread state, then select the next thread to run
86  * and load its state.
87  * %rdi = oldtd
88  * %rsi = newtd
89  */
90 ENTRY(cpu_switch)
91         /* Switch to new thread.  First, save context. */
92         movq    TD_PCB(%rdi),%r8
93
94         movq    (%rsp),%rax                     /* Hardware registers */
95         movq    %rax,PCB_RIP(%r8)
96         movq    %rbx,PCB_RBX(%r8)
97         movq    %rsp,PCB_RSP(%r8)
98         movq    %rbp,PCB_RBP(%r8)
99         movq    %r12,PCB_R12(%r8)
100         movq    %r13,PCB_R13(%r8)
101         movq    %r14,PCB_R14(%r8)
102         movq    %r15,PCB_R15(%r8)
103
104         testl   $PCB_32BIT,PCB_FLAGS(%r8)
105         jz      1f                              /* no, skip over */
106
107         /* Save userland %gs */
108         movl    %gs,PCB_GS(%r8)
109         movq    PCB_GS32P(%r8),%rax
110         movq    (%rax),%rax
111         movq    %rax,PCB_GS32SD(%r8)
112
113 1:
114         /* Test if debug registers should be saved. */
115         testl   $PCB_DBREGS,PCB_FLAGS(%r8)
116         jz      1f                              /* no, skip over */
117         movq    %dr7,%rax                       /* yes, do the save */
118         movq    %rax,PCB_DR7(%r8)
119         andq    $0x0000fc00, %rax               /* disable all watchpoints */
120         movq    %rax,%dr7
121         movq    %dr6,%rax
122         movq    %rax,PCB_DR6(%r8)
123         movq    %dr3,%rax
124         movq    %rax,PCB_DR3(%r8)
125         movq    %dr2,%rax
126         movq    %rax,PCB_DR2(%r8)
127         movq    %dr1,%rax
128         movq    %rax,PCB_DR1(%r8)
129         movq    %dr0,%rax
130         movq    %rax,PCB_DR0(%r8)
131 1:
132
133         /* have we used fp, and need a save? */
134         cmpq    %rdi,PCPU(FPCURTHREAD)
135         jne     1f
136         addq    $PCB_SAVEFPU,%r8
137         clts
138         fxsave  (%r8)
139         smsw    %ax
140         orb     $CR0_TS,%al
141         lmsw    %ax
142         xorl    %eax,%eax
143         movq    %rax,PCPU(FPCURTHREAD)
144 1:
145
146         /* Save is done.  Now fire up new thread. Leave old vmspace. */
147         movq    TD_PCB(%rsi),%r8
148
149         /* switch address space */
150         movq    PCB_CR3(%r8),%rdx
151         movq    %cr3,%rax
152         cmpq    %rdx,%rax                       /* Same address space? */
153         je      sw1
154         movq    %rdx,%cr3                       /* new address space */
155
156         movl    PCPU(CPUID), %eax
157         /* Release bit from old pmap->pm_active */
158         movq    TD_PROC(%rdi), %rdx             /* oldproc */
159         movq    P_VMSPACE(%rdx), %rdx
160         LK btrl %eax, VM_PMAP+PM_ACTIVE(%rdx)   /* clear old */
161
162         /* Set bit in new pmap->pm_active */
163         movq    TD_PROC(%rsi),%rdx              /* newproc */
164         movq    P_VMSPACE(%rdx), %rdx
165         LK btsl %eax, VM_PMAP+PM_ACTIVE(%rdx)   /* set new */
166
167 sw1:
168         /*
169          * At this point, we've switched address spaces and are ready
170          * to load up the rest of the next context.
171          */
172         movq    TD_PCB(%rsi),%r8
173
174         /* Restore userland %fs */
175         movl    $MSR_FSBASE,%ecx
176         movl    PCB_FSBASE(%r8),%eax
177         movl    PCB_FSBASE+4(%r8),%edx
178         wrmsr
179
180         /* Restore userland %gs */
181         movl    $MSR_KGSBASE,%ecx
182         movl    PCB_GSBASE(%r8),%eax
183         movl    PCB_GSBASE+4(%r8),%edx
184         wrmsr
185
186         /* Update the TSS_RSP0 pointer for the next interrupt */
187         movq    PCPU(TSSP), %rax
188         addq    $COMMON_TSS_RSP0, %rax
189         leaq    -16(%r8), %rbx
190         movq    %rbx, (%rax)
191         movq    %rbx, PCPU(RSP0)
192
193         movl    TD_TID(%rsi), %eax
194         movq    %r8, PCPU(CURPCB)
195         movl    %eax, PCPU(CURTID)
196         movq    %rsi, PCPU(CURTHREAD)           /* into next thread */
197
198         testl   $PCB_32BIT,PCB_FLAGS(%r8)
199         jz      1f                              /* no, skip over */
200
201         /* Restore userland %gs while preserving kernel gsbase */
202         movq    PCB_GS32P(%r8),%rax
203         movq    PCB_GS32SD(%r8),%rbx
204         movq    %rbx,(%rax)
205         movl    $MSR_GSBASE,%ecx
206         rdmsr
207         movl    PCB_GS(%r8),%gs
208         wrmsr
209
210 1:
211         /* Restore context. */
212         movq    PCB_RBX(%r8),%rbx
213         movq    PCB_RSP(%r8),%rsp
214         movq    PCB_RBP(%r8),%rbp
215         movq    PCB_R12(%r8),%r12
216         movq    PCB_R13(%r8),%r13
217         movq    PCB_R14(%r8),%r14
218         movq    PCB_R15(%r8),%r15
219         movq    PCB_RIP(%r8),%rax
220         movq    %rax,(%rsp)
221
222         /* Test if debug registers should be restored. */
223         testl   $PCB_DBREGS,PCB_FLAGS(%r8)
224         jz      1f
225         movq    PCB_DR6(%r8),%rax
226         movq    %rax,%dr6
227         movq    PCB_DR3(%r8),%rax
228         movq    %rax,%dr3
229         movq    PCB_DR2(%r8),%rax
230         movq    %rax,%dr2
231         movq    PCB_DR1(%r8),%rax
232         movq    %rax,%dr1
233         movq    PCB_DR0(%r8),%rax
234         movq    %rax,%dr0
235         /* But preserve reserved bits in %dr7 */
236         movq    %dr7,%rax
237         andq    $0x0000fc00,%rax
238         movq    PCB_DR7(%r8),%rcx
239         andq    $~0x0000fc00,%rcx
240         orq     %rcx,%rax
241         movq    %rax,%dr7
242 1:
243         ret
244
245 /*
246  * savectx(pcb)
247  * Update pcb, saving current processor state.
248  */
249 ENTRY(savectx)
250         /* Fetch PCB. */
251         movq    %rdi,%rcx
252
253         /* Save caller's return address. */
254         movq    (%rsp),%rax
255         movq    %rax,PCB_RIP(%rcx)
256
257         movq    %cr3,%rax
258         movq    %rax,PCB_CR3(%rcx)
259
260         movq    %rbx,PCB_RBX(%rcx)
261         movq    %rsp,PCB_RSP(%rcx)
262         movq    %rbp,PCB_RBP(%rcx)
263         movq    %r12,PCB_R12(%rcx)
264         movq    %r13,PCB_R13(%rcx)
265         movq    %r14,PCB_R14(%rcx)
266         movq    %r15,PCB_R15(%rcx)
267
268         /*
269          * If fpcurthread == NULL, then the fpu h/w state is irrelevant and the
270          * state had better already be in the pcb.  This is true for forks
271          * but not for dumps (the old book-keeping with FP flags in the pcb
272          * always lost for dumps because the dump pcb has 0 flags).
273          *
274          * If fpcurthread != NULL, then we have to save the fpu h/w state to
275          * fpcurthread's pcb and copy it to the requested pcb, or save to the
276          * requested pcb and reload.  Copying is easier because we would
277          * have to handle h/w bugs for reloading.  We used to lose the
278          * parent's fpu state for forks by forgetting to reload.
279          */
280         pushfq
281         cli
282         movq    PCPU(FPCURTHREAD),%rax
283         testq   %rax,%rax
284         je      1f
285
286         movq    TD_PCB(%rax),%rdi
287         leaq    PCB_SAVEFPU(%rdi),%rdi
288         clts
289         fxsave  (%rdi)
290         smsw    %ax
291         orb     $CR0_TS,%al
292         lmsw    %ax
293
294         movq    $PCB_SAVEFPU_SIZE,%rdx  /* arg 3 */
295         leaq    PCB_SAVEFPU(%rcx),%rsi  /* arg 2 */
296         /* arg 1 (%rdi) already loaded */
297         call    bcopy
298 1:
299         popfq
300
301         ret