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