]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/cpu_switch.S
This commit was generated by cvs2svn to compensate for changes in r100946,
[FreeBSD/FreeBSD.git] / sys / amd64 / amd64 / cpu_switch.S
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * William Jolitz.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $FreeBSD$
37  */
38
39 #include "opt_npx.h"
40
41 #include <machine/asmacros.h>
42
43 #ifdef SMP
44 #include <machine/apic.h>
45 #include <machine/smptests.h>                   /* CHEAP_TPR, GRAB_LOPRIO */
46 #endif
47
48 #include "assym.s"
49
50 /*****************************************************************************/
51 /* Scheduling                                                                */
52 /*****************************************************************************/
53
54         .data
55
56         .globl  panic
57
58 #ifdef SWTCH_OPTIM_STATS
59         .globl  swtch_optim_stats, tlb_flush_count
60 swtch_optim_stats:      .long   0               /* number of _swtch_optims */
61 tlb_flush_count:        .long   0
62 #endif
63
64         .text
65
66 /*
67  * cpu_throw()
68  *
69  * This is the second half of cpu_swtch(). It is used when the current
70  * thread is either a dummy or slated to die, and we no longer care
71  * about its state.
72  */
73 ENTRY(cpu_throw)
74         jmp     sw1
75
76 /*
77  * cpu_switch()
78  *
79  * Save the current thread state, then select the next thread to run
80  * and load its state.
81  */
82 ENTRY(cpu_switch)
83
84         /* Switch to new thread.  First, save context as needed. */
85         movl    PCPU(CURTHREAD),%ecx
86
87         /* If no thread to save, don't save it (XXX shouldn't happen). */
88         testl   %ecx,%ecx
89         jz      sw1
90
91         movl    TD_PROC(%ecx), %eax
92         movl    P_VMSPACE(%eax), %edx
93         movl    PCPU(CPUID), %eax
94         btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
95
96         movl    TD_PCB(%ecx),%edx
97
98         movl    (%esp),%eax                     /* Hardware registers */
99         movl    %eax,PCB_EIP(%edx)
100         movl    %ebx,PCB_EBX(%edx)
101         movl    %esp,PCB_ESP(%edx)
102         movl    %ebp,PCB_EBP(%edx)
103         movl    %esi,PCB_ESI(%edx)
104         movl    %edi,PCB_EDI(%edx)
105         movl    %gs,PCB_GS(%edx)
106         pushfl                                  /* PSL */
107         popl    PCB_PSL(%edx)
108
109         /* Test if debug registers should be saved. */
110         testl   $PCB_DBREGS,PCB_FLAGS(%edx)
111         jz      1f                              /* no, skip over */
112         movl    %dr7,%eax                       /* yes, do the save */
113         movl    %eax,PCB_DR7(%edx)
114         andl    $0x0000fc00, %eax               /* disable all watchpoints */
115         movl    %eax,%dr7
116         movl    %dr6,%eax
117         movl    %eax,PCB_DR6(%edx)
118         movl    %dr3,%eax
119         movl    %eax,PCB_DR3(%edx)
120         movl    %dr2,%eax
121         movl    %eax,PCB_DR2(%edx)
122         movl    %dr1,%eax
123         movl    %eax,PCB_DR1(%edx)
124         movl    %dr0,%eax
125         movl    %eax,PCB_DR0(%edx)
126 1:
127
128 #ifdef SMP
129         /* XXX FIXME: we should be saving the local APIC TPR */
130 #endif
131
132 #ifdef DEV_NPX
133         /* have we used fp, and need a save? */
134         cmpl    %ecx,PCPU(FPCURTHREAD)
135         jne     1f
136         addl    $PCB_SAVEFPU,%edx               /* h/w bugs make saving complicated */
137         pushl   %edx
138         call    npxsave                         /* do it in a big C function */
139         popl    %eax
140 1:
141 #endif
142
143         /* Save is done.  Now choose a new thread. */
144         /* XXX still trashing space above the old "Top Of Stack". */
145 sw1:
146
147 #ifdef SMP
148         /*
149          * Stop scheduling if smp_active has become zero (for rebooting) and
150          * we are not the BSP.
151          */
152         cmpl    $0,smp_active
153         jne     1f
154         cmpl    $0,PCPU(CPUID)
155         je      1f
156         movl    PCPU(IDLETHREAD), %eax
157         jmp     sw1b
158 1:
159 #endif
160
161         /*
162          * Choose a new thread to schedule.  choosethread() returns idlethread
163          * if it cannot find another thread to run.
164          */
165         call    choosethread                    /* Trash ecx, edx; ret eax. */
166
167 #ifdef INVARIANTS
168         testl   %eax,%eax                       /* no thread? */
169         jz      badsw3                          /* no, panic */
170 #endif
171
172 sw1b:
173         movl    %eax,%ecx
174         movl    TD_PCB(%ecx),%edx
175
176 #ifdef SWTCH_OPTIM_STATS
177         incl    swtch_optim_stats
178 #endif
179
180         /* switch address space */
181         movl    %cr3,%ebx                       /* The same address space? */
182         cmpl    PCB_CR3(%edx),%ebx
183         je      4f                              /* Yes, skip all that cruft */
184 #ifdef SWTCH_OPTIM_STATS
185         decl    swtch_optim_stats
186         incl    tlb_flush_count
187 #endif
188         movl    PCB_CR3(%edx),%ebx              /* Tell the CPU about the */
189         movl    %ebx,%cr3                       /* new address space */
190 4:
191
192         movl    PCPU(CPUID), %esi
193         cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
194         je      1f                              /* If not, use the default */
195         btsl    %esi, private_tss               /* mark use of private tss */
196         movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
197         jmp     2f                              /* Load it up */
198
199 1:      /*
200          * Use the common default TSS instead of our own.
201          * Set our stack pointer into the TSS, it's set to just
202          * below the PCB.  In C, common_tss.tss_esp0 = &pcb - 16;
203          */
204         leal    -16(%edx), %ebx                 /* leave space for vm86 */
205         movl    %ebx, PCPU(COMMON_TSS) + TSS_ESP0
206
207         /*
208          * Test this CPU's  bit in the bitmap to see if this
209          * CPU was using a private TSS.
210          */
211         btrl    %esi, private_tss               /* Already using the common? */
212         jae     3f                              /* if so, skip reloading */
213         PCPU_ADDR(COMMON_TSSD, %edi)
214 2:
215         /* Move correct tss descriptor into GDT slot, then reload tr. */
216         movl    PCPU(TSS_GDT), %ebx             /* entry in GDT */
217         movl    0(%edi), %eax
218         movl    %eax, 0(%ebx)
219         movl    4(%edi), %eax
220         movl    %eax, 4(%ebx)
221         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
222         ltr     %si
223 3:
224         /* Note in vmspace that this cpu is using it. */
225         movl    TD_PROC(%ecx),%eax
226         movl    P_VMSPACE(%eax), %ebx
227         movl    PCPU(CPUID), %eax
228         btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
229
230         /* Restore context. */
231         movl    PCB_EBX(%edx),%ebx
232         movl    PCB_ESP(%edx),%esp
233         movl    PCB_EBP(%edx),%ebp
234         movl    PCB_ESI(%edx),%esi
235         movl    PCB_EDI(%edx),%edi
236         movl    PCB_EIP(%edx),%eax
237         movl    %eax,(%esp)
238         pushl   PCB_PSL(%edx)
239         popfl
240
241 #if defined(SMP) && defined(GRAB_LOPRIO)
242         /* Hold LOPRIO for interrupts. */
243 #ifdef CHEAP_TPR
244         movl    $0, lapic+LA_TPR
245 #else
246         andl    $~APIC_TPR_PRIO, lapic+LA_TPR
247 #endif
248 #endif
249         movl    %edx, PCPU(CURPCB)
250         movl    %ecx, PCPU(CURTHREAD)           /* into next thread */
251
252 #ifdef SMP
253         /* XXX FIXME: we should be restoring the local APIC TPR */
254 #endif
255
256         /*
257          * Determine the LDT to use and load it if is the default one and
258          * that is not the current one.
259          */
260         movl    TD_PROC(%ecx),%eax
261         cmpl    $0,P_MD+MD_LDT(%eax)
262         jnz     1f
263         movl    _default_ldt,%eax
264         cmpl    PCPU(CURRENTLDT),%eax
265         je      2f
266         lldt    _default_ldt
267         movl    %eax,PCPU(CURRENTLDT)
268         jmp     2f
269 1:
270         /* Load the LDT when it is not the default one. */
271         pushl   %edx                            /* Preserve pointer to pcb. */
272         addl    $P_MD,%eax                      /* Pointer to mdproc is arg. */
273         pushl   %eax
274         call    set_user_ldt
275         addl    $4,%esp
276         popl    %edx
277 2:
278
279         /* This must be done after loading the user LDT. */
280         .globl  cpu_switch_load_gs
281 cpu_switch_load_gs:
282         movl    PCB_GS(%edx),%gs
283
284         /* Test if debug registers should be restored. */
285         testl   $PCB_DBREGS,PCB_FLAGS(%edx)
286         jz      1f
287
288         /*
289          * Restore debug registers.  The special code for dr7 is to
290          * preserve the current values of its reserved bits.
291          */
292         movl    PCB_DR6(%edx),%eax
293         movl    %eax,%dr6
294         movl    PCB_DR3(%edx),%eax
295         movl    %eax,%dr3
296         movl    PCB_DR2(%edx),%eax
297         movl    %eax,%dr2
298         movl    PCB_DR1(%edx),%eax
299         movl    %eax,%dr1
300         movl    PCB_DR0(%edx),%eax
301         movl    %eax,%dr0
302         movl    %dr7,%eax
303         andl    $0x0000fc00,%eax
304         movl    PCB_DR7(%edx),%ecx
305         andl    $~0x0000fc00,%ecx
306         orl     %ecx,%eax
307         movl    %eax,%dr7
308 1:
309         ret
310
311 #ifdef INVARIANTS
312 badsw3:
313         pushal
314         pushl   $sw0_3
315         call    panic
316
317 sw0_3:  .asciz  "cpu_switch: choosethread returned NULL"
318 #endif
319
320 /*
321  * savectx(pcb)
322  * Update pcb, saving current processor state.
323  */
324 ENTRY(savectx)
325         /* Fetch PCB. */
326         movl    4(%esp),%ecx
327
328         /* Save caller's return address.  Child won't execute this routine. */
329         movl    (%esp),%eax
330         movl    %eax,PCB_EIP(%ecx)
331
332         movl    %cr3,%eax
333         movl    %eax,PCB_CR3(%ecx)
334
335         movl    %ebx,PCB_EBX(%ecx)
336         movl    %esp,PCB_ESP(%ecx)
337         movl    %ebp,PCB_EBP(%ecx)
338         movl    %esi,PCB_ESI(%ecx)
339         movl    %edi,PCB_EDI(%ecx)
340         movl    %gs,PCB_GS(%ecx)
341         pushfl
342         popl    PCB_PSL(%ecx)
343
344 #ifdef DEV_NPX
345         /*
346          * If fpcurthread == NULL, then the npx h/w state is irrelevant and the
347          * state had better already be in the pcb.  This is true for forks
348          * but not for dumps (the old book-keeping with FP flags in the pcb
349          * always lost for dumps because the dump pcb has 0 flags).
350          *
351          * If fpcurthread != NULL, then we have to save the npx h/w state to
352          * fpcurthread's pcb and copy it to the requested pcb, or save to the
353          * requested pcb and reload.  Copying is easier because we would
354          * have to handle h/w bugs for reloading.  We used to lose the
355          * parent's npx state for forks by forgetting to reload.
356          */
357         pushfl
358         cli
359         movl    PCPU(FPCURTHREAD),%eax
360         testl   %eax,%eax
361         je      1f
362
363         pushl   %ecx
364         movl    TD_PCB(%eax),%eax
365         leal    PCB_SAVEFPU(%eax),%eax
366         pushl   %eax
367         pushl   %eax
368         call    npxsave
369         addl    $4,%esp
370         popl    %eax
371         popl    %ecx
372
373         pushl   $PCB_SAVEFPU_SIZE
374         leal    PCB_SAVEFPU(%ecx),%ecx
375         pushl   %ecx
376         pushl   %eax
377         call    bcopy
378         addl    $12,%esp
379 1:
380         popfl
381 #endif  /* DEV_NPX */
382
383         ret