cpu_switch.S revision 69779
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/amd64/amd64/cpu_switch.S 69779 2000-12-08 21:31:52Z jake $
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#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#include <machine/lock.h>
52#endif /* SMP */
53
54#include "assym.s"
55
56
57/*****************************************************************************/
58/* Scheduling                                                                */
59/*****************************************************************************/
60
61	.data
62
63	.globl	_panic
64
65#if defined(SWTCH_OPTIM_STATS)
66	.globl	_swtch_optim_stats, _tlb_flush_count
67_swtch_optim_stats:	.long	0		/* number of _swtch_optims */
68_tlb_flush_count:	.long	0
69#endif
70
71	.text
72
73/*
74 * cpu_throw()
75 */
76ENTRY(cpu_throw)
77	jmp	sw1
78
79/*
80 * cpu_switch()
81 */
82ENTRY(cpu_switch)
83
84	/* switch to new process. first, save context as needed */
85	movl	_curproc,%ecx
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)
1331:
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
1511:
152#endif	/* NNPX > 0 */
153
154	/* save is done, now choose a new process */
155sw1:
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
1661:
167#endif
168
169	/*
170	 * Choose a new process to schedule.  chooseproc() returns idleproc
171	 * if it cannot find another process to run.
172	 */
173sw1a:
174	call	_chooseproc			/* trash ecx, edx, ret eax*/
175
176#ifdef INVARIANTS
177	testl	%eax,%eax			/* no process? */
178	jz	badsw3				/* no, panic */
179#endif
180sw1b:
181	movl	%eax,%ecx
182
183	xorl	%eax,%eax
184	andl	$~AST_RESCHED,_astpending
185
186#ifdef	INVARIANTS
187	cmpb	$SRUN,P_STAT(%ecx)
188	jne	badsw2
189#endif
190
191	movl	P_ADDR(%ecx),%edx
192
193#if defined(SWTCH_OPTIM_STATS)
194	incl	_swtch_optim_stats
195#endif
196	/* switch address space */
197	movl	%cr3,%ebx
198	cmpl	PCB_CR3(%edx),%ebx
199	je	4f
200#if defined(SWTCH_OPTIM_STATS)
201	decl	_swtch_optim_stats
202	incl	_tlb_flush_count
203#endif
204	movl	PCB_CR3(%edx),%ebx
205	movl	%ebx,%cr3
2064:
207
208#ifdef SMP
209	movl	_cpuid, %esi
210#else
211	xorl	%esi, %esi
212#endif
213	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
214	je	1f
215	btsl	%esi, _private_tss		/* mark use of private tss */
216	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
217	jmp	2f
2181:
219
220	/* update common_tss.tss_esp0 pointer */
221	movl	%edx, %ebx			/* pcb */
222	addl	$(UPAGES * PAGE_SIZE - 16), %ebx
223	movl	%ebx, _common_tss + TSS_ESP0
224
225	btrl	%esi, _private_tss
226	jae	3f
227#ifdef SMP
228	movl	$gd_common_tssd, %edi
229	addl	%fs:0, %edi
230#else
231	movl	$_common_tssd, %edi
232#endif
2332:
234	/* move correct tss descriptor into GDT slot, then reload tr */
235	movl	_tss_gdt, %ebx			/* entry in GDT */
236	movl	0(%edi), %eax
237	movl	%eax, 0(%ebx)
238	movl	4(%edi), %eax
239	movl	%eax, 4(%ebx)
240	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
241	ltr	%si
2423:
243	movl	P_VMSPACE(%ecx), %ebx
244#ifdef SMP
245	movl	_cpuid, %eax
246#else
247	xorl	%eax, %eax
248#endif
249	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
250
251	/* restore context */
252	movl	PCB_EBX(%edx),%ebx
253	movl	PCB_ESP(%edx),%esp
254	movl	PCB_EBP(%edx),%ebp
255	movl	PCB_ESI(%edx),%esi
256	movl	PCB_EDI(%edx),%edi
257	movl	PCB_EIP(%edx),%eax
258	movl	%eax,(%esp)
259
260#ifdef SMP
261#ifdef GRAB_LOPRIO				/* hold LOPRIO for INTs */
262#ifdef CHEAP_TPR
263	movl	$0, lapic_tpr
264#else
265	andl	$~APIC_TPR_PRIO, lapic_tpr
266#endif /** CHEAP_TPR */
267#endif /** GRAB_LOPRIO */
268	movl	_cpuid,%eax
269	movb	%al, P_ONCPU(%ecx)
270#endif /* SMP */
271	movl	%edx, _curpcb
272	movl	%ecx, _curproc			/* into next process */
273
274#ifdef SMP
275	/* XXX FIXME: we should be restoring the local APIC TPR */
276#endif /* SMP */
277
278#ifdef	USER_LDT
279	cmpl	$0, PCB_USERLDT(%edx)
280	jnz	1f
281	movl	__default_ldt,%eax
282	cmpl	_currentldt,%eax
283	je	2f
284	lldt	__default_ldt
285	movl	%eax,_currentldt
286	jmp	2f
2871:	pushl	%edx
288	call	_set_user_ldt
289	popl	%edx
2902:
291#endif
292
293	/* This must be done after loading the user LDT. */
294	.globl	cpu_switch_load_gs
295cpu_switch_load_gs:
296	movl	PCB_GS(%edx),%gs
297
298	/* test if debug regisers should be restored */
299	movb    PCB_FLAGS(%edx),%al
300	andb    $PCB_DBREGS,%al
301	jz      1f                              /* no, skip over */
302	movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
303	movl    %eax,%dr6
304	movl    PCB_DR3(%edx),%eax
305	movl    %eax,%dr3
306	movl    PCB_DR2(%edx),%eax
307	movl    %eax,%dr2
308	movl    PCB_DR1(%edx),%eax
309	movl    %eax,%dr1
310	movl    PCB_DR0(%edx),%eax
311	movl    %eax,%dr0
312	movl    PCB_DR7(%edx),%eax
313	movl    %eax,%dr7
3141:
315
316	/*
317	 * restore sched_lock recursion count and transfer ownership to
318	 * new process
319	 */
320	movl	PCB_SCHEDNEST(%edx),%eax
321	movl	%eax,_sched_lock+MTX_RECURSE
322
323	movl	_curproc,%eax
324	movl	%eax,_sched_lock+MTX_LOCK
325
326	ret
327
328CROSSJUMPTARGET(sw1a)
329
330#ifdef INVARIANTS
331badsw2:
332	pushl	$sw0_2
333	call	_panic
334
335sw0_2:	.asciz	"cpu_switch: not SRUN"
336
337badsw3:
338	pushl	$sw0_3
339	call	_panic
340
341sw0_3:	.asciz	"cpu_switch: chooseproc returned NULL"
342#endif
343
344/*
345 * savectx(pcb)
346 * Update pcb, saving current processor state.
347 */
348ENTRY(savectx)
349	/* fetch PCB */
350	movl	4(%esp),%ecx
351
352	/* caller's return address - child won't execute this routine */
353	movl	(%esp),%eax
354	movl	%eax,PCB_EIP(%ecx)
355
356	movl	%cr3,%eax
357	movl	%eax,PCB_CR3(%ecx)
358
359	movl	%ebx,PCB_EBX(%ecx)
360	movl	%esp,PCB_ESP(%ecx)
361	movl	%ebp,PCB_EBP(%ecx)
362	movl	%esi,PCB_ESI(%ecx)
363	movl	%edi,PCB_EDI(%ecx)
364	movl	%gs,PCB_GS(%ecx)
365
366#if NNPX > 0
367	/*
368	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
369	 * state had better already be in the pcb.  This is true for forks
370	 * but not for dumps (the old book-keeping with FP flags in the pcb
371	 * always lost for dumps because the dump pcb has 0 flags).
372	 *
373	 * If npxproc != NULL, then we have to save the npx h/w state to
374	 * npxproc's pcb and copy it to the requested pcb, or save to the
375	 * requested pcb and reload.  Copying is easier because we would
376	 * have to handle h/w bugs for reloading.  We used to lose the
377	 * parent's npx state for forks by forgetting to reload.
378	 */
379	movl	_npxproc,%eax
380	testl	%eax,%eax
381	je	1f
382
383	pushl	%ecx
384	movl	P_ADDR(%eax),%eax
385	leal	PCB_SAVEFPU(%eax),%eax
386	pushl	%eax
387	pushl	%eax
388	call	_npxsave
389	addl	$4,%esp
390	popl	%eax
391	popl	%ecx
392
393	pushl	$PCB_SAVEFPU_SIZE
394	leal	PCB_SAVEFPU(%ecx),%ecx
395	pushl	%ecx
396	pushl	%eax
397	call	_bcopy
398	addl	$12,%esp
399#endif	/* NNPX > 0 */
400
4011:
402	ret
403