cpu_switch.S revision 66855
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 66855 2000-10-09 05:39:03Z bde $
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 DIAGNOSTIC
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	DIAGNOSTIC
187	cmpl	%eax,P_WCHAN(%ecx)
188	jne	badsw1
189	cmpb	$SRUN,P_STAT(%ecx)
190	jne	badsw2
191#endif
192
193	movl	P_ADDR(%ecx),%edx
194
195#if defined(SWTCH_OPTIM_STATS)
196	incl	_swtch_optim_stats
197#endif
198	/* switch address space */
199	movl	%cr3,%ebx
200	cmpl	PCB_CR3(%edx),%ebx
201	je	4f
202#if defined(SWTCH_OPTIM_STATS)
203	decl	_swtch_optim_stats
204	incl	_tlb_flush_count
205#endif
206	movl	PCB_CR3(%edx),%ebx
207	movl	%ebx,%cr3
2084:
209
210#ifdef SMP
211	movl	_cpuid, %esi
212#else
213	xorl	%esi, %esi
214#endif
215	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
216	je	1f
217	btsl	%esi, _private_tss		/* mark use of private tss */
218	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
219	jmp	2f
2201:
221
222	/* update common_tss.tss_esp0 pointer */
223	movl	%edx, %ebx			/* pcb */
224	addl	$(UPAGES * PAGE_SIZE - 16), %ebx
225	movl	%ebx, _common_tss + TSS_ESP0
226
227	btrl	%esi, _private_tss
228	jae	3f
229#ifdef SMP
230	movl	$gd_common_tssd, %edi
231	addl	%fs:0, %edi
232#else
233	movl	$_common_tssd, %edi
234#endif
2352:
236	/* move correct tss descriptor into GDT slot, then reload tr */
237	movl	_tss_gdt, %ebx			/* entry in GDT */
238	movl	0(%edi), %eax
239	movl	%eax, 0(%ebx)
240	movl	4(%edi), %eax
241	movl	%eax, 4(%ebx)
242	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
243	ltr	%si
2443:
245	movl	P_VMSPACE(%ecx), %ebx
246#ifdef SMP
247	movl	_cpuid, %eax
248#else
249	xorl	%eax, %eax
250#endif
251	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
252
253	/* restore context */
254	movl	PCB_EBX(%edx),%ebx
255	movl	PCB_ESP(%edx),%esp
256	movl	PCB_EBP(%edx),%ebp
257	movl	PCB_ESI(%edx),%esi
258	movl	PCB_EDI(%edx),%edi
259	movl	PCB_EIP(%edx),%eax
260	movl	%eax,(%esp)
261
262#ifdef SMP
263#ifdef GRAB_LOPRIO				/* hold LOPRIO for INTs */
264#ifdef CHEAP_TPR
265	movl	$0, lapic_tpr
266#else
267	andl	$~APIC_TPR_PRIO, lapic_tpr
268#endif /** CHEAP_TPR */
269#endif /** GRAB_LOPRIO */
270	movl	_cpuid,%eax
271	movb	%al, P_ONCPU(%ecx)
272#endif /* SMP */
273	movl	%edx, _curpcb
274	movl	%ecx, _curproc			/* into next process */
275
276#ifdef SMP
277	/* XXX FIXME: we should be restoring the local APIC TPR */
278#endif /* SMP */
279
280#ifdef	USER_LDT
281	cmpl	$0, PCB_USERLDT(%edx)
282	jnz	1f
283	movl	__default_ldt,%eax
284	cmpl	_currentldt,%eax
285	je	2f
286	lldt	__default_ldt
287	movl	%eax,_currentldt
288	jmp	2f
2891:	pushl	%edx
290	call	_set_user_ldt
291	popl	%edx
2922:
293#endif
294
295	/* This must be done after loading the user LDT. */
296	.globl	cpu_switch_load_gs
297cpu_switch_load_gs:
298	movl	PCB_GS(%edx),%gs
299
300	/* test if debug regisers should be restored */
301	movb    PCB_FLAGS(%edx),%al
302	andb    $PCB_DBREGS,%al
303	jz      1f                              /* no, skip over */
304	movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
305	movl    %eax,%dr6
306	movl    PCB_DR3(%edx),%eax
307	movl    %eax,%dr3
308	movl    PCB_DR2(%edx),%eax
309	movl    %eax,%dr2
310	movl    PCB_DR1(%edx),%eax
311	movl    %eax,%dr1
312	movl    PCB_DR0(%edx),%eax
313	movl    %eax,%dr0
314	movl    PCB_DR7(%edx),%eax
315	movl    %eax,%dr7
3161:
317
318	/*
319	 * restore sched_lock recursion count and transfer ownership to
320	 * new process
321	 */
322	movl	PCB_SCHEDNEST(%edx),%eax
323	movl	%eax,_sched_lock+MTX_RECURSE
324
325	movl	_curproc,%eax
326	movl	%eax,_sched_lock+MTX_LOCK
327
328#ifdef DIAGNOSTIC
329	pushfl
330	popl	%ecx
331	testl	$0x200, %ecx			/* interrupts enabled? */
332	jnz	badsw6				/* that way madness lies */
333#endif
334	ret
335
336CROSSJUMPTARGET(sw1a)
337
338#ifdef DIAGNOSTIC
339badsw1:
340	pushl	$sw0_1
341	call	_panic
342
343sw0_1:	.asciz	"cpu_switch: has wchan"
344
345badsw2:
346	pushl	$sw0_2
347	call	_panic
348
349sw0_2:	.asciz	"cpu_switch: not SRUN"
350
351badsw3:
352	pushl	$sw0_3
353	call	_panic
354
355sw0_3:	.asciz	"cpu_switch: chooseproc returned NULL"
356
357#endif
358
359#ifdef DIAGNOSTIC
360badsw5:
361	pushl	$sw0_5
362	call	_panic
363
364sw0_5:	.asciz	"cpu_switch: interrupts enabled (again)"
365badsw6:
366	pushl	$sw0_6
367	call	_panic
368
369sw0_6:	.asciz	"cpu_switch: interrupts enabled"
370#endif
371
372/*
373 * savectx(pcb)
374 * Update pcb, saving current processor state.
375 */
376ENTRY(savectx)
377	/* fetch PCB */
378	movl	4(%esp),%ecx
379
380	/* caller's return address - child won't execute this routine */
381	movl	(%esp),%eax
382	movl	%eax,PCB_EIP(%ecx)
383
384	movl	%ebx,PCB_EBX(%ecx)
385	movl	%esp,PCB_ESP(%ecx)
386	movl	%ebp,PCB_EBP(%ecx)
387	movl	%esi,PCB_ESI(%ecx)
388	movl	%edi,PCB_EDI(%ecx)
389	movl	%gs,PCB_GS(%ecx)
390
391#if NNPX > 0
392	/*
393	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
394	 * state had better already be in the pcb.  This is true for forks
395	 * but not for dumps (the old book-keeping with FP flags in the pcb
396	 * always lost for dumps because the dump pcb has 0 flags).
397	 *
398	 * If npxproc != NULL, then we have to save the npx h/w state to
399	 * npxproc's pcb and copy it to the requested pcb, or save to the
400	 * requested pcb and reload.  Copying is easier because we would
401	 * have to handle h/w bugs for reloading.  We used to lose the
402	 * parent's npx state for forks by forgetting to reload.
403	 */
404	movl	_npxproc,%eax
405	testl	%eax,%eax
406	je	1f
407
408	pushl	%ecx
409	movl	P_ADDR(%eax),%eax
410	leal	PCB_SAVEFPU(%eax),%eax
411	pushl	%eax
412	pushl	%eax
413	call	_npxsave
414	addl	$4,%esp
415	popl	%eax
416	popl	%ecx
417
418	pushl	$PCB_SAVEFPU_SIZE
419	leal	PCB_SAVEFPU(%ecx),%ecx
420	pushl	%ecx
421	pushl	%eax
422	call	_bcopy
423	addl	$12,%esp
424#endif	/* NNPX > 0 */
425
4261:
427	ret
428