swtch.s revision 72358
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: head/sys/i386/i386/swtch.s 72358 2001-02-11 10:44:09Z markm $
37 */
38
39#include "opt_npx.h"
40#include "opt_user_ldt.h"
41
42#include <sys/rtprio.h>
43
44#include <machine/asmacros.h>
45#include <machine/ipl.h>
46
47#ifdef SMP
48#include <machine/pmap.h>
49#include <machine/apic.h>
50#include <machine/smptests.h>		/** GRAB_LOPRIO */
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 */
75ENTRY(cpu_throw)
76	jmp	sw1
77
78/*
79 * cpu_switch()
80 */
81ENTRY(cpu_switch)
82
83	/* switch to new process. first, save context as needed */
84	movl	PCPU(CURPROC),%ecx
85
86	/* if no process to save, don't bother */
87	testl	%ecx,%ecx
88	jz	sw1
89
90#ifdef SMP
91	movb	P_ONCPU(%ecx), %al		/* save "last" cpu */
92	movb	%al, P_LASTCPU(%ecx)
93	movb	$0xff, P_ONCPU(%ecx)		/* "leave" the cpu */
94#endif /* SMP */
95	movl	P_VMSPACE(%ecx), %edx
96#ifdef SMP
97	movl	PCPU(CPUID), %eax
98#else
99	xorl	%eax, %eax
100#endif /* SMP */
101	btrl	%eax, VM_PMAP+PM_ACTIVE(%edx)
102
103	movl	P_ADDR(%ecx),%edx
104
105	movl	(%esp),%eax			/* Hardware registers */
106	movl	%eax,PCB_EIP(%edx)
107	movl	%ebx,PCB_EBX(%edx)
108	movl	%esp,PCB_ESP(%edx)
109	movl	%ebp,PCB_EBP(%edx)
110	movl	%esi,PCB_ESI(%edx)
111	movl	%edi,PCB_EDI(%edx)
112	movl	%gs,PCB_GS(%edx)
113
114	/* test if debug registers should be saved */
115	movb    PCB_FLAGS(%edx),%al
116	andb    $PCB_DBREGS,%al
117	jz      1f                              /* no, skip over */
118	movl    %dr7,%eax                       /* yes, do the save */
119	movl    %eax,PCB_DR7(%edx)
120	andl    $0x0000ff00, %eax               /* disable all watchpoints */
121	movl    %eax,%dr7
122	movl    %dr6,%eax
123	movl    %eax,PCB_DR6(%edx)
124	movl    %dr3,%eax
125	movl    %eax,PCB_DR3(%edx)
126	movl    %dr2,%eax
127	movl    %eax,PCB_DR2(%edx)
128	movl    %dr1,%eax
129	movl    %eax,PCB_DR1(%edx)
130	movl    %dr0,%eax
131	movl    %eax,PCB_DR0(%edx)
1321:
133
134	/* save sched_lock recursion count */
135	movl	_sched_lock+MTX_RECURSECNT,%eax
136	movl    %eax,PCB_SCHEDNEST(%edx)
137
138#ifdef SMP
139	/* XXX FIXME: we should be saving the local APIC TPR */
140#endif /* SMP */
141
142#ifdef DEV_NPX
143	/* have we used fp, and need a save? */
144	cmpl	%ecx,PCPU(NPXPROC)
145	jne	1f
146	addl	$PCB_SAVEFPU,%edx		/* h/w bugs make saving complicated */
147	pushl	%edx
148	call	_npxsave			/* do it in a big C function */
149	popl	%eax
1501:
151#endif	/* DEV_NPX */
152
153	/* save is done, now choose a new process */
154sw1:
155
156#ifdef SMP
157	/* Stop scheduling if smp_active goes zero and we are not BSP */
158	cmpl	$0,_smp_active
159	jne	1f
160	cmpl	$0,PCPU(CPUID)
161	je	1f
162
163	movl	PCPU(IDLEPROC), %eax
164	jmp	sw1b
1651:
166#endif
167
168	/*
169	 * Choose a new process to schedule.  chooseproc() returns idleproc
170	 * if it cannot find another process to run.
171	 */
172sw1a:
173	call	_chooseproc			/* trash ecx, edx, ret eax*/
174
175#ifdef INVARIANTS
176	testl	%eax,%eax			/* no process? */
177	jz	badsw3				/* no, panic */
178#endif
179sw1b:
180	movl	%eax,%ecx
181
182#ifdef	INVARIANTS
183	cmpb	$SRUN,P_STAT(%ecx)
184	jne	badsw2
185#endif
186
187	movl	P_ADDR(%ecx),%edx
188
189#if defined(SWTCH_OPTIM_STATS)
190	incl	_swtch_optim_stats
191#endif
192	/* switch address space */
193	movl	%cr3,%ebx
194	cmpl	PCB_CR3(%edx),%ebx
195	je	4f
196#if defined(SWTCH_OPTIM_STATS)
197	decl	_swtch_optim_stats
198	incl	_tlb_flush_count
199#endif
200	movl	PCB_CR3(%edx),%ebx
201	movl	%ebx,%cr3
2024:
203
204#ifdef SMP
205	movl	PCPU(CPUID), %esi
206#else
207	xorl	%esi, %esi
208#endif
209	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
210	je	1f
211	btsl	%esi, _private_tss		/* mark use of private tss */
212	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
213	jmp	2f
2141:
215
216	/* update common_tss.tss_esp0 pointer */
217	movl	%edx, %ebx			/* pcb */
218	addl	$(UPAGES * PAGE_SIZE - 16), %ebx
219	movl	%ebx, PCPU(COMMON_TSS) + TSS_ESP0
220
221	btrl	%esi, _private_tss
222	jae	3f
223	PCPU_ADDR(COMMON_TSSD, %edi)
2242:
225	/* move correct tss descriptor into GDT slot, then reload tr */
226	movl	PCPU(TSS_GDT), %ebx		/* entry in GDT */
227	movl	0(%edi), %eax
228	movl	%eax, 0(%ebx)
229	movl	4(%edi), %eax
230	movl	%eax, 4(%ebx)
231	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
232	ltr	%si
2333:
234	movl	P_VMSPACE(%ecx), %ebx
235#ifdef SMP
236	movl	PCPU(CPUID), %eax
237#else
238	xorl	%eax, %eax
239#endif
240	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
241
242	/* restore context */
243	movl	PCB_EBX(%edx),%ebx
244	movl	PCB_ESP(%edx),%esp
245	movl	PCB_EBP(%edx),%ebp
246	movl	PCB_ESI(%edx),%esi
247	movl	PCB_EDI(%edx),%edi
248	movl	PCB_EIP(%edx),%eax
249	movl	%eax,(%esp)
250
251#ifdef SMP
252#ifdef GRAB_LOPRIO				/* hold LOPRIO for INTs */
253#ifdef CHEAP_TPR
254	movl	$0, _lapic+LA_TPR
255#else
256	andl	$~APIC_TPR_PRIO, _lapic+LA_TPR
257#endif /** CHEAP_TPR */
258#endif /** GRAB_LOPRIO */
259	movl	PCPU(CPUID),%eax
260	movb	%al, P_ONCPU(%ecx)
261#endif /* SMP */
262	movl	%edx, PCPU(CURPCB)
263	movl	%ecx, PCPU(CURPROC)		/* into next process */
264
265#ifdef SMP
266	/* XXX FIXME: we should be restoring the local APIC TPR */
267#endif /* SMP */
268
269#ifdef	USER_LDT
270	cmpl	$0, PCB_USERLDT(%edx)
271	jnz	1f
272	movl	__default_ldt,%eax
273	cmpl	PCPU(CURRENTLDT),%eax
274	je	2f
275	lldt	__default_ldt
276	movl	%eax,PCPU(CURRENTLDT)
277	jmp	2f
2781:	pushl	%edx
279	call	_set_user_ldt
280	popl	%edx
2812:
282#endif
283
284	/* This must be done after loading the user LDT. */
285	.globl	cpu_switch_load_gs
286cpu_switch_load_gs:
287	movl	PCB_GS(%edx),%gs
288
289	/* test if debug regisers should be restored */
290	movb    PCB_FLAGS(%edx),%al
291	andb    $PCB_DBREGS,%al
292	jz      1f                              /* no, skip over */
293	movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
294	movl    %eax,%dr6
295	movl    PCB_DR3(%edx),%eax
296	movl    %eax,%dr3
297	movl    PCB_DR2(%edx),%eax
298	movl    %eax,%dr2
299	movl    PCB_DR1(%edx),%eax
300	movl    %eax,%dr1
301	movl    PCB_DR0(%edx),%eax
302	movl    %eax,%dr0
303	movl    PCB_DR7(%edx),%eax
304	movl    %eax,%dr7
3051:
306
307	/*
308	 * restore sched_lock recursion count and transfer ownership to
309	 * new process
310	 */
311	movl	PCB_SCHEDNEST(%edx),%eax
312	movl	%eax,_sched_lock+MTX_RECURSECNT
313
314	movl	PCPU(CURPROC),%eax
315	movl	%eax,_sched_lock+MTX_LOCK
316
317	ret
318
319CROSSJUMPTARGET(sw1a)
320
321#ifdef INVARIANTS
322badsw2:
323	pushl	$sw0_2
324	call	_panic
325
326sw0_2:	.asciz	"cpu_switch: not SRUN"
327
328badsw3:
329	pushl	$sw0_3
330	call	_panic
331
332sw0_3:	.asciz	"cpu_switch: chooseproc returned NULL"
333#endif
334
335/*
336 * savectx(pcb)
337 * Update pcb, saving current processor state.
338 */
339ENTRY(savectx)
340	/* fetch PCB */
341	movl	4(%esp),%ecx
342
343	/* caller's return address - child won't execute this routine */
344	movl	(%esp),%eax
345	movl	%eax,PCB_EIP(%ecx)
346
347	movl	%cr3,%eax
348	movl	%eax,PCB_CR3(%ecx)
349
350	movl	%ebx,PCB_EBX(%ecx)
351	movl	%esp,PCB_ESP(%ecx)
352	movl	%ebp,PCB_EBP(%ecx)
353	movl	%esi,PCB_ESI(%ecx)
354	movl	%edi,PCB_EDI(%ecx)
355	movl	%gs,PCB_GS(%ecx)
356
357#ifdef DEV_NPX
358	/*
359	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
360	 * state had better already be in the pcb.  This is true for forks
361	 * but not for dumps (the old book-keeping with FP flags in the pcb
362	 * always lost for dumps because the dump pcb has 0 flags).
363	 *
364	 * If npxproc != NULL, then we have to save the npx h/w state to
365	 * npxproc's pcb and copy it to the requested pcb, or save to the
366	 * requested pcb and reload.  Copying is easier because we would
367	 * have to handle h/w bugs for reloading.  We used to lose the
368	 * parent's npx state for forks by forgetting to reload.
369	 */
370	movl	PCPU(NPXPROC),%eax
371	testl	%eax,%eax
372	je	1f
373
374	pushl	%ecx
375	movl	P_ADDR(%eax),%eax
376	leal	PCB_SAVEFPU(%eax),%eax
377	pushl	%eax
378	pushl	%eax
379	call	_npxsave
380	addl	$4,%esp
381	popl	%eax
382	popl	%ecx
383
384	pushl	$PCB_SAVEFPU_SIZE
385	leal	PCB_SAVEFPU(%ecx),%ecx
386	pushl	%ecx
387	pushl	%eax
388	call	_bcopy
389	addl	$12,%esp
390#endif	/* DEV_NPX */
391
3921:
393	ret
394