]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/swtch.s
This commit was generated by cvs2svn to compensate for changes in r53660,
[FreeBSD/FreeBSD.git] / sys / amd64 / amd64 / swtch.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/ipl.h>
51 #include <machine/lock.h>
52 #endif /* SMP */
53
54 #include "assym.s"
55
56
57 /*****************************************************************************/
58 /* Scheduling                                                                */
59 /*****************************************************************************/
60
61         .data
62
63         .globl  _hlt_vector
64 _hlt_vector:    .long   _default_halt   /* pointer to halt routine */
65
66         .globl  _panic
67
68         .globl  _want_resched
69 _want_resched:  .long   0               /* we need to re-run the scheduler */
70 #if defined(SWTCH_OPTIM_STATS)
71         .globl  _swtch_optim_stats, _tlb_flush_count
72 _swtch_optim_stats:     .long   0               /* number of _swtch_optims */
73 _tlb_flush_count:       .long   0
74 #endif
75
76         .text
77
78 /*
79  * When no processes are on the runq, cpu_switch() branches to _idle
80  * to wait for something to come ready.
81  */
82         ALIGN_TEXT
83         .type   _idle,@function
84 _idle:
85         xorl    %ebp,%ebp
86         movl    %ebp,_switchtime
87
88 #ifdef SMP
89
90         /* when called, we have the mplock, intr disabled */
91         /* use our idleproc's "context" */
92         movl    _IdlePTD, %ecx
93         movl    %cr3, %eax
94         cmpl    %ecx, %eax
95         je              2f
96 #if defined(SWTCH_OPTIM_STATS)
97         decl    _swtch_optim_stats
98         incl    _tlb_flush_count
99 #endif
100         movl    %ecx, %cr3
101 2:
102         /* Keep space for nonexisting return addr, or profiling bombs */
103         movl    $gd_idlestack_top-4, %ecx       
104         addl    %fs:0, %ecx
105         movl    %ecx, %esp
106
107         /* update common_tss.tss_esp0 pointer */
108         movl    %ecx, _common_tss + TSS_ESP0
109
110         movl    _cpuid, %esi
111         btrl    %esi, _private_tss
112         jae     1f
113
114         movl    $gd_common_tssd, %edi
115         addl    %fs:0, %edi
116
117         /* move correct tss descriptor into GDT slot, then reload tr */
118         movl    _tss_gdt, %ebx                  /* entry in GDT */
119         movl    0(%edi), %eax
120         movl    %eax, 0(%ebx)
121         movl    4(%edi), %eax
122         movl    %eax, 4(%ebx)
123         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
124         ltr     %si
125 1:
126
127         sti
128
129         /*
130          * XXX callers of cpu_switch() do a bogus splclock().  Locking should
131          * be left to cpu_switch().
132          */
133         call    _spl0
134
135         cli
136
137         /*
138          * _REALLY_ free the lock, no matter how deep the prior nesting.
139          * We will recover the nesting on the way out when we have a new
140          * proc to load.
141          *
142          * XXX: we had damn well better be sure we had it before doing this!
143          */
144         movl    $FREE_LOCK, %eax
145         movl    %eax, _mp_lock
146
147         /* do NOT have lock, intrs disabled */
148         .globl  idle_loop
149 idle_loop:
150
151         cmpl    $0,_smp_active
152         jne     1f
153         cmpl    $0,_cpuid
154         je      1f
155         jmp     2f
156
157 1:
158         call    _procrunnable
159         testl   %eax,%eax
160         jnz     3f
161
162         cmpl    $0,_do_page_zero_idle
163         je      2f
164
165         /* XXX appears to cause panics */
166         /*
167          * Inside zero_idle we enable interrupts and grab the mplock
168          * as needed.  It needs to be careful about entry/exit mutexes.
169          */
170         call    _vm_page_zero_idle              /* internal locking */
171         testl   %eax, %eax
172         jnz     idle_loop
173 2:
174
175         /* enable intrs for a halt */
176         movl    $0, lapic_tpr                   /* 1st candidate for an INT */
177         sti
178         call    *_hlt_vector                    /* wait for interrupt */
179         cli
180         jmp     idle_loop
181
182 3:
183         movl    $LOPRIO_LEVEL, lapic_tpr        /* arbitrate for INTs */
184         call    _get_mplock
185         call    _procrunnable
186         testl   %eax,%eax
187         CROSSJUMP(jnz, sw1a, jz)
188         call    _rel_mplock
189         jmp     idle_loop
190
191 #else /* !SMP */
192
193         movl    $HIDENAME(tmpstk),%esp
194 #if defined(OVERLY_CONSERVATIVE_PTD_MGMT)
195 #if defined(SWTCH_OPTIM_STATS)
196         incl    _swtch_optim_stats
197 #endif
198         movl    _IdlePTD, %ecx
199         movl    %cr3, %eax
200         cmpl    %ecx, %eax
201         je              2f
202 #if defined(SWTCH_OPTIM_STATS)
203         decl    _swtch_optim_stats
204         incl    _tlb_flush_count
205 #endif
206         movl    %ecx, %cr3
207 2:
208 #endif
209
210         /* update common_tss.tss_esp0 pointer */
211         movl    %esp, _common_tss + TSS_ESP0
212
213         movl    $0, %esi
214         btrl    %esi, _private_tss
215         jae     1f
216
217         movl    $_common_tssd, %edi
218
219         /* move correct tss descriptor into GDT slot, then reload tr */
220         movl    _tss_gdt, %ebx                  /* entry in GDT */
221         movl    0(%edi), %eax
222         movl    %eax, 0(%ebx)
223         movl    4(%edi), %eax
224         movl    %eax, 4(%ebx)
225         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
226         ltr     %si
227 1:
228
229         sti
230
231         /*
232          * XXX callers of cpu_switch() do a bogus splclock().  Locking should
233          * be left to cpu_switch().
234          */
235         call    _spl0
236
237         ALIGN_TEXT
238 idle_loop:
239         cli
240         call    _procrunnable
241         testl   %eax,%eax
242         CROSSJUMP(jnz, sw1a, jz)
243         call    _vm_page_zero_idle
244         testl   %eax, %eax
245         jnz     idle_loop
246         sti
247         call    *_hlt_vector                    /* wait for interrupt */
248         jmp     idle_loop
249
250 #endif /* SMP */
251
252 CROSSJUMPTARGET(_idle)
253
254 ENTRY(default_halt)
255 #ifndef SMP
256         hlt                                     /* XXX:  until a wakeup IPI */
257 #endif
258         ret
259
260 /*
261  * cpu_switch()
262  */
263 ENTRY(cpu_switch)
264         
265         /* switch to new process. first, save context as needed */
266         movl    _curproc,%ecx
267
268         /* if no process to save, don't bother */
269         testl   %ecx,%ecx
270         je      sw1
271
272 #ifdef SMP
273         movb    P_ONCPU(%ecx), %al              /* save "last" cpu */
274         movb    %al, P_LASTCPU(%ecx)
275         movb    $0xff, P_ONCPU(%ecx)            /* "leave" the cpu */
276 #endif /* SMP */
277         movl    P_VMSPACE(%ecx), %edx
278 #ifdef SMP
279         movl    _cpuid, %eax
280 #else
281         xorl    %eax, %eax
282 #endif /* SMP */
283         btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
284
285         movl    P_ADDR(%ecx),%edx
286
287         movl    (%esp),%eax                     /* Hardware registers */
288         movl    %eax,PCB_EIP(%edx)
289         movl    %ebx,PCB_EBX(%edx)
290         movl    %esp,PCB_ESP(%edx)
291         movl    %ebp,PCB_EBP(%edx)
292         movl    %esi,PCB_ESI(%edx)
293         movl    %edi,PCB_EDI(%edx)
294         movl    %gs,PCB_GS(%edx)
295
296         /* test if debug regisers should be saved */
297         movb    PCB_FLAGS(%edx),%al
298         andb    $PCB_DBREGS,%al
299         jz      1f                              /* no, skip over */
300         movl    %dr7,%eax                       /* yes, do the save */
301         movl    %eax,PCB_DR7(%edx)
302         andl    $0x0000ff00, %eax               /* disable all watchpoints */
303         movl    %eax,%dr7
304         movl    %dr6,%eax
305         movl    %eax,PCB_DR6(%edx)
306         movl    %dr3,%eax
307         movl    %eax,PCB_DR3(%edx)
308         movl    %dr2,%eax
309         movl    %eax,PCB_DR2(%edx)
310         movl    %dr1,%eax
311         movl    %eax,PCB_DR1(%edx)
312         movl    %dr0,%eax
313         movl    %eax,PCB_DR0(%edx)
314 1:
315  
316 #ifdef SMP
317         movl    _mp_lock, %eax
318         /* XXX FIXME: we should be saving the local APIC TPR */
319 #ifdef DIAGNOSTIC
320         cmpl    $FREE_LOCK, %eax                /* is it free? */
321         je      badsw4                          /* yes, bad medicine! */
322 #endif /* DIAGNOSTIC */
323         andl    $COUNT_FIELD, %eax              /* clear CPU portion */
324         movl    %eax, PCB_MPNEST(%edx)          /* store it */
325 #endif /* SMP */
326
327 #if NNPX > 0
328         /* have we used fp, and need a save? */
329         cmpl    %ecx,_npxproc
330         jne     1f
331         addl    $PCB_SAVEFPU,%edx               /* h/w bugs make saving complicated */
332         pushl   %edx
333         call    _npxsave                        /* do it in a big C function */
334         popl    %eax
335 1:
336 #endif  /* NNPX > 0 */
337
338         movl    $0,_curproc                     /* out of process */
339
340         /* save is done, now choose a new process or idle */
341 sw1:
342         cli
343
344 #ifdef SMP
345         /* Stop scheduling if smp_active goes zero and we are not BSP */
346         cmpl    $0,_smp_active
347         jne     1f
348         cmpl    $0,_cpuid
349         CROSSJUMP(je, _idle, jne)               /* wind down */
350 1:
351 #endif
352
353 sw1a:
354         call    _chooseproc                     /* trash ecx, edx, ret eax*/
355         testl   %eax,%eax
356         CROSSJUMP(je, _idle, jne)               /* if no proc, idle */
357         movl    %eax,%ecx
358
359         movl    $0,%eax
360         movl    %eax,_want_resched
361
362 #ifdef  DIAGNOSTIC
363         cmpl    %eax,P_WCHAN(%ecx)
364         jne     badsw1
365         cmpb    $SRUN,P_STAT(%ecx)
366         jne     badsw2
367 #endif
368
369         movl    P_ADDR(%ecx),%edx
370
371 #if defined(SWTCH_OPTIM_STATS)
372         incl    _swtch_optim_stats
373 #endif
374         /* switch address space */
375         movl    %cr3,%ebx
376         cmpl    PCB_CR3(%edx),%ebx
377         je      4f
378 #if defined(SWTCH_OPTIM_STATS)
379         decl    _swtch_optim_stats
380         incl    _tlb_flush_count
381 #endif
382         movl    PCB_CR3(%edx),%ebx
383         movl    %ebx,%cr3
384 4:
385
386 #ifdef SMP
387         movl    _cpuid, %esi
388 #else
389         xorl    %esi, %esi
390 #endif
391         cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
392         je      1f
393         btsl    %esi, _private_tss              /* mark use of private tss */
394         movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
395         jmp     2f
396 1:
397
398         /* update common_tss.tss_esp0 pointer */
399         movl    %edx, %ebx                      /* pcb */
400         addl    $(UPAGES * PAGE_SIZE - 16), %ebx
401         movl    %ebx, _common_tss + TSS_ESP0
402
403         btrl    %esi, _private_tss
404         jae     3f
405 #ifdef SMP
406         movl    $gd_common_tssd, %edi
407         addl    %fs:0, %edi
408 #else
409         movl    $_common_tssd, %edi
410 #endif
411 2:
412         /* move correct tss descriptor into GDT slot, then reload tr */
413         movl    _tss_gdt, %ebx                  /* entry in GDT */
414         movl    0(%edi), %eax
415         movl    %eax, 0(%ebx)
416         movl    4(%edi), %eax
417         movl    %eax, 4(%ebx)
418         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
419         ltr     %si
420 3:
421         movl    P_VMSPACE(%ecx), %ebx
422 #ifdef SMP
423         movl    _cpuid, %eax
424 #else
425         xorl    %eax, %eax
426 #endif
427         btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
428
429         /* restore context */
430         movl    PCB_EBX(%edx),%ebx
431         movl    PCB_ESP(%edx),%esp
432         movl    PCB_EBP(%edx),%ebp
433         movl    PCB_ESI(%edx),%esi
434         movl    PCB_EDI(%edx),%edi
435         movl    PCB_EIP(%edx),%eax
436         movl    %eax,(%esp)
437
438 #ifdef SMP
439 #ifdef GRAB_LOPRIO                              /* hold LOPRIO for INTs */
440 #ifdef CHEAP_TPR
441         movl    $0, lapic_tpr
442 #else
443         andl    $~APIC_TPR_PRIO, lapic_tpr
444 #endif /** CHEAP_TPR */
445 #endif /** GRAB_LOPRIO */
446         movl    _cpuid,%eax
447         movb    %al, P_ONCPU(%ecx)
448 #endif /* SMP */
449         movl    %edx, _curpcb
450         movl    %ecx, _curproc                  /* into next process */
451
452 #ifdef SMP
453         movl    _cpu_lockid, %eax
454         orl     PCB_MPNEST(%edx), %eax          /* add next count from PROC */
455         movl    %eax, _mp_lock                  /* load the mp_lock */
456         /* XXX FIXME: we should be restoring the local APIC TPR */
457 #endif /* SMP */
458
459 #ifdef  USER_LDT
460         cmpl    $0, PCB_USERLDT(%edx)
461         jnz     1f
462         movl    __default_ldt,%eax
463         cmpl    _currentldt,%eax
464         je      2f
465         lldt    __default_ldt
466         movl    %eax,_currentldt
467         jmp     2f
468 1:      pushl   %edx
469         call    _set_user_ldt
470         popl    %edx
471 2:
472 #endif
473
474         /* This must be done after loading the user LDT. */
475         .globl  cpu_switch_load_gs
476 cpu_switch_load_gs:
477         movl    PCB_GS(%edx),%gs
478
479         /* test if debug regisers should be restored */
480         movb    PCB_FLAGS(%edx),%al
481         andb    $PCB_DBREGS,%al
482         jz      1f                              /* no, skip over */
483         movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
484         movl    %eax,%dr6
485         movl    PCB_DR3(%edx),%eax
486         movl    %eax,%dr3
487         movl    PCB_DR2(%edx),%eax
488         movl    %eax,%dr2
489         movl    PCB_DR1(%edx),%eax
490         movl    %eax,%dr1
491         movl    PCB_DR0(%edx),%eax
492         movl    %eax,%dr0
493         movl    PCB_DR7(%edx),%eax
494         movl    %eax,%dr7
495 1:
496
497         sti
498         ret
499
500 CROSSJUMPTARGET(sw1a)
501
502 #ifdef DIAGNOSTIC
503 badsw1:
504         pushl   $sw0_1
505         call    _panic
506
507 sw0_1:  .asciz  "cpu_switch: has wchan"
508
509 badsw2:
510         pushl   $sw0_2
511         call    _panic
512
513 sw0_2:  .asciz  "cpu_switch: not SRUN"
514 #endif
515
516 #if defined(SMP) && defined(DIAGNOSTIC)
517 badsw4:
518         pushl   $sw0_4
519         call    _panic
520
521 sw0_4:  .asciz  "cpu_switch: do not have lock"
522 #endif /* SMP && DIAGNOSTIC */
523
524 /*
525  * savectx(pcb)
526  * Update pcb, saving current processor state.
527  */
528 ENTRY(savectx)
529         /* fetch PCB */
530         movl    4(%esp),%ecx
531
532         /* caller's return address - child won't execute this routine */
533         movl    (%esp),%eax
534         movl    %eax,PCB_EIP(%ecx)
535
536         movl    %ebx,PCB_EBX(%ecx)
537         movl    %esp,PCB_ESP(%ecx)
538         movl    %ebp,PCB_EBP(%ecx)
539         movl    %esi,PCB_ESI(%ecx)
540         movl    %edi,PCB_EDI(%ecx)
541         movl    %gs,PCB_GS(%ecx)
542
543 #if NNPX > 0
544         /*
545          * If npxproc == NULL, then the npx h/w state is irrelevant and the
546          * state had better already be in the pcb.  This is true for forks
547          * but not for dumps (the old book-keeping with FP flags in the pcb
548          * always lost for dumps because the dump pcb has 0 flags).
549          *
550          * If npxproc != NULL, then we have to save the npx h/w state to
551          * npxproc's pcb and copy it to the requested pcb, or save to the
552          * requested pcb and reload.  Copying is easier because we would
553          * have to handle h/w bugs for reloading.  We used to lose the
554          * parent's npx state for forks by forgetting to reload.
555          */
556         movl    _npxproc,%eax
557         testl   %eax,%eax
558         je      1f
559
560         pushl   %ecx
561         movl    P_ADDR(%eax),%eax
562         leal    PCB_SAVEFPU(%eax),%eax
563         pushl   %eax
564         pushl   %eax
565         call    _npxsave
566         addl    $4,%esp
567         popl    %eax
568         popl    %ecx
569
570         pushl   $PCB_SAVEFPU_SIZE
571         leal    PCB_SAVEFPU(%ecx),%ecx
572         pushl   %ecx
573         pushl   %eax
574         call    _bcopy
575         addl    $12,%esp
576 #endif  /* NNPX > 0 */
577
578 1:
579         ret