cpu_switch.S revision 85487
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 85487 2001-10-25 16:47:01Z jhb $
37 */
38
39#include "opt_npx.h"
40
41#include <machine/asmacros.h>
42
43#ifdef SMP
44#include <machine/apic.h>
45#include <machine/smptests.h>			/* CHEAP_TPR, GRAB_LOPRIO */
46#endif /* SMP */
47
48#include "assym.s"
49
50/*****************************************************************************/
51/* Scheduling                                                                */
52/*****************************************************************************/
53
54	.data
55
56	.globl	panic
57
58#if defined(SWTCH_OPTIM_STATS)
59	.globl	swtch_optim_stats, tlb_flush_count
60swtch_optim_stats:	.long	0		/* number of _swtch_optims */
61tlb_flush_count:	.long	0
62#endif
63
64	.text
65
66/*
67 * cpu_throw()
68 */
69ENTRY(cpu_throw)
70	jmp	sw1
71
72/*
73 * cpu_switch()
74 */
75ENTRY(cpu_switch)
76
77	/* Switch to new process.  First, save context as needed. */
78	movl	PCPU(CURTHREAD),%ecx
79
80	/* If no process to save, don't save it (XXX shouldn't happen). */
81	testl	%ecx,%ecx
82	jz	sw1
83
84	movl	TD_PROC(%ecx), %eax
85	movl	P_VMSPACE(%eax), %edx
86	movl	PCPU(CPUID), %eax
87	btrl	%eax, VM_PMAP+PM_ACTIVE(%edx)
88
89	movl	TD_PCB(%ecx),%edx
90
91	movl	(%esp),%eax			/* Hardware registers */
92	movl	%eax,PCB_EIP(%edx)
93	movl	%ebx,PCB_EBX(%edx)
94	movl	%esp,PCB_ESP(%edx)
95	movl	%ebp,PCB_EBP(%edx)
96	movl	%esi,PCB_ESI(%edx)
97	movl	%edi,PCB_EDI(%edx)
98	movl	%gs,PCB_GS(%edx)
99
100	/* Test if debug registers should be saved. */
101	movb    PCB_FLAGS(%edx),%al
102	andb    $PCB_DBREGS,%al
103	jz      1f                              /* no, skip over */
104	movl    %dr7,%eax                       /* yes, do the save */
105	movl    %eax,PCB_DR7(%edx)
106	andl    $0x0000fc00, %eax               /* disable all watchpoints */
107	movl    %eax,%dr7
108	movl    %dr6,%eax
109	movl    %eax,PCB_DR6(%edx)
110	movl    %dr3,%eax
111	movl    %eax,PCB_DR3(%edx)
112	movl    %dr2,%eax
113	movl    %eax,PCB_DR2(%edx)
114	movl    %dr1,%eax
115	movl    %eax,PCB_DR1(%edx)
116	movl    %dr0,%eax
117	movl    %eax,PCB_DR0(%edx)
1181:
119
120#ifdef SMP
121	/* XXX FIXME: we should be saving the local APIC TPR */
122#endif /* SMP */
123
124#ifdef DEV_NPX
125	/* have we used fp, and need a save? */
126	cmpl	%ecx,PCPU(NPXTHREAD)
127	jne	1f
128	addl	$PCB_SAVEFPU,%edx		/* h/w bugs make saving complicated */
129	pushl	%edx
130	call	npxsave				/* do it in a big C function */
131	popl	%eax
1321:
133#endif	/* DEV_NPX */
134
135	/* Save is done.  Now choose a new thread. */
136	/* XXX still trashing space above the old "Top Of Stack". */
137sw1:
138
139#ifdef SMP
140	/* Stop scheduling if smp_active goes to zero and we are not the BSP. */
141	cmpl	$0,smp_active
142	jne	1f
143	cmpl	$0,PCPU(CPUID)
144	je	1f
145	/* Idle thread can run on any kernel context. */
146	movl	PCPU(IDLETHREAD), %eax
147	jmp	sw1b
1481:
149#endif
150
151	/*
152	 * Choose a new thread to schedule.  choosethread() returns idlethread
153	 * if it cannot find another thread to run.
154	 */
155sw1a:
156	call	choosethread			/* trash ecx, edx, ret eax */
157
158#ifdef INVARIANTS
159	testl	%eax,%eax			/* no thread? */
160	jz	badsw3				/* no, panic */
161#endif
162
163sw1b:
164	movl	%eax,%ecx
165
166#ifdef	INVARIANTS
167	movl	TD_PROC(%ecx), %eax		/* XXXKSE */
168	cmpb	$SRUN,P_STAT(%eax)
169	jne	badsw2
170#endif
171
172	movl	TD_PCB(%ecx),%edx
173
174#if defined(SWTCH_OPTIM_STATS)
175	incl	swtch_optim_stats
176#endif
177
178	/* switch address space */
179	movl	%cr3,%ebx
180	cmpl	PCB_CR3(%edx),%ebx
181	je	4f
182#if defined(SWTCH_OPTIM_STATS)
183	decl	swtch_optim_stats
184	incl	tlb_flush_count
185#endif
186	movl	PCB_CR3(%edx),%ebx
187	movl	%ebx,%cr3			/* Load new page tables. */
1884:
189
190	movl	PCPU(CPUID), %esi
191	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
192	je	1f
193	btsl	%esi, private_tss		/* mark use of private tss */
194	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
195	jmp	2f
1961:
197	/* update common_tss.tss_esp0 pointer */
198	leal	-16(%edx), %ebx			/* leave space for vm86 */
199	movl	%ebx, PCPU(COMMON_TSS) + TSS_ESP0 /* stack is below pcb */
200
201	btrl	%esi, private_tss
202	jae	3f
203	PCPU_ADDR(COMMON_TSSD, %edi)
2042:
205	/* Move correct tss descriptor into GDT slot, then reload tr. */
206	movl	PCPU(TSS_GDT), %ebx		/* entry in GDT */
207	movl	0(%edi), %eax
208	movl	%eax, 0(%ebx)
209	movl	4(%edi), %eax
210	movl	%eax, 4(%ebx)
211	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
212	ltr	%si
2133:
214	/* Note in a vmspace that this cpu is using it. */
215	movl	TD_PROC(%ecx),%eax		/* XXXKSE proc from thread */
216	movl	P_VMSPACE(%eax), %ebx
217	movl	PCPU(CPUID), %eax
218	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
219
220	/* restore context */
221	movl	PCB_EBX(%edx),%ebx
222	movl	PCB_ESP(%edx),%esp
223	movl	PCB_EBP(%edx),%ebp
224	movl	PCB_ESI(%edx),%esi
225	movl	PCB_EDI(%edx),%edi
226	movl	PCB_EIP(%edx),%eax
227	movl	%eax,(%esp)
228
229#ifdef SMP
230#ifdef GRAB_LOPRIO				/* hold LOPRIO for INTs */
231#ifdef CHEAP_TPR
232	movl	$0, lapic+LA_TPR
233#else
234	andl	$~APIC_TPR_PRIO, lapic+LA_TPR
235#endif /** CHEAP_TPR */
236#endif /** GRAB_LOPRIO */
237#endif /* SMP */
238	movl	%edx, PCPU(CURPCB)
239	movl	%ecx, PCPU(CURTHREAD)		/* into next thread */
240
241#ifdef SMP
242	/* XXX FIXME: we should be restoring the local APIC TPR */
243#endif /* SMP */
244
245	movl	TD_PROC(%ecx),%eax
246	cmpl    $0,P_MD+MD_LDT(%eax)		/* Have an LDT? */
247	jnz	1f				/* Yes, use it. */
248	movl	_default_ldt,%eax		/* Otherwise, use default. */
249	cmpl	PCPU(CURRENTLDT),%eax
250	je	2f
251	lldt	_default_ldt
252	movl	%eax,PCPU(CURRENTLDT)
253	jmp	2f
254
2551:	pushl	%edx				/* Preserver pointer to pcb. */
256	pushl	P_MD+MD_LDT(%eax)	/* passing p_md -> set_user_ldt */
257	call	set_user_ldt			/* Check and load the ldt. */
258	popl	%eax
259	popl	%edx
2602:
261
262	/* This must be done after loading the user LDT. */
263	.globl	cpu_switch_load_gs
264cpu_switch_load_gs:
265	movl	PCB_GS(%edx),%gs
266
267	/* test if debug regisers should be restored */
268	movb    PCB_FLAGS(%edx),%al
269	andb    $PCB_DBREGS,%al
270	jz      1f                              /* no, skip over */
271	movl    PCB_DR6(%edx),%eax              /* yes, do the restore */
272	movl    %eax,%dr6
273	movl    PCB_DR3(%edx),%eax
274	movl    %eax,%dr3
275	movl    PCB_DR2(%edx),%eax
276	movl    %eax,%dr2
277	movl    PCB_DR1(%edx),%eax
278	movl    %eax,%dr1
279	movl    PCB_DR0(%edx),%eax
280	movl    %eax,%dr0
281	movl	%dr7,%eax                	/* load dr7 so as not to */
282	andl    $0x0000fc00,%eax         	/* disturb reserved bits */
283	pushl   %ebx
284	movl    PCB_DR7(%edx),%ebx
285	andl	$~0x0000fc00,%ebx	/* re-enable the restored watchpoints */
286	orl     %ebx,%eax
287	popl	%ebx
288	movl    %eax,%dr7
2891:
290	ret
291
292CROSSJUMPTARGET(sw1a)
293
294#ifdef INVARIANTS
295badsw2:
296	pushl	$sw0_2
297	call	panic
298
299sw0_2:	.asciz	"cpu_switch: not SRUN"
300
301badsw3:
302	pushl	$sw0_3
303	call	panic
304
305sw0_3:	.asciz	"cpu_switch: choosethread returned NULL"
306#endif
307
308/*
309 * savectx(pcb)
310 * Update pcb, saving current processor state.
311 */
312ENTRY(savectx)
313	/* fetch PCB */
314	movl	4(%esp),%ecx
315
316	/* caller's return address - child won't execute this routine */
317	movl	(%esp),%eax
318	movl	%eax,PCB_EIP(%ecx)
319
320	movl	%cr3,%eax
321	movl	%eax,PCB_CR3(%ecx)
322
323	movl	%ebx,PCB_EBX(%ecx)
324	movl	%esp,PCB_ESP(%ecx)
325	movl	%ebp,PCB_EBP(%ecx)
326	movl	%esi,PCB_ESI(%ecx)
327	movl	%edi,PCB_EDI(%ecx)
328	movl	%gs,PCB_GS(%ecx)
329
330#ifdef DEV_NPX
331	/*
332	 * If npxthread == NULL, then the npx h/w state is irrelevant and the
333	 * state had better already be in the pcb.  This is true for forks
334	 * but not for dumps (the old book-keeping with FP flags in the pcb
335	 * always lost for dumps because the dump pcb has 0 flags).
336	 *
337	 * If npxthread != NULL, then we have to save the npx h/w state to
338	 * npxthread's pcb and copy it to the requested pcb, or save to the
339	 * requested pcb and reload.  Copying is easier because we would
340	 * have to handle h/w bugs for reloading.  We used to lose the
341	 * parent's npx state for forks by forgetting to reload.
342	 */
343	pushfl
344	cli
345	movl	PCPU(NPXTHREAD),%eax
346	testl	%eax,%eax
347	je	1f
348
349	pushl	%ecx
350	movl	TD_PCB(%eax),%eax
351	leal	PCB_SAVEFPU(%eax),%eax
352	pushl	%eax
353	pushl	%eax
354	call	npxsave
355	addl	$4,%esp
356	popl	%eax
357	popl	%ecx
358
359	pushl	$PCB_SAVEFPU_SIZE
360	leal	PCB_SAVEFPU(%ecx),%ecx
361	pushl	%ecx
362	pushl	%eax
363	call	bcopy
364	addl	$12,%esp
3651:
366	popfl
367#endif	/* DEV_NPX */
368
369	ret
370