xref: /freebsd/sys/i386/i386/swtch.S (revision 61e21613)
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. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "opt_sched.h"
34
35#include <machine/asmacros.h>
36
37#include "assym.inc"
38
39#if defined(SMP) && defined(SCHED_ULE)
40#define	SETOP		xchgl
41#define	BLOCK_SPIN(reg)							\
42		movl		$blocked_lock,%eax ;			\
43	100: ;								\
44		lock ;							\
45		cmpxchgl	%eax,TD_LOCK(reg) ;			\
46		jne		101f ;					\
47		pause ;							\
48		jmp		100b ;					\
49	101:
50#else
51#define	SETOP		movl
52#define	BLOCK_SPIN(reg)
53#endif
54
55/*****************************************************************************/
56/* Scheduling                                                                */
57/*****************************************************************************/
58
59	.text
60
61/*
62 * cpu_throw()
63 *
64 * This is the second half of cpu_switch(). It is used when the current
65 * thread is either a dummy or slated to die, and we no longer care
66 * about its state.  This is only a slight optimization and is probably
67 * not worth it anymore.  Note that we need to clear the pm_active bits so
68 * we do need the old proc if it still exists.
69 * 0(%esp) = ret
70 * 4(%esp) = oldtd
71 * 8(%esp) = newtd
72 */
73ENTRY(cpu_throw)
74	movl	PCPU(CPUID), %esi
75	/* release bit from old pm_active */
76	movl	PCPU(CURPMAP), %ebx
77#ifdef SMP
78	lock
79#endif
80	btrl	%esi, PM_ACTIVE(%ebx)		/* clear old */
81	movl	8(%esp),%ecx			/* New thread */
82	movl	TD_PCB(%ecx),%edx
83	/* set bit in new pm_active */
84	movl	TD_PROC(%ecx),%eax
85	movl	P_VMSPACE(%eax), %ebx
86	addl	$VM_PMAP, %ebx
87	movl	%ebx, PCPU(CURPMAP)
88#ifdef SMP
89	lock
90#endif
91	btsl	%esi, PM_ACTIVE(%ebx)		/* set new */
92	jmp	sw1
93END(cpu_throw)
94
95/*
96 * cpu_switch(old, new)
97 *
98 * Save the current thread state, then select the next thread to run
99 * and load its state.
100 * 0(%esp) = ret
101 * 4(%esp) = oldtd
102 * 8(%esp) = newtd
103 * 12(%esp) = newlock
104 */
105ENTRY(cpu_switch)
106
107	/* Switch to new thread.  First, save context. */
108	movl	4(%esp),%ecx
109
110#ifdef INVARIANTS
111	testl	%ecx,%ecx			/* no thread? */
112	jz	badsw2				/* no, panic */
113#endif
114
115	movl	TD_PCB(%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	mov	%gs,PCB_GS(%edx)
125	/* Test if debug registers should be saved. */
126	testl	$PCB_DBREGS,PCB_FLAGS(%edx)
127	jz      1f                              /* no, skip over */
128	movl    %dr7,%eax                       /* yes, do the save */
129	movl    %eax,PCB_DR7(%edx)
130	andl    $0x0000fc00, %eax               /* disable all watchpoints */
131	movl    %eax,%dr7
132	movl    %dr6,%eax
133	movl    %eax,PCB_DR6(%edx)
134	movl    %dr3,%eax
135	movl    %eax,PCB_DR3(%edx)
136	movl    %dr2,%eax
137	movl    %eax,PCB_DR2(%edx)
138	movl    %dr1,%eax
139	movl    %eax,PCB_DR1(%edx)
140	movl    %dr0,%eax
141	movl    %eax,PCB_DR0(%edx)
1421:
143
144	/* have we used fp, and need a save? */
145	cmpl	%ecx,PCPU(FPCURTHREAD)
146	jne	1f
147	pushl	PCB_SAVEFPU(%edx)		/* h/w bugs make saving complicated */
148	call	npxsave				/* do it in a big C function */
149	popl	%eax
1501:
151
152	/* Save is done.  Now fire up new thread. */
153	movl	4(%esp),%edi
154	movl	8(%esp),%ecx			/* New thread */
155	movl	12(%esp),%esi			/* New lock */
156#ifdef INVARIANTS
157	testl	%ecx,%ecx			/* no thread? */
158	jz	badsw3				/* no, panic */
159#endif
160	movl	TD_PCB(%ecx),%edx
161
162	/* Switchout td_lock */
163	movl	%esi,%eax
164	movl	PCPU(CPUID),%esi
165	SETOP	%eax,TD_LOCK(%edi)
166
167	/* Release bit from old pmap->pm_active */
168	movl	PCPU(CURPMAP), %ebx
169#ifdef SMP
170	lock
171#endif
172	btrl	%esi, PM_ACTIVE(%ebx)		/* clear old */
173
174	/* Set bit in new pmap->pm_active */
175	movl	TD_PROC(%ecx),%eax		/* newproc */
176	movl	P_VMSPACE(%eax), %ebx
177	addl	$VM_PMAP, %ebx
178	movl	%ebx, PCPU(CURPMAP)
179#ifdef SMP
180	lock
181#endif
182	btsl	%esi, PM_ACTIVE(%ebx)		/* set new */
183sw1:
184	BLOCK_SPIN(%ecx)
185	/*
186	 * At this point, we have managed thread locks and are ready
187	 * to load up the rest of the next context.
188	 */
189
190	/* Load a pointer to the thread kernel stack into PCPU. */
191	leal	-VM86_STACK_SPACE(%edx), %eax	/* leave space for vm86 */
192	movl	%eax, PCPU(KESP0)
193
194	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
195	je	1f				/* If not, use the default */
196	movl	$1, PCPU(PRIVATE_TSS) 		/* mark use of private tss */
197	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
198	movl	PCPU(TRAMPSTK), %ebx
199	movl	%ebx, PCB_EXT_TSS+TSS_ESP0(%edi)
200	jmp	2f				/* Load it up */
201
2021:	/*
203	 * Use the common default TSS instead of our own.
204	 * Stack pointer in the common TSS points to the trampoline stack
205	 * already and should be not changed.
206	 *
207	 * Test this CPU's flag to see if this CPU was using a private TSS.
208	 */
209	cmpl	$0, PCPU(PRIVATE_TSS)		/* Already using the common? */
210	je	3f				/* if so, skip reloading */
211	movl	$0, PCPU(PRIVATE_TSS)
212	PCPU_ADDR(COMMON_TSSD, %edi)
2132:
214	/* Move correct tss descriptor into GDT slot, then reload tr. */
215	movl	PCPU(TSS_GDT), %ebx		/* entry in GDT */
216	movl	0(%edi), %eax
217	movl	4(%edi), %esi
218	movl	%eax, 0(%ebx)
219	movl	%esi, 4(%ebx)
220	movl	$GPROC0_SEL*8, %esi		/* GSEL(GPROC0_SEL, SEL_KPL) */
221	ltr	%si
2223:
223
224	/* Copy the %fs and %gs selectors into this pcpu gdt */
225	leal	PCB_FSD(%edx), %esi
226	movl	PCPU(FSGS_GDT), %edi
227	movl	0(%esi), %eax		/* %fs selector */
228	movl	4(%esi), %ebx
229	movl	%eax, 0(%edi)
230	movl	%ebx, 4(%edi)
231	movl	8(%esi), %eax		/* %gs selector, comes straight after */
232	movl	12(%esi), %ebx
233	movl	%eax, 8(%edi)
234	movl	%ebx, 12(%edi)
235
236	/* Restore context. */
237	movl	PCB_EBX(%edx),%ebx
238	movl	PCB_ESP(%edx),%esp
239	movl	PCB_EBP(%edx),%ebp
240	movl	PCB_ESI(%edx),%esi
241	movl	PCB_EDI(%edx),%edi
242	movl	PCB_EIP(%edx),%eax
243	movl	%eax,(%esp)
244
245	movl	%edx, PCPU(CURPCB)
246	movl	%ecx, PCPU(CURTHREAD)		/* into next thread */
247
248	/*
249	 * Determine the LDT to use and load it if is the default one and
250	 * that is not the current one.
251	 */
252	movl	TD_PROC(%ecx),%eax
253	cmpl    $0,P_MD+MD_LDT(%eax)
254	jnz	1f
255	movl	_default_ldt,%eax
256	cmpl	PCPU(CURRENTLDT),%eax
257	je	2f
258	lldt	_default_ldt
259	movl	%eax,PCPU(CURRENTLDT)
260	jmp	2f
2611:
262	/* Load the LDT when it is not the default one. */
263	pushl	%edx				/* Preserve pointer to pcb. */
264	addl	$P_MD,%eax			/* Pointer to mdproc is arg. */
265	pushl	%eax
266	/*
267	 * Holding dt_lock prevents context switches, so dt_lock cannot
268	 * be held now and set_user_ldt() will not deadlock acquiring it.
269	 */
270	call	set_user_ldt
271	addl	$4,%esp
272	popl	%edx
2732:
274
275	/* This must be done after loading the user LDT. */
276	.globl	cpu_switch_load_gs
277cpu_switch_load_gs:
278	mov	PCB_GS(%edx),%gs
279
280	pushl	%edx
281	pushl	PCPU(CURTHREAD)
282	call	npxswitch
283	popl	%edx
284	popl	%edx
285
286	/* Test if debug registers should be restored. */
287	testl	$PCB_DBREGS,PCB_FLAGS(%edx)
288	jz      1f
289
290	/*
291	 * Restore debug registers.  The special code for dr7 is to
292	 * preserve the current values of its reserved bits.
293	 */
294	movl    PCB_DR6(%edx),%eax
295	movl    %eax,%dr6
296	movl    PCB_DR3(%edx),%eax
297	movl    %eax,%dr3
298	movl    PCB_DR2(%edx),%eax
299	movl    %eax,%dr2
300	movl    PCB_DR1(%edx),%eax
301	movl    %eax,%dr1
302	movl    PCB_DR0(%edx),%eax
303	movl    %eax,%dr0
304	movl	%dr7,%eax
305	andl    $0x0000fc00,%eax
306	movl    PCB_DR7(%edx),%ecx
307	andl	$~0x0000fc00,%ecx
308	orl     %ecx,%eax
309	movl    %eax,%dr7
3101:
311	ret
312
313#ifdef INVARIANTS
314badsw1:
315	pushal
316	pushl	$sw0_1
317	call	panic
318sw0_1:	.asciz	"cpu_throw: no newthread supplied"
319
320badsw2:
321	pushal
322	pushl	$sw0_2
323	call	panic
324sw0_2:	.asciz	"cpu_switch: no curthread supplied"
325
326badsw3:
327	pushal
328	pushl	$sw0_3
329	call	panic
330sw0_3:	.asciz	"cpu_switch: no newthread supplied"
331#endif
332END(cpu_switch)
333
334/*
335 * savectx(pcb)
336 * Update pcb, saving current processor state.
337 */
338ENTRY(savectx)
339	/* Fetch PCB. */
340	movl	4(%esp),%ecx
341
342	/* Save caller's return address.  Child won't execute this routine. */
343	movl	(%esp),%eax
344	movl	%eax,PCB_EIP(%ecx)
345
346	movl	%cr3,%eax
347	movl	%eax,PCB_CR3(%ecx)
348
349	movl	%ebx,PCB_EBX(%ecx)
350	movl	%esp,PCB_ESP(%ecx)
351	movl	%ebp,PCB_EBP(%ecx)
352	movl	%esi,PCB_ESI(%ecx)
353	movl	%edi,PCB_EDI(%ecx)
354	mov	%gs,PCB_GS(%ecx)
355
356	movl	%cr0,%eax
357	movl	%eax,PCB_CR0(%ecx)
358	movl	%cr2,%eax
359	movl	%eax,PCB_CR2(%ecx)
360	movl	%cr4,%eax
361	movl	%eax,PCB_CR4(%ecx)
362
363	movl	%dr0,%eax
364	movl	%eax,PCB_DR0(%ecx)
365	movl	%dr1,%eax
366	movl	%eax,PCB_DR1(%ecx)
367	movl	%dr2,%eax
368	movl	%eax,PCB_DR2(%ecx)
369	movl	%dr3,%eax
370	movl	%eax,PCB_DR3(%ecx)
371	movl	%dr6,%eax
372	movl	%eax,PCB_DR6(%ecx)
373	movl	%dr7,%eax
374	movl	%eax,PCB_DR7(%ecx)
375
376	mov	%ds,PCB_DS(%ecx)
377	mov	%es,PCB_ES(%ecx)
378	mov	%fs,PCB_FS(%ecx)
379	mov	%ss,PCB_SS(%ecx)
380
381	sgdt	PCB_GDT(%ecx)
382	sidt	PCB_IDT(%ecx)
383	sldt	PCB_LDT(%ecx)
384	str	PCB_TR(%ecx)
385
386	movl	$1,%eax
387	ret
388END(savectx)
389
390/*
391 * resumectx(pcb) __fastcall
392 * Resuming processor state from pcb.
393 */
394ENTRY(resumectx)
395	/* Restore GDT. */
396	lgdt	PCB_GDT(%ecx)
397
398	/* Restore segment registers */
399	movzwl	PCB_DS(%ecx),%eax
400	mov	%ax,%ds
401	movzwl	PCB_ES(%ecx),%eax
402	mov	%ax,%es
403	movzwl	PCB_FS(%ecx),%eax
404	mov	%ax,%fs
405	movzwl	PCB_GS(%ecx),%eax
406	movw	%ax,%gs
407	movzwl	PCB_SS(%ecx),%eax
408	mov	%ax,%ss
409
410	/* Restore CR2, CR4, CR3 and CR0 */
411	movl	PCB_CR2(%ecx),%eax
412	movl	%eax,%cr2
413	movl	PCB_CR4(%ecx),%eax
414	movl	%eax,%cr4
415	movl	PCB_CR3(%ecx),%eax
416	movl	%eax,%cr3
417	movl	PCB_CR0(%ecx),%eax
418	movl	%eax,%cr0
419	jmp	1f
4201:
421
422	/* Restore descriptor tables */
423	lidt	PCB_IDT(%ecx)
424	lldt	PCB_LDT(%ecx)
425
426#define SDT_SYS386TSS	9
427#define SDT_SYS386BSY	11
428	/* Clear "task busy" bit and reload TR */
429	movl	PCPU(TSS_GDT),%eax
430	andb	$(~SDT_SYS386BSY | SDT_SYS386TSS),5(%eax)
431	movzwl	PCB_TR(%ecx),%eax
432	ltr	%ax
433#undef SDT_SYS386TSS
434#undef SDT_SYS386BSY
435
436	/* Restore debug registers */
437	movl	PCB_DR0(%ecx),%eax
438	movl	%eax,%dr0
439	movl	PCB_DR1(%ecx),%eax
440	movl	%eax,%dr1
441	movl	PCB_DR2(%ecx),%eax
442	movl	%eax,%dr2
443	movl	PCB_DR3(%ecx),%eax
444	movl	%eax,%dr3
445	movl	PCB_DR6(%ecx),%eax
446	movl	%eax,%dr6
447	movl	PCB_DR7(%ecx),%eax
448	movl	%eax,%dr7
449
450	/* Restore other registers */
451	movl	PCB_EDI(%ecx),%edi
452	movl	PCB_ESI(%ecx),%esi
453	movl	PCB_EBP(%ecx),%ebp
454	movl	PCB_ESP(%ecx),%esp
455	movl	PCB_EBX(%ecx),%ebx
456
457	/* reload code selector by turning return into intersegmental return */
458	pushl	PCB_EIP(%ecx)
459	movl	$KCSEL,4(%esp)
460	xorl	%eax,%eax
461	lret
462END(resumectx)
463