thread stage 5: Separate the inline functions out of sys/buf.h, creating
[dragonfly.git] / sys / platform / pc32 / 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/platform/pc32/i386/swtch.s,v 1.6 2003/06/18 18:29:55 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    _curthread,%ecx
284         movl    TD_PROC(%ecx),%ecx
285
286         /* if no process to save, don't bother */
287         testl   %ecx,%ecx
288         je      sw1
289
290 #ifdef SMP
291         movb    P_ONCPU(%ecx), %al              /* save "last" cpu */
292         movb    %al, P_LASTCPU(%ecx)
293         movb    $0xff, P_ONCPU(%ecx)            /* "leave" the cpu */
294 #endif /* SMP */
295         movl    P_VMSPACE(%ecx), %edx
296 #ifdef SMP
297         movl    _cpuid, %eax
298 #else
299         xorl    %eax, %eax
300 #endif /* SMP */
301         btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
302
303         movl    _curthread,%edx
304         movl    TD_PCB(%edx),%edx
305
306         movl    (%esp),%eax                     /* Hardware registers */
307         movl    %eax,PCB_EIP(%edx)
308         movl    %ebx,PCB_EBX(%edx)
309         movl    %esp,PCB_ESP(%edx)
310         movl    %ebp,PCB_EBP(%edx)
311         movl    %esi,PCB_ESI(%edx)
312         movl    %edi,PCB_EDI(%edx)
313         movl    %gs,PCB_GS(%edx)
314
315         /* test if debug regisers should be saved */
316         movb    PCB_FLAGS(%edx),%al
317         andb    $PCB_DBREGS,%al
318         jz      1f                              /* no, skip over */
319         movl    %dr7,%eax                       /* yes, do the save */
320         movl    %eax,PCB_DR7(%edx)
321         andl    $0x0000fc00, %eax               /* disable all watchpoints */
322         movl    %eax,%dr7
323         movl    %dr6,%eax
324         movl    %eax,PCB_DR6(%edx)
325         movl    %dr3,%eax
326         movl    %eax,PCB_DR3(%edx)
327         movl    %dr2,%eax
328         movl    %eax,PCB_DR2(%edx)
329         movl    %dr1,%eax
330         movl    %eax,PCB_DR1(%edx)
331         movl    %dr0,%eax
332         movl    %eax,PCB_DR0(%edx)
333 1:
334  
335 #ifdef SMP
336         movl    _mp_lock, %eax
337         /* XXX FIXME: we should be saving the local APIC TPR */
338 #ifdef DIAGNOSTIC
339         cmpl    $FREE_LOCK, %eax                /* is it free? */
340         je      badsw4                          /* yes, bad medicine! */
341 #endif /* DIAGNOSTIC */
342         andl    $COUNT_FIELD, %eax              /* clear CPU portion */
343         movl    %eax, PCB_MPNEST(%edx)          /* store it */
344 #endif /* SMP */
345
346 #if NNPX > 0
347         /* have we used fp, and need a save? */
348         movl    P_THREAD(%ecx),%ecx
349         cmpl    %ecx,_npxthread
350         jne     1f
351         addl    $PCB_SAVEFPU,%edx               /* h/w bugs make saving complicated */
352         pushl   %edx
353         call    _npxsave                        /* do it in a big C function */
354         popl    %eax
355 1:
356         /* %ecx,%edx trashed */
357 #endif  /* NNPX > 0 */
358
359         /*
360          * out of processes, set curthread to the current cpu's
361          * idlethread.  Note that idlethread.td_proc will be NULL.
362          */
363 #ifdef SMP
364         movl    $gd_idlethread, %edi
365         addl    %fs:0, %edi
366 #else
367         movl    $_idlethread, %edi
368 #endif
369         movl    %edi,_curthread
370
371         /* save is done, now choose a new process or idle */
372 sw1:
373         cli
374
375 #ifdef SMP
376         /* Stop scheduling if smp_active goes zero and we are not BSP */
377         cmpl    $0,_smp_active
378         jne     1f
379         cmpl    $0,_cpuid
380         CROSSJUMP(je, _idle, jne)               /* wind down */
381 1:
382 #endif
383
384 sw1a:
385         call    _chooseproc                     /* trash ecx, edx, ret eax*/
386         testl   %eax,%eax
387         CROSSJUMP(je, _idle, jne)               /* if no proc, idle */
388         movl    %eax,%ecx
389
390         xorl    %eax,%eax
391         andl    $~AST_RESCHED,_astpending
392
393 #ifdef  DIAGNOSTIC
394         cmpl    %eax,P_WCHAN(%ecx)
395         jne     badsw1
396         cmpb    $SRUN,P_STAT(%ecx)
397         jne     badsw2
398 #endif
399         movl    P_THREAD(%ecx),%edx
400         movl    TD_PCB(%edx),%edx
401
402 #if defined(SWTCH_OPTIM_STATS)
403         incl    _swtch_optim_stats
404 #endif
405         /* switch address space */
406         movl    %cr3,%ebx
407         cmpl    PCB_CR3(%edx),%ebx
408         je      4f
409 #if defined(SWTCH_OPTIM_STATS)
410         decl    _swtch_optim_stats
411         incl    _tlb_flush_count
412 #endif
413         movl    PCB_CR3(%edx),%ebx
414         movl    %ebx,%cr3
415 4:
416
417 #ifdef SMP
418         movl    _cpuid, %esi
419 #else
420         xorl    %esi, %esi
421 #endif
422         cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
423         je      1f
424         btsl    %esi, _private_tss              /* mark use of private tss */
425         movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
426         jmp     2f
427 1:
428
429         /*
430          * update common_tss.tss_esp0 pointer.  This is the supervisor
431          * stack pointer on entry from user mode.  Since the pcb is
432          * at the top of the supervisor stack esp0 starts just below it.
433          * We leave enough space for vm86 (16 bytes).
434          */
435         leal    -16(%edx),%ebx
436         movl    %ebx, _common_tss + TSS_ESP0
437
438         btrl    %esi, _private_tss
439         jae     3f
440 #ifdef SMP
441         movl    $gd_common_tssd, %edi
442         addl    %fs:0, %edi
443 #else
444         movl    $_common_tssd, %edi
445 #endif
446 2:
447         /* move correct tss descriptor into GDT slot, then reload tr */
448         movl    _tss_gdt, %ebx                  /* entry in GDT */
449         movl    0(%edi), %eax
450         movl    %eax, 0(%ebx)
451         movl    4(%edi), %eax
452         movl    %eax, 4(%ebx)
453         movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
454         ltr     %si
455 3:
456         movl    P_VMSPACE(%ecx), %ebx
457 #ifdef SMP
458         movl    _cpuid, %eax
459 #else
460         xorl    %eax, %eax
461 #endif
462         btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
463
464         /* restore context */
465         movl    PCB_EBX(%edx),%ebx
466         movl    PCB_ESP(%edx),%esp
467         movl    PCB_EBP(%edx),%ebp
468         movl    PCB_ESI(%edx),%esi
469         movl    PCB_EDI(%edx),%edi
470         movl    PCB_EIP(%edx),%eax
471         movl    %eax,(%esp)
472
473 #ifdef SMP
474 #ifdef GRAB_LOPRIO                              /* hold LOPRIO for INTs */
475 #ifdef CHEAP_TPR
476         movl    $0, lapic_tpr
477 #else
478         andl    $~APIC_TPR_PRIO, lapic_tpr
479 #endif /** CHEAP_TPR */
480 #endif /** GRAB_LOPRIO */
481         movl    _cpuid,%eax
482         movb    %al, P_ONCPU(%ecx)
483 #endif /* SMP */
484         movl    P_THREAD(%ecx),%ecx             /* ecx = thread */
485         movl    %ecx, _curthread
486         movl    TD_PROC(%ecx),%ecx  /* YYY does %ecx need to be restored? */
487
488 #ifdef SMP
489         movl    _cpu_lockid, %eax
490         orl     PCB_MPNEST(%edx), %eax          /* add next count from PROC */
491         movl    %eax, _mp_lock                  /* load the mp_lock */
492         /* XXX FIXME: we should be restoring the local APIC TPR */
493 #endif /* SMP */
494
495 #ifdef  USER_LDT
496         cmpl    $0, PCB_USERLDT(%edx)
497         jnz     1f
498         movl    __default_ldt,%eax
499         cmpl    _currentldt,%eax
500         je      2f
501         lldt    __default_ldt
502         movl    %eax,_currentldt
503         jmp     2f
504 1:      pushl   %edx
505         call    _set_user_ldt
506         popl    %edx
507 2:
508 #endif
509
510         /* This must be done after loading the user LDT. */
511         .globl  cpu_switch_load_gs
512 cpu_switch_load_gs:
513         movl    PCB_GS(%edx),%gs
514
515         /* test if debug regisers should be restored */
516         movb    PCB_FLAGS(%edx),%al
517         andb    $PCB_DBREGS,%al
518         jz      1f                              /* no, skip over */
519         movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
520         movl    %eax,%dr6
521         movl    PCB_DR3(%edx),%eax
522         movl    %eax,%dr3
523         movl    PCB_DR2(%edx),%eax
524         movl    %eax,%dr2
525         movl    PCB_DR1(%edx),%eax
526         movl    %eax,%dr1
527         movl    PCB_DR0(%edx),%eax
528         movl    %eax,%dr0
529         movl    %dr7,%eax                /* load dr7 so as not to disturb */
530         andl    $0x0000fc00,%eax         /*   reserved bits               */
531         pushl   %ebx
532         movl    PCB_DR7(%edx),%ebx
533         andl    $~0x0000fc00,%ebx
534         orl     %ebx,%eax
535         popl    %ebx
536         movl    %eax,%dr7
537 1:
538
539         sti
540         ret
541
542 CROSSJUMPTARGET(sw1a)
543
544 #ifdef DIAGNOSTIC
545 badsw1:
546         pushl   $sw0_1
547         call    _panic
548
549 sw0_1:  .asciz  "cpu_switch: has wchan"
550
551 badsw2:
552         pushl   $sw0_2
553         call    _panic
554
555 sw0_2:  .asciz  "cpu_switch: not SRUN"
556 #endif
557
558 #if defined(SMP) && defined(DIAGNOSTIC)
559 badsw4:
560         pushl   $sw0_4
561         call    _panic
562
563 sw0_4:  .asciz  "cpu_switch: do not have lock"
564 #endif /* SMP && DIAGNOSTIC */
565
566 /*
567  * savectx(pcb)
568  * Update pcb, saving current processor state.
569  */
570 ENTRY(savectx)
571         /* fetch PCB */
572         movl    4(%esp),%ecx
573
574         /* caller's return address - child won't execute this routine */
575         movl    (%esp),%eax
576         movl    %eax,PCB_EIP(%ecx)
577
578         movl    %cr3,%eax
579         movl    %eax,PCB_CR3(%ecx)
580
581         movl    %ebx,PCB_EBX(%ecx)
582         movl    %esp,PCB_ESP(%ecx)
583         movl    %ebp,PCB_EBP(%ecx)
584         movl    %esi,PCB_ESI(%ecx)
585         movl    %edi,PCB_EDI(%ecx)
586         movl    %gs,PCB_GS(%ecx)
587
588 #if NNPX > 0
589         /*
590          * If npxthread == NULL, then the npx h/w state is irrelevant and the
591          * state had better already be in the pcb.  This is true for forks
592          * but not for dumps (the old book-keeping with FP flags in the pcb
593          * always lost for dumps because the dump pcb has 0 flags).
594          *
595          * If npxthread != NULL, then we have to save the npx h/w state to
596          * npxthread's pcb and copy it to the requested pcb, or save to the
597          * requested pcb and reload.  Copying is easier because we would
598          * have to handle h/w bugs for reloading.  We used to lose the
599          * parent's npx state for forks by forgetting to reload.
600          */
601         movl    _npxthread,%eax
602         testl   %eax,%eax
603         je      1f
604
605         pushl   %ecx
606         movl    TD_PCB(%eax),%eax
607         leal    PCB_SAVEFPU(%eax),%eax
608         pushl   %eax
609         pushl   %eax
610         call    _npxsave
611         addl    $4,%esp
612         popl    %eax
613         popl    %ecx
614
615         pushl   $PCB_SAVEFPU_SIZE
616         leal    PCB_SAVEFPU(%ecx),%ecx
617         pushl   %ecx
618         pushl   %eax
619         call    _bcopy
620         addl    $12,%esp
621 #endif  /* NNPX > 0 */
622
623 1:
624         ret