]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/cpu_switch.S
- Heavyweight interrupt threads on the alpha for device I/O interrupts.
[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 "npx.h"
40 #include "opt_user_ldt.h"
41
42 #include <sys/rtprio.h>
43
44 #include <machine/asmacros.h>
45
46 #ifdef SMP
47 #include <machine/pmap.h>
48 #include <machine/apic.h>
49 #include <machine/smptests.h>           /** GRAB_LOPRIO */
50 #include <machine/lock.h>
51 #endif /* SMP */
52
53 #include "assym.s"
54
55
56 /*****************************************************************************/
57 /* Scheduling                                                                */
58 /*****************************************************************************/
59
60         .data
61
62         .globl  _panic
63
64 #if defined(SWTCH_OPTIM_STATS)
65         .globl  _swtch_optim_stats, _tlb_flush_count
66 _swtch_optim_stats:     .long   0               /* number of _swtch_optims */
67 _tlb_flush_count:       .long   0
68 #endif
69
70         .text
71
72 /*
73  * cpu_throw()
74  */
75 ENTRY(cpu_throw)
76         jmp     sw1
77
78 /*
79  * cpu_switch()
80  */
81 ENTRY(cpu_switch)
82         
83         /* switch to new process. first, save context as needed */
84         movl    _curproc,%ecx
85         movl    %ecx,_prevproc
86
87         /* if no process to save, don't bother */
88         testl   %ecx,%ecx
89         jz      sw1
90
91 #ifdef SMP
92         movb    P_ONCPU(%ecx), %al              /* save "last" cpu */
93         movb    %al, P_LASTCPU(%ecx)
94         movb    $0xff, P_ONCPU(%ecx)            /* "leave" the cpu */
95 #endif /* SMP */
96         movl    P_VMSPACE(%ecx), %edx
97 #ifdef SMP
98         movl    _cpuid, %eax
99 #else
100         xorl    %eax, %eax
101 #endif /* SMP */
102         btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
103
104         movl    P_ADDR(%ecx),%edx
105
106         movl    (%esp),%eax                     /* Hardware registers */
107         movl    %eax,PCB_EIP(%edx)
108         movl    %ebx,PCB_EBX(%edx)
109         movl    %esp,PCB_ESP(%edx)
110         movl    %ebp,PCB_EBP(%edx)
111         movl    %esi,PCB_ESI(%edx)
112         movl    %edi,PCB_EDI(%edx)
113         movl    %gs,PCB_GS(%edx)
114
115         /* test if debug registers should be saved */
116         movb    PCB_FLAGS(%edx),%al
117         andb    $PCB_DBREGS,%al
118         jz      1f                              /* no, skip over */
119         movl    %dr7,%eax                       /* yes, do the save */
120         movl    %eax,PCB_DR7(%edx)
121         andl    $0x0000ff00, %eax               /* disable all watchpoints */
122         movl    %eax,%dr7
123         movl    %dr6,%eax
124         movl    %eax,PCB_DR6(%edx)
125         movl    %dr3,%eax
126         movl    %eax,PCB_DR3(%edx)
127         movl    %dr2,%eax
128         movl    %eax,PCB_DR2(%edx)
129         movl    %dr1,%eax
130         movl    %eax,PCB_DR1(%edx)
131         movl    %dr0,%eax
132         movl    %eax,PCB_DR0(%edx)
133 1:
134  
135         /* save sched_lock recursion count */
136         movl    _sched_lock+MTX_RECURSE,%eax
137         movl    %eax,PCB_SCHEDNEST(%edx)
138  
139 #ifdef SMP
140         /* XXX FIXME: we should be saving the local APIC TPR */
141 #endif /* SMP */
142
143 #if NNPX > 0
144         /* have we used fp, and need a save? */
145         cmpl    %ecx,_npxproc
146         jne     1f
147         addl    $PCB_SAVEFPU,%edx               /* h/w bugs make saving complicated */
148         pushl   %edx
149         call    _npxsave                        /* do it in a big C function */
150         popl    %eax
151 1:
152 #endif  /* NNPX > 0 */
153
154         /* save is done, now choose a new process */
155 sw1:
156
157 #ifdef SMP
158         /* Stop scheduling if smp_active goes zero and we are not BSP */
159         cmpl    $0,_smp_active
160         jne     1f
161         cmpl    $0,_cpuid
162         je      1f
163
164         movl    _idleproc, %eax
165         jmp     sw1b
166 1:
167 #endif
168
169         /*
170          * Choose a new process to schedule.  chooseproc() returns idleproc
171          * if it cannot find another process to run.
172          */
173 sw1a:
174         call    _chooseproc                     /* trash ecx, edx, ret eax*/
175
176 #ifdef DIAGNOSTIC
177         testl   %eax,%eax                       /* no process? */
178         jz      badsw3                          /* no, panic */
179 #endif
180 sw1b:
181         movl    %eax,%ecx
182
183         xorl    %eax,%eax
184         andl    $~AST_RESCHED,_astpending
185
186 #ifdef  DIAGNOSTIC
187         cmpl    %eax,P_WCHAN(%ecx)
188         jne     badsw1
189         cmpb    $SRUN,P_STAT(%ecx)
190         jne     badsw2
191 #endif
192
193         movl    P_ADDR(%ecx),%edx
194
195 #if defined(SWTCH_OPTIM_STATS)
196         incl    _swtch_optim_stats
197 #endif
198         /* switch address space */
199         movl    %cr3,%ebx
200         cmpl    PCB_CR3(%edx),%ebx
201         je      4f
202 #if defined(SWTCH_OPTIM_STATS)
203         decl    _swtch_optim_stats
204         incl    _tlb_flush_count
205 #endif
206         movl    PCB_CR3(%edx),%ebx
207         movl    %ebx,%cr3
208 4:
209
210 #ifdef SMP
211         movl    _cpuid, %esi
212 #else
213         xorl    %esi, %esi
214 #endif
215         cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
216         je      1f
217         btsl    %esi, _private_tss              /* mark use of private tss */
218         movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
219         jmp     2f
220 1:
221
222         /* update common_tss.tss_esp0 pointer */
223         movl    %edx, %ebx                      /* pcb */
224         addl    $(UPAGES * PAGE_SIZE - 16), %ebx
225         movl    %ebx, _common_tss + TSS_ESP0
226
227         btrl    %esi, _private_tss
228         jae     3f
229 #ifdef SMP
230         movl    $gd_common_tssd, %edi
231         addl    %fs:0, %edi
232 #else
233         movl    $_common_tssd, %edi
234 #endif
235 2:
236         /* move correct tss descriptor into GDT slot, then reload tr */
237         movl    _tss_gdt, %ebx                  /* entry in GDT */
238         movl    0(%edi), %eax
239         movl    %eax, 0(%ebx)
240         movl    4(%edi), %eax
241         movl    %eax, 4(%ebx)
242         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
243         ltr     %si
244 3:
245         movl    P_VMSPACE(%ecx), %ebx
246 #ifdef SMP
247         movl    _cpuid, %eax
248 #else
249         xorl    %eax, %eax
250 #endif
251         btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
252
253         /* restore context */
254         movl    PCB_EBX(%edx),%ebx
255         movl    PCB_ESP(%edx),%esp
256         movl    PCB_EBP(%edx),%ebp
257         movl    PCB_ESI(%edx),%esi
258         movl    PCB_EDI(%edx),%edi
259         movl    PCB_EIP(%edx),%eax
260         movl    %eax,(%esp)
261
262 #ifdef SMP
263 #ifdef GRAB_LOPRIO                              /* hold LOPRIO for INTs */
264 #ifdef CHEAP_TPR
265         movl    $0, lapic_tpr
266 #else
267         andl    $~APIC_TPR_PRIO, lapic_tpr
268 #endif /** CHEAP_TPR */
269 #endif /** GRAB_LOPRIO */
270         movl    _cpuid,%eax
271         movb    %al, P_ONCPU(%ecx)
272 #endif /* SMP */
273         movl    %edx, _curpcb
274         movl    %ecx, _curproc                  /* into next process */
275
276 #ifdef SMP
277         /* XXX FIXME: we should be restoring the local APIC TPR */
278 #endif /* SMP */
279
280 #ifdef  USER_LDT
281         cmpl    $0, PCB_USERLDT(%edx)
282         jnz     1f
283         movl    __default_ldt,%eax
284         cmpl    _currentldt,%eax
285         je      2f
286         lldt    __default_ldt
287         movl    %eax,_currentldt
288         jmp     2f
289 1:      pushl   %edx
290         call    _set_user_ldt
291         popl    %edx
292 2:
293 #endif
294
295         /* This must be done after loading the user LDT. */
296         .globl  cpu_switch_load_gs
297 cpu_switch_load_gs:
298         movl    PCB_GS(%edx),%gs
299
300         /* test if debug regisers should be restored */
301         movb    PCB_FLAGS(%edx),%al
302         andb    $PCB_DBREGS,%al
303         jz      1f                              /* no, skip over */
304         movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
305         movl    %eax,%dr6
306         movl    PCB_DR3(%edx),%eax
307         movl    %eax,%dr3
308         movl    PCB_DR2(%edx),%eax
309         movl    %eax,%dr2
310         movl    PCB_DR1(%edx),%eax
311         movl    %eax,%dr1
312         movl    PCB_DR0(%edx),%eax
313         movl    %eax,%dr0
314         movl    PCB_DR7(%edx),%eax
315         movl    %eax,%dr7
316 1:
317
318         /*
319          * restore sched_lock recursion count and transfer ownership to
320          * new process
321          */
322         movl    PCB_SCHEDNEST(%edx),%eax
323         movl    %eax,_sched_lock+MTX_RECURSE
324
325         movl    _curproc,%eax
326         movl    %eax,_sched_lock+MTX_LOCK
327
328 #ifdef DIAGNOSTIC
329         pushfl
330         popl    %ecx
331         testl   $0x200, %ecx                    /* interrupts enabled? */
332         jnz     badsw6                          /* that way madness lies */
333 #endif
334         ret
335
336 CROSSJUMPTARGET(sw1a)
337
338 #ifdef DIAGNOSTIC
339 badsw1:
340         pushl   $sw0_1
341         call    _panic
342
343 sw0_1:  .asciz  "cpu_switch: has wchan"
344
345 badsw2:
346         pushl   $sw0_2
347         call    _panic
348
349 sw0_2:  .asciz  "cpu_switch: not SRUN"
350
351 badsw3:
352         pushl   $sw0_3
353         call    _panic
354
355 sw0_3:  .asciz  "cpu_switch: chooseproc returned NULL"
356
357 #endif
358
359 #ifdef DIAGNOSTIC
360 badsw5:
361         pushl   $sw0_5
362         call    _panic
363
364 sw0_5:  .asciz  "cpu_switch: interrupts enabled (again)"
365 badsw6:
366         pushl   $sw0_6
367         call    _panic
368
369 sw0_6:  .asciz  "cpu_switch: interrupts enabled"
370 #endif
371
372 /*
373  * savectx(pcb)
374  * Update pcb, saving current processor state.
375  */
376 ENTRY(savectx)
377         /* fetch PCB */
378         movl    4(%esp),%ecx
379
380         /* caller's return address - child won't execute this routine */
381         movl    (%esp),%eax
382         movl    %eax,PCB_EIP(%ecx)
383
384         movl    %ebx,PCB_EBX(%ecx)
385         movl    %esp,PCB_ESP(%ecx)
386         movl    %ebp,PCB_EBP(%ecx)
387         movl    %esi,PCB_ESI(%ecx)
388         movl    %edi,PCB_EDI(%ecx)
389         movl    %gs,PCB_GS(%ecx)
390
391 #if NNPX > 0
392         /*
393          * If npxproc == NULL, then the npx h/w state is irrelevant and the
394          * state had better already be in the pcb.  This is true for forks
395          * but not for dumps (the old book-keeping with FP flags in the pcb
396          * always lost for dumps because the dump pcb has 0 flags).
397          *
398          * If npxproc != NULL, then we have to save the npx h/w state to
399          * npxproc's pcb and copy it to the requested pcb, or save to the
400          * requested pcb and reload.  Copying is easier because we would
401          * have to handle h/w bugs for reloading.  We used to lose the
402          * parent's npx state for forks by forgetting to reload.
403          */
404         movl    _npxproc,%eax
405         testl   %eax,%eax
406         je      1f
407
408         pushl   %ecx
409         movl    P_ADDR(%eax),%eax
410         leal    PCB_SAVEFPU(%eax),%eax
411         pushl   %eax
412         pushl   %eax
413         call    _npxsave
414         addl    $4,%esp
415         popl    %eax
416         popl    %ecx
417
418         pushl   $PCB_SAVEFPU_SIZE
419         leal    PCB_SAVEFPU(%ecx),%ecx
420         pushl   %ecx
421         pushl   %eax
422         call    _bcopy
423         addl    $12,%esp
424 #endif  /* NNPX > 0 */
425
426 1:
427         ret