]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/cpu_switch.S
This commit was generated by cvs2svn to compensate for changes in r98681,
[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 #if defined(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 ENTRY(cpu_throw)
70         jmp     sw1
71
72 /*
73  * cpu_switch()
74  */
75 ENTRY(cpu_switch)
76
77         /* Switch to new thread.  First, save context as needed. */
78         movl    PCPU(CURTHREAD),%ecx
79
80         /* If no thread to save, don't save it (XXX shouldn't happen). */
81         testl   %ecx,%ecx
82         jz      sw1
83
84         movl    TD_PROC(%ecx), %eax
85         movl    P_VMSPACE(%eax), %edx
86         movl    PCPU(CPUID), %eax
87         btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
88
89         movl    TD_PCB(%ecx),%edx
90
91         movl    (%esp),%eax                     /* Hardware registers */
92         movl    %eax,PCB_EIP(%edx)
93         movl    %ebx,PCB_EBX(%edx)
94         movl    %esp,PCB_ESP(%edx)
95         movl    %ebp,PCB_EBP(%edx)
96         movl    %esi,PCB_ESI(%edx)
97         movl    %edi,PCB_EDI(%edx)
98         movl    %gs,PCB_GS(%edx)
99         pushfl                                  /* PSL */
100         popl    PCB_PSL(%edx)
101
102         /* Test if debug registers should be saved. */
103         testl   $PCB_DBREGS,PCB_FLAGS(%edx)
104         jz      1f                              /* no, skip over */
105         movl    %dr7,%eax                       /* yes, do the save */
106         movl    %eax,PCB_DR7(%edx)
107         andl    $0x0000fc00, %eax               /* disable all watchpoints */
108         movl    %eax,%dr7
109         movl    %dr6,%eax
110         movl    %eax,PCB_DR6(%edx)
111         movl    %dr3,%eax
112         movl    %eax,PCB_DR3(%edx)
113         movl    %dr2,%eax
114         movl    %eax,PCB_DR2(%edx)
115         movl    %dr1,%eax
116         movl    %eax,PCB_DR1(%edx)
117         movl    %dr0,%eax
118         movl    %eax,PCB_DR0(%edx)
119 1:
120
121 #ifdef SMP
122         /* XXX FIXME: we should be saving the local APIC TPR */
123 #endif
124
125 #ifdef DEV_NPX
126         /* have we used fp, and need a save? */
127         cmpl    %ecx,PCPU(FPCURTHREAD)
128         jne     1f
129         addl    $PCB_SAVEFPU,%edx               /* h/w bugs make saving complicated */
130         pushl   %edx
131         call    npxsave                         /* do it in a big C function */
132         popl    %eax
133 1:
134 #endif
135
136         /* Save is done.  Now choose a new thread. */
137         /* XXX still trashing space above the old "Top Of Stack". */
138 sw1:
139
140 #ifdef SMP
141         /*
142          * Stop scheduling if smp_active has become zero (for rebooting) and
143          * we are not the BSP.
144          */
145         cmpl    $0,smp_active
146         jne     1f
147         cmpl    $0,PCPU(CPUID)
148         je      1f
149         movl    PCPU(IDLETHREAD), %eax
150         jmp     sw1b
151 1:
152 #endif
153
154         /*
155          * Choose a new thread to schedule.  choosethread() returns idlethread
156          * if it cannot find another thread to run.
157          */
158         call    choosethread                    /* Trash ecx, edx; ret eax. */
159
160 #ifdef INVARIANTS
161         testl   %eax,%eax                       /* no thread? */
162         jz      badsw3                          /* no, panic */
163 #endif
164
165 sw1b:
166         movl    %eax,%ecx
167
168 #ifdef  INVARIANTS
169         movl    TD_PROC(%ecx), %eax             /* XXXKSE */
170         cmpb    $SRUN,P_STAT(%eax)
171         jne     badsw2
172 #endif
173
174         movl    TD_PCB(%ecx),%edx
175
176 #if defined(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 #if defined(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 badsw2:
313         pushl   $sw0_2
314         call    panic
315
316 sw0_2:  .asciz  "cpu_switch: not TDS_RUNQ"
317
318 badsw3:
319         pushl   $sw0_3
320         call    panic
321
322 sw0_3:  .asciz  "cpu_switch: choosethread returned NULL"
323 #endif
324
325 /*
326  * savectx(pcb)
327  * Update pcb, saving current processor state.
328  */
329 ENTRY(savectx)
330         /* Fetch PCB. */
331         movl    4(%esp),%ecx
332
333         /* Save caller's return address.  Child won't execute this routine. */
334         movl    (%esp),%eax
335         movl    %eax,PCB_EIP(%ecx)
336
337         movl    %cr3,%eax
338         movl    %eax,PCB_CR3(%ecx)
339
340         movl    %ebx,PCB_EBX(%ecx)
341         movl    %esp,PCB_ESP(%ecx)
342         movl    %ebp,PCB_EBP(%ecx)
343         movl    %esi,PCB_ESI(%ecx)
344         movl    %edi,PCB_EDI(%ecx)
345         movl    %gs,PCB_GS(%ecx)
346         pushfl
347         popl    PCB_PSL(%ecx)
348
349 #ifdef DEV_NPX
350         /*
351          * If fpcurthread == NULL, then the npx h/w state is irrelevant and the
352          * state had better already be in the pcb.  This is true for forks
353          * but not for dumps (the old book-keeping with FP flags in the pcb
354          * always lost for dumps because the dump pcb has 0 flags).
355          *
356          * If fpcurthread != NULL, then we have to save the npx h/w state to
357          * fpcurthread's pcb and copy it to the requested pcb, or save to the
358          * requested pcb and reload.  Copying is easier because we would
359          * have to handle h/w bugs for reloading.  We used to lose the
360          * parent's npx state for forks by forgetting to reload.
361          */
362         pushfl
363         cli
364         movl    PCPU(FPCURTHREAD),%eax
365         testl   %eax,%eax
366         je      1f
367
368         pushl   %ecx
369         movl    TD_PCB(%eax),%eax
370         leal    PCB_SAVEFPU(%eax),%eax
371         pushl   %eax
372         pushl   %eax
373         call    npxsave
374         addl    $4,%esp
375         popl    %eax
376         popl    %ecx
377
378         pushl   $PCB_SAVEFPU_SIZE
379         leal    PCB_SAVEFPU(%ecx),%ecx
380         pushl   %ecx
381         pushl   %eax
382         call    bcopy
383         addl    $12,%esp
384 1:
385         popfl
386 #endif  /* DEV_NPX */
387
388         ret