cpu_switch.S revision 65557
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 65557 2000-09-07 01:33:02Z jasone $
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	_hlt_vector
64_hlt_vector:	.long	_default_halt	/* pointer to halt routine */
65
66	.globl	_panic
67
68#if defined(SWTCH_OPTIM_STATS)
69	.globl	_swtch_optim_stats, _tlb_flush_count
70_swtch_optim_stats:	.long	0		/* number of _swtch_optims */
71_tlb_flush_count:	.long	0
72#endif
73
74	.text
75
76ENTRY(default_halt)
77	sti
78#ifndef SMP
79	hlt					/* XXX:	 until a wakeup IPI */
80#endif
81	ret
82
83/*
84 * cpu_throw()
85 */
86ENTRY(cpu_throw)
87	jmp	sw1
88
89/*
90 * cpu_switch()
91 */
92ENTRY(cpu_switch)
93
94	/* switch to new process. first, save context as needed */
95	movl	_curproc,%ecx
96	movl	%ecx,_prevproc
97
98	/* if no process to save, don't bother */
99	testl	%ecx,%ecx
100	jz	sw1
101
102#ifdef SMP
103	movb	P_ONCPU(%ecx), %al		/* save "last" cpu */
104	movb	%al, P_LASTCPU(%ecx)
105	movb	$0xff, P_ONCPU(%ecx)		/* "leave" the cpu */
106#endif /* SMP */
107	movl	P_VMSPACE(%ecx), %edx
108#ifdef SMP
109	movl	_cpuid, %eax
110#else
111	xorl	%eax, %eax
112#endif /* SMP */
113	btrl	%eax, VM_PMAP+PM_ACTIVE(%edx)
114
115	movl	P_ADDR(%ecx),%edx
116
117	movl	(%esp),%eax			/* Hardware registers */
118	movl	%eax,PCB_EIP(%edx)
119	movl	%ebx,PCB_EBX(%edx)
120	movl	%esp,PCB_ESP(%edx)
121	movl	%ebp,PCB_EBP(%edx)
122	movl	%esi,PCB_ESI(%edx)
123	movl	%edi,PCB_EDI(%edx)
124	movl	%gs,PCB_GS(%edx)
125
126	/* test if debug registers should be saved */
127	movb    PCB_FLAGS(%edx),%al
128	andb    $PCB_DBREGS,%al
129	jz      1f                              /* no, skip over */
130	movl    %dr7,%eax                       /* yes, do the save */
131	movl    %eax,PCB_DR7(%edx)
132	andl    $0x0000ff00, %eax               /* disable all watchpoints */
133	movl    %eax,%dr7
134	movl    %dr6,%eax
135	movl    %eax,PCB_DR6(%edx)
136	movl    %dr3,%eax
137	movl    %eax,PCB_DR3(%edx)
138	movl    %dr2,%eax
139	movl    %eax,PCB_DR2(%edx)
140	movl    %dr1,%eax
141	movl    %eax,PCB_DR1(%edx)
142	movl    %dr0,%eax
143	movl    %eax,PCB_DR0(%edx)
1441:
145
146	/* save sched_lock recursion count */
147	movl	_sched_lock+MTX_RECURSE,%eax
148	movl    %eax,PCB_SCHEDNEST(%edx)
149
150#ifdef SMP
151	/* XXX FIXME: we should be saving the local APIC TPR */
152#endif /* SMP */
153
154#if NNPX > 0
155	/* have we used fp, and need a save? */
156	cmpl	%ecx,_npxproc
157	jne	1f
158	addl	$PCB_SAVEFPU,%edx		/* h/w bugs make saving complicated */
159	pushl	%edx
160	call	_npxsave			/* do it in a big C function */
161	popl	%eax
1621:
163#endif	/* NNPX > 0 */
164
165	/* save is done, now choose a new process */
166sw1:
167
168#ifdef SMP
169	/* Stop scheduling if smp_active goes zero and we are not BSP */
170	cmpl	$0,_smp_active
171	jne	1f
172	cmpl	$0,_cpuid
173	je	1f
174
175	movl	_idleproc, %eax
176	jmp	sw1b
1771:
178#endif
179
180	/*
181	 * Choose a new process to schedule.  chooseproc() returns idleproc
182	 * if it cannot find another process to run.
183	 */
184sw1a:
185	call	_chooseproc			/* trash ecx, edx, ret eax*/
186
187#ifdef DIAGNOSTIC
188	testl	%eax,%eax			/* no process? */
189	jz	badsw3				/* no, panic */
190#endif
191sw1b:
192	movl	%eax,%ecx
193
194	xorl	%eax,%eax
195	andl	$~AST_RESCHED,_astpending
196
197#ifdef	DIAGNOSTIC
198	cmpl	%eax,P_WCHAN(%ecx)
199	jne	badsw1
200	cmpb	$SRUN,P_STAT(%ecx)
201	jne	badsw2
202#endif
203
204	movl	P_ADDR(%ecx),%edx
205
206#if defined(SWTCH_OPTIM_STATS)
207	incl	_swtch_optim_stats
208#endif
209	/* switch address space */
210	movl	%cr3,%ebx
211	cmpl	PCB_CR3(%edx),%ebx
212	je	4f
213#if defined(SWTCH_OPTIM_STATS)
214	decl	_swtch_optim_stats
215	incl	_tlb_flush_count
216#endif
217	movl	PCB_CR3(%edx),%ebx
218	movl	%ebx,%cr3
2194:
220
221#ifdef SMP
222	movl	_cpuid, %esi
223#else
224	xorl	%esi, %esi
225#endif
226	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
227	je	1f
228	btsl	%esi, _private_tss		/* mark use of private tss */
229	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
230	jmp	2f
2311:
232
233	/* update common_tss.tss_esp0 pointer */
234	movl	%edx, %ebx			/* pcb */
235	addl	$(UPAGES * PAGE_SIZE - 16), %ebx
236	movl	%ebx, _common_tss + TSS_ESP0
237
238	btrl	%esi, _private_tss
239	jae	3f
240#ifdef SMP
241	movl	$gd_common_tssd, %edi
242	addl	%fs:0, %edi
243#else
244	movl	$_common_tssd, %edi
245#endif
2462:
247	/* move correct tss descriptor into GDT slot, then reload tr */
248	movl	_tss_gdt, %ebx			/* entry in GDT */
249	movl	0(%edi), %eax
250	movl	%eax, 0(%ebx)
251	movl	4(%edi), %eax
252	movl	%eax, 4(%ebx)
253	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
254	ltr	%si
2553:
256	movl	P_VMSPACE(%ecx), %ebx
257#ifdef SMP
258	movl	_cpuid, %eax
259#else
260	xorl	%eax, %eax
261#endif
262	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
263
264	/* restore context */
265	movl	PCB_EBX(%edx),%ebx
266	movl	PCB_ESP(%edx),%esp
267	movl	PCB_EBP(%edx),%ebp
268	movl	PCB_ESI(%edx),%esi
269	movl	PCB_EDI(%edx),%edi
270	movl	PCB_EIP(%edx),%eax
271	movl	%eax,(%esp)
272
273#ifdef SMP
274#ifdef GRAB_LOPRIO				/* hold LOPRIO for INTs */
275#ifdef CHEAP_TPR
276	movl	$0, lapic_tpr
277#else
278	andl	$~APIC_TPR_PRIO, lapic_tpr
279#endif /** CHEAP_TPR */
280#endif /** GRAB_LOPRIO */
281	movl	_cpuid,%eax
282	movb	%al, P_ONCPU(%ecx)
283#endif /* SMP */
284	movl	%edx, _curpcb
285	movl	%ecx, _curproc			/* into next process */
286
287#ifdef SMP
288	/* XXX FIXME: we should be restoring the local APIC TPR */
289#endif /* SMP */
290
291#ifdef	USER_LDT
292	cmpl	$0, PCB_USERLDT(%edx)
293	jnz	1f
294	movl	__default_ldt,%eax
295	cmpl	_currentldt,%eax
296	je	2f
297	lldt	__default_ldt
298	movl	%eax,_currentldt
299	jmp	2f
3001:	pushl	%edx
301	call	_set_user_ldt
302	popl	%edx
3032:
304#endif
305
306	/* This must be done after loading the user LDT. */
307	.globl	cpu_switch_load_gs
308cpu_switch_load_gs:
309	movl	PCB_GS(%edx),%gs
310
311	/* test if debug regisers should be restored */
312	movb    PCB_FLAGS(%edx),%al
313	andb    $PCB_DBREGS,%al
314	jz      1f                              /* no, skip over */
315	movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
316	movl    %eax,%dr6
317	movl    PCB_DR3(%edx),%eax
318	movl    %eax,%dr3
319	movl    PCB_DR2(%edx),%eax
320	movl    %eax,%dr2
321	movl    PCB_DR1(%edx),%eax
322	movl    %eax,%dr1
323	movl    PCB_DR0(%edx),%eax
324	movl    %eax,%dr0
325	movl    PCB_DR7(%edx),%eax
326	movl    %eax,%dr7
3271:
328
329	/*
330	 * restore sched_lock recursion count and transfer ownership to
331	 * new process
332	 */
333	movl	PCB_SCHEDNEST(%edx),%eax
334	movl	%eax,_sched_lock+MTX_RECURSE
335
336	movl	_curproc,%eax
337	movl	%eax,_sched_lock+MTX_LOCK
338
339#ifdef DIAGNOSTIC
340	pushfl
341	popl	%ecx
342	testl	$0x200, %ecx			/* interrupts enabled? */
343	jnz	badsw6				/* that way madness lies */
344#endif
345	ret
346
347CROSSJUMPTARGET(sw1a)
348
349#ifdef DIAGNOSTIC
350badsw1:
351	pushl	$sw0_1
352	call	_panic
353
354sw0_1:	.asciz	"cpu_switch: has wchan"
355
356badsw2:
357	pushl	$sw0_2
358	call	_panic
359
360sw0_2:	.asciz	"cpu_switch: not SRUN"
361
362badsw3:
363	pushl	$sw0_3
364	call	_panic
365
366sw0_3:	.asciz	"cpu_switch: chooseproc returned NULL"
367
368#endif
369
370#ifdef DIAGNOSTIC
371badsw5:
372	pushl	$sw0_5
373	call	_panic
374
375sw0_5:	.asciz	"cpu_switch: interrupts enabled (again)"
376badsw6:
377	pushl	$sw0_6
378	call	_panic
379
380sw0_6:	.asciz	"cpu_switch: interrupts enabled"
381#endif
382
383/*
384 * savectx(pcb)
385 * Update pcb, saving current processor state.
386 */
387ENTRY(savectx)
388	/* fetch PCB */
389	movl	4(%esp),%ecx
390
391	/* caller's return address - child won't execute this routine */
392	movl	(%esp),%eax
393	movl	%eax,PCB_EIP(%ecx)
394
395	movl	%ebx,PCB_EBX(%ecx)
396	movl	%esp,PCB_ESP(%ecx)
397	movl	%ebp,PCB_EBP(%ecx)
398	movl	%esi,PCB_ESI(%ecx)
399	movl	%edi,PCB_EDI(%ecx)
400	movl	%gs,PCB_GS(%ecx)
401
402#if NNPX > 0
403	/*
404	 * If npxproc == NULL, then the npx h/w state is irrelevant and the
405	 * state had better already be in the pcb.  This is true for forks
406	 * but not for dumps (the old book-keeping with FP flags in the pcb
407	 * always lost for dumps because the dump pcb has 0 flags).
408	 *
409	 * If npxproc != NULL, then we have to save the npx h/w state to
410	 * npxproc's pcb and copy it to the requested pcb, or save to the
411	 * requested pcb and reload.  Copying is easier because we would
412	 * have to handle h/w bugs for reloading.  We used to lose the
413	 * parent's npx state for forks by forgetting to reload.
414	 */
415	movl	_npxproc,%eax
416	testl	%eax,%eax
417	je	1f
418
419	pushl	%ecx
420	movl	P_ADDR(%eax),%eax
421	leal	PCB_SAVEFPU(%eax),%eax
422	pushl	%eax
423	pushl	%eax
424	call	_npxsave
425	addl	$4,%esp
426	popl	%eax
427	popl	%ecx
428
429	pushl	$PCB_SAVEFPU_SIZE
430	leal	PCB_SAVEFPU(%ecx),%ecx
431	pushl	%ecx
432	pushl	%eax
433	call	_bcopy
434	addl	$12,%esp
435#endif	/* NNPX > 0 */
436
4371:
438	ret
439