xref: /freebsd/sys/amd64/amd64/cpu_switch.S (revision 9869fa1d)
10967373eSDavid Greenman/*-
20967373eSDavid Greenman * Copyright (c) 1990 The Regents of the University of California.
30967373eSDavid Greenman * All rights reserved.
40967373eSDavid Greenman *
50967373eSDavid Greenman * This code is derived from software contributed to Berkeley by
60967373eSDavid Greenman * William Jolitz.
70967373eSDavid Greenman *
80967373eSDavid Greenman * Redistribution and use in source and binary forms, with or without
90967373eSDavid Greenman * modification, are permitted provided that the following conditions
100967373eSDavid Greenman * are met:
110967373eSDavid Greenman * 1. Redistributions of source code must retain the above copyright
120967373eSDavid Greenman *    notice, this list of conditions and the following disclaimer.
130967373eSDavid Greenman * 2. Redistributions in binary form must reproduce the above copyright
140967373eSDavid Greenman *    notice, this list of conditions and the following disclaimer in the
150967373eSDavid Greenman *    documentation and/or other materials provided with the distribution.
160967373eSDavid Greenman * 3. All advertising materials mentioning features or use of this software
170967373eSDavid Greenman *    must display the following acknowledgement:
180967373eSDavid Greenman *	This product includes software developed by the University of
190967373eSDavid Greenman *	California, Berkeley and its contributors.
200967373eSDavid Greenman * 4. Neither the name of the University nor the names of its contributors
210967373eSDavid Greenman *    may be used to endorse or promote products derived from this software
220967373eSDavid Greenman *    without specific prior written permission.
230967373eSDavid Greenman *
240967373eSDavid Greenman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
250967373eSDavid Greenman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
260967373eSDavid Greenman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
270967373eSDavid Greenman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
280967373eSDavid Greenman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
290967373eSDavid Greenman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
300967373eSDavid Greenman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
310967373eSDavid Greenman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
320967373eSDavid Greenman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
330967373eSDavid Greenman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
340967373eSDavid Greenman * SUCH DAMAGE.
350967373eSDavid Greenman *
36c3aac50fSPeter Wemm * $FreeBSD$
370967373eSDavid Greenman */
380967373eSDavid Greenman
39558226eaSPeter Wemm#include "opt_npx.h"
400967373eSDavid Greenman
410513ce7fSBruce Evans#include <machine/asmacros.h>
42477a642cSPeter Wemm
43665bb8faSSteve Passe#ifdef SMP
44b3196e4bSPeter Wemm#include <machine/apic.h>
45163fd6fbSJohn Baldwin#include <machine/smptests.h>			/* CHEAP_TPR, GRAB_LOPRIO */
469869fa1dSJohn Baldwin#endif
470513ce7fSBruce Evans
480513ce7fSBruce Evans#include "assym.s"
490513ce7fSBruce Evans
500967373eSDavid Greenman/*****************************************************************************/
510967373eSDavid Greenman/* Scheduling                                                                */
520967373eSDavid Greenman/*****************************************************************************/
530967373eSDavid Greenman
540967373eSDavid Greenman	.data
55665bb8faSSteve Passe
5602318dacSJake Burkholder	.globl	panic
570967373eSDavid Greenman
5882566551SJohn Dyson#if defined(SWTCH_OPTIM_STATS)
5902318dacSJake Burkholder	.globl	swtch_optim_stats, tlb_flush_count
6002318dacSJake Burkholderswtch_optim_stats:	.long	0		/* number of _swtch_optims */
6102318dacSJake Burkholdertlb_flush_count:	.long	0
6282566551SJohn Dyson#endif
630967373eSDavid Greenman
640967373eSDavid Greenman	.text
650967373eSDavid Greenman
660967373eSDavid Greenman/*
670384fff8SJason Evans * cpu_throw()
680384fff8SJason Evans */
690384fff8SJason EvansENTRY(cpu_throw)
700384fff8SJason Evans	jmp	sw1
710384fff8SJason Evans
720384fff8SJason Evans/*
7326f9a767SRodney W. Grimes * cpu_switch()
740967373eSDavid Greenman */
7526f9a767SRodney W. GrimesENTRY(cpu_switch)
76477a642cSPeter Wemm
779869fa1dSJohn Baldwin	/* Switch to new thread.  First, save context as needed. */
78b40ce416SJulian Elischer	movl	PCPU(CURTHREAD),%ecx
790967373eSDavid Greenman
809869fa1dSJohn Baldwin	/* If no thread to save, don't save it (XXX shouldn't happen). */
810967373eSDavid Greenman	testl	%ecx,%ecx
820384fff8SJason Evans	jz	sw1
83163fd6fbSJohn Baldwin
84b40ce416SJulian Elischer	movl	TD_PROC(%ecx), %eax
85b40ce416SJulian Elischer	movl	P_VMSPACE(%eax), %edx
866d43764aSJake Burkholder	movl	PCPU(CPUID), %eax
87087e80a9SAlan Cox	btrl	%eax, VM_PMAP+PM_ACTIVE(%edx)
88477a642cSPeter Wemm
89b40ce416SJulian Elischer	movl	TD_PCB(%ecx),%edx
900967373eSDavid Greenman
911b00f920SJake Burkholder	movl	(%esp),%eax			/* Hardware registers */
921b00f920SJake Burkholder	movl	%eax,PCB_EIP(%edx)
93087e80a9SAlan Cox	movl	%ebx,PCB_EBX(%edx)
94087e80a9SAlan Cox	movl	%esp,PCB_ESP(%edx)
95087e80a9SAlan Cox	movl	%ebp,PCB_EBP(%edx)
96087e80a9SAlan Cox	movl	%esi,PCB_ESI(%edx)
97087e80a9SAlan Cox	movl	%edi,PCB_EDI(%edx)
98087e80a9SAlan Cox	movl	%gs,PCB_GS(%edx)
990967373eSDavid Greenman
100163fd6fbSJohn Baldwin	/* Test if debug registers should be saved. */
101ab001a72SJonathan Lemon	movb    PCB_FLAGS(%edx),%al
102ab001a72SJonathan Lemon	andb    $PCB_DBREGS,%al
103ab001a72SJonathan Lemon	jz      1f                              /* no, skip over */
104ab001a72SJonathan Lemon	movl    %dr7,%eax                       /* yes, do the save */
105ab001a72SJonathan Lemon	movl    %eax,PCB_DR7(%edx)
1063ad234d4SBrian S. Dean	andl    $0x0000fc00, %eax               /* disable all watchpoints */
107ab001a72SJonathan Lemon	movl    %eax,%dr7
108ab001a72SJonathan Lemon	movl    %dr6,%eax
109ab001a72SJonathan Lemon	movl    %eax,PCB_DR6(%edx)
110ab001a72SJonathan Lemon	movl    %dr3,%eax
111ab001a72SJonathan Lemon	movl    %eax,PCB_DR3(%edx)
112ab001a72SJonathan Lemon	movl    %dr2,%eax
113ab001a72SJonathan Lemon	movl    %eax,PCB_DR2(%edx)
114ab001a72SJonathan Lemon	movl    %dr1,%eax
115ab001a72SJonathan Lemon	movl    %eax,PCB_DR1(%edx)
116ab001a72SJonathan Lemon	movl    %dr0,%eax
117ab001a72SJonathan Lemon	movl    %eax,PCB_DR0(%edx)
118ab001a72SJonathan Lemon1:
119ab001a72SJonathan Lemon
120477a642cSPeter Wemm#ifdef SMP
121da9f0182SSteve Passe	/* XXX FIXME: we should be saving the local APIC TPR */
1229869fa1dSJohn Baldwin#endif
123477a642cSPeter Wemm
124558226eaSPeter Wemm#ifdef DEV_NPX
1250967373eSDavid Greenman	/* have we used fp, and need a save? */
126b40ce416SJulian Elischer	cmpl	%ecx,PCPU(NPXTHREAD)
1270967373eSDavid Greenman	jne	1f
128087e80a9SAlan Cox	addl	$PCB_SAVEFPU,%edx		/* h/w bugs make saving complicated */
129087e80a9SAlan Cox	pushl	%edx
13002318dacSJake Burkholder	call	npxsave				/* do it in a big C function */
1310967373eSDavid Greenman	popl	%eax
1320967373eSDavid Greenman1:
1339869fa1dSJohn Baldwin#endif
1340967373eSDavid Greenman
135163fd6fbSJohn Baldwin	/* Save is done.  Now choose a new thread. */
136163fd6fbSJohn Baldwin	/* XXX still trashing space above the old "Top Of Stack". */
1370967373eSDavid Greenmansw1:
1389a3b3e8bSPeter Wemm
1399a3b3e8bSPeter Wemm#ifdef SMP
1409869fa1dSJohn Baldwin	/*
1419869fa1dSJohn Baldwin	 * Stop scheduling if smp_active has become zero (for rebooting) and
1429869fa1dSJohn Baldwin	 * we are not the BSP.
1439869fa1dSJohn Baldwin	 */
14402318dacSJake Burkholder	cmpl	$0,smp_active
1459a3b3e8bSPeter Wemm	jne	1f
1466d43764aSJake Burkholder	cmpl	$0,PCPU(CPUID)
1470384fff8SJason Evans	je	1f
148b40ce416SJulian Elischer	movl	PCPU(IDLETHREAD), %eax
149163fd6fbSJohn Baldwin	jmp	sw1b
1509a3b3e8bSPeter Wemm1:
1519a3b3e8bSPeter Wemm#endif
1529a3b3e8bSPeter Wemm
1530384fff8SJason Evans	/*
154163fd6fbSJohn Baldwin	 * Choose a new thread to schedule.  choosethread() returns idlethread
155163fd6fbSJohn Baldwin	 * if it cannot find another thread to run.
1560384fff8SJason Evans	 */
1579869fa1dSJohn Baldwin	call	choosethread			/* Trash ecx, edx; ret eax. */
1580384fff8SJason Evans
159835a748fSJohn Baldwin#ifdef INVARIANTS
160163fd6fbSJohn Baldwin	testl	%eax,%eax			/* no thread? */
1610384fff8SJason Evans	jz	badsw3				/* no, panic */
1620384fff8SJason Evans#endif
163163fd6fbSJohn Baldwin
1640384fff8SJason Evanssw1b:
16528f31ccfSPeter Wemm	movl	%eax,%ecx
1667216391eSDavid Greenman
167835a748fSJohn Baldwin#ifdef	INVARIANTS
168b40ce416SJulian Elischer	movl	TD_PROC(%ecx), %eax		/* XXXKSE */
169b40ce416SJulian Elischer	cmpb	$SRUN,P_STAT(%eax)
170b3196e4bSPeter Wemm	jne	badsw2
1710967373eSDavid Greenman#endif
1720967373eSDavid Greenman
173b40ce416SJulian Elischer	movl	TD_PCB(%ecx),%edx
1740967373eSDavid Greenman
17582566551SJohn Dyson#if defined(SWTCH_OPTIM_STATS)
17602318dacSJake Burkholder	incl	swtch_optim_stats
17782566551SJohn Dyson#endif
178b40ce416SJulian Elischer
1790967373eSDavid Greenman	/* switch address space */
18082566551SJohn Dyson	movl	%cr3,%ebx
18182566551SJohn Dyson	cmpl	PCB_CR3(%edx),%ebx
18282566551SJohn Dyson	je	4f
18382566551SJohn Dyson#if defined(SWTCH_OPTIM_STATS)
18402318dacSJake Burkholder	decl	swtch_optim_stats
18502318dacSJake Burkholder	incl	tlb_flush_count
18682566551SJohn Dyson#endif
18782566551SJohn Dyson	movl	PCB_CR3(%edx),%ebx
1889869fa1dSJohn Baldwin	movl	%ebx,%cr3
18982566551SJohn Dyson4:
190163fd6fbSJohn Baldwin
1916d43764aSJake Burkholder	movl	PCPU(CPUID), %esi
19248a09cf2SJohn Dyson	cmpl	$0, PCB_EXT(%edx)		/* has pcb extension? */
19348a09cf2SJohn Dyson	je	1f
19402318dacSJake Burkholder	btsl	%esi, private_tss		/* mark use of private tss */
19548a09cf2SJohn Dyson	movl	PCB_EXT(%edx), %edi		/* new tss descriptor */
19648a09cf2SJohn Dyson	jmp	2f
19748a09cf2SJohn Dyson1:
1989869fa1dSJohn Baldwin
1999869fa1dSJohn Baldwin	/* Update common_tss.tss_esp0 pointer. */
2002f01a0c0SPeter Wemm	leal	-16(%edx), %ebx			/* leave space for vm86 */
2012f01a0c0SPeter Wemm	movl	%ebx, PCPU(COMMON_TSS) + TSS_ESP0 /* stack is below pcb */
202163fd6fbSJohn Baldwin
20302318dacSJake Burkholder	btrl	%esi, private_tss
2045206bca1SLuoqi Chen	jae	3f
20541ed17bfSJake Burkholder	PCPU_ADDR(COMMON_TSSD, %edi)
20648a09cf2SJohn Dyson2:
207163fd6fbSJohn Baldwin	/* Move correct tss descriptor into GDT slot, then reload tr. */
2086d43764aSJake Burkholder	movl	PCPU(TSS_GDT), %ebx		/* entry in GDT */
20948a09cf2SJohn Dyson	movl	0(%edi), %eax
21048a09cf2SJohn Dyson	movl	%eax, 0(%ebx)
21148a09cf2SJohn Dyson	movl	4(%edi), %eax
21248a09cf2SJohn Dyson	movl	%eax, 4(%ebx)
2130f0fe5a4SLuoqi Chen	movl	$GPROC0_SEL*8, %esi		/* GSEL(entry, SEL_KPL) */
21448a09cf2SJohn Dyson	ltr	%si
21548a09cf2SJohn Dyson3:
2169869fa1dSJohn Baldwin	/* Note in vmspace that this cpu is using it. */
217163fd6fbSJohn Baldwin	movl	TD_PROC(%ecx),%eax		/* XXXKSE proc from thread */
218163fd6fbSJohn Baldwin	movl	P_VMSPACE(%eax), %ebx
2196d43764aSJake Burkholder	movl	PCPU(CPUID), %eax
220087e80a9SAlan Cox	btsl	%eax, VM_PMAP+PM_ACTIVE(%ebx)
221a2a1c95cSPeter Wemm
2229869fa1dSJohn Baldwin	/* Restore context. */
2230967373eSDavid Greenman	movl	PCB_EBX(%edx),%ebx
2240967373eSDavid Greenman	movl	PCB_ESP(%edx),%esp
2250967373eSDavid Greenman	movl	PCB_EBP(%edx),%ebp
2260967373eSDavid Greenman	movl	PCB_ESI(%edx),%esi
2270967373eSDavid Greenman	movl	PCB_EDI(%edx),%edi
2281b00f920SJake Burkholder	movl	PCB_EIP(%edx),%eax
2291b00f920SJake Burkholder	movl	%eax,(%esp)
2300967373eSDavid Greenman
2319869fa1dSJohn Baldwin#if defined(SMP) && defined(GRAP_LOPRIO)
2329869fa1dSJohn Baldwin	/* Hold LOPRIO for interrupts. */
233570dbb53SSteve Passe#ifdef CHEAP_TPR
23402318dacSJake Burkholder	movl	$0, lapic+LA_TPR
235570dbb53SSteve Passe#else
23602318dacSJake Burkholder	andl	$~APIC_TPR_PRIO, lapic+LA_TPR
2379869fa1dSJohn Baldwin#endif
2389869fa1dSJohn Baldwin#endif
2396d43764aSJake Burkholder	movl	%edx, PCPU(CURPCB)
240163fd6fbSJohn Baldwin	movl	%ecx, PCPU(CURTHREAD)		/* into next thread */
241477a642cSPeter Wemm
242477a642cSPeter Wemm#ifdef SMP
243da9f0182SSteve Passe	/* XXX FIXME: we should be restoring the local APIC TPR */
2449869fa1dSJohn Baldwin#endif
2450967373eSDavid Greenman
2469869fa1dSJohn Baldwin	/*
2479869fa1dSJohn Baldwin	 * Determine the LDT to use and load it if is the default one and
2489869fa1dSJohn Baldwin	 * that is not the current one.
2499869fa1dSJohn Baldwin	 */
250163fd6fbSJohn Baldwin	movl	TD_PROC(%ecx),%eax
2519869fa1dSJohn Baldwin	cmpl    $0,P_MD+MD_LDT(%eax)
2529869fa1dSJohn Baldwin	jnz	1f
2539869fa1dSJohn Baldwin	movl	_default_ldt,%eax
254163fd6fbSJohn Baldwin	cmpl	PCPU(CURRENTLDT),%eax
255163fd6fbSJohn Baldwin	je	2f
256163fd6fbSJohn Baldwin	lldt	_default_ldt
257163fd6fbSJohn Baldwin	movl	%eax,PCPU(CURRENTLDT)
258da59a31cSDavid Greenman	jmp	2f
2599869fa1dSJohn Baldwin1:
2609869fa1dSJohn Baldwin	/* Load the LDT when it is not the default one. */
2619869fa1dSJohn Baldwin	pushl	%edx				/* Preserve pointer to pcb. */
2627df8a724SJohn Baldwin	addl	$P_MD,%eax			/* Pointer to mdproc is arg. */
2637df8a724SJohn Baldwin	pushl	%eax
2649869fa1dSJohn Baldwin	call	set_user_ldt
2657df8a724SJohn Baldwin	addl	$4,%esp
266163fd6fbSJohn Baldwin	popl	%edx
267da59a31cSDavid Greenman2:
268da59a31cSDavid Greenman
2697b3c8424SBruce Evans	/* This must be done after loading the user LDT. */
2707b3c8424SBruce Evans	.globl	cpu_switch_load_gs
2717b3c8424SBruce Evanscpu_switch_load_gs:
2727b3c8424SBruce Evans	movl	PCB_GS(%edx),%gs
2737b3c8424SBruce Evans
2749869fa1dSJohn Baldwin	/* Test if debug regisers should be restored. */
275ab001a72SJonathan Lemon	movb    PCB_FLAGS(%edx),%al
276ab001a72SJonathan Lemon	andb    $PCB_DBREGS,%al
2779869fa1dSJohn Baldwin	jz      1f
2789869fa1dSJohn Baldwin
2799869fa1dSJohn Baldwin	/*
2809869fa1dSJohn Baldwin	 * Restore debug registers.  The special code for dr7 is to
2819869fa1dSJohn Baldwin	 * preserve the current values of its reserved bits.
2829869fa1dSJohn Baldwin	 */
2839869fa1dSJohn Baldwin	movl    PCB_DR6(%edx),%eax
284ab001a72SJonathan Lemon	movl    %eax,%dr6
285ab001a72SJonathan Lemon	movl    PCB_DR3(%edx),%eax
286ab001a72SJonathan Lemon	movl    %eax,%dr3
287ab001a72SJonathan Lemon	movl    PCB_DR2(%edx),%eax
288ab001a72SJonathan Lemon	movl    %eax,%dr2
289ab001a72SJonathan Lemon	movl    PCB_DR1(%edx),%eax
290ab001a72SJonathan Lemon	movl    %eax,%dr1
291ab001a72SJonathan Lemon	movl    PCB_DR0(%edx),%eax
292ab001a72SJonathan Lemon	movl    %eax,%dr0
2939869fa1dSJohn Baldwin	movl	%dr7,%eax
2949869fa1dSJohn Baldwin	andl    $0x0000fc00,%eax
29502c41f11SJohn Baldwin	movl    PCB_DR7(%edx),%ecx
2969869fa1dSJohn Baldwin	andl	$~0x0000fc00,%ecx
29702c41f11SJohn Baldwin	orl     %ecx,%eax
298ab001a72SJonathan Lemon	movl    %eax,%dr7
299ab001a72SJonathan Lemon1:
3000967373eSDavid Greenman	ret
3010967373eSDavid Greenman
302835a748fSJohn Baldwin#ifdef INVARIANTS
303b3196e4bSPeter Wemmbadsw2:
304b3196e4bSPeter Wemm	pushl	$sw0_2
30502318dacSJake Burkholder	call	panic
306b3196e4bSPeter Wemm
307b3196e4bSPeter Wemmsw0_2:	.asciz	"cpu_switch: not SRUN"
308b3196e4bSPeter Wemm
3090384fff8SJason Evansbadsw3:
3100384fff8SJason Evans	pushl	$sw0_3
31102318dacSJake Burkholder	call	panic
312b3196e4bSPeter Wemm
313163fd6fbSJohn Baldwinsw0_3:	.asciz	"cpu_switch: choosethread returned NULL"
3140384fff8SJason Evans#endif
315ebd707d3SBruce Evans
3160967373eSDavid Greenman/*
3172924d491SDavid Greenman * savectx(pcb)
3182924d491SDavid Greenman * Update pcb, saving current processor state.
3190967373eSDavid Greenman */
3200967373eSDavid GreenmanENTRY(savectx)
3219869fa1dSJohn Baldwin	/* Fetch PCB. */
3220967373eSDavid Greenman	movl	4(%esp),%ecx
3232924d491SDavid Greenman
3249869fa1dSJohn Baldwin	/* Save caller's return address.  Child won't execute this routine. */
3250967373eSDavid Greenman	movl	(%esp),%eax
3260967373eSDavid Greenman	movl	%eax,PCB_EIP(%ecx)
3272924d491SDavid Greenman
328ac5f943cSPeter Wemm	movl	%cr3,%eax
329ac5f943cSPeter Wemm	movl	%eax,PCB_CR3(%ecx)
330ac5f943cSPeter Wemm
3310967373eSDavid Greenman	movl	%ebx,PCB_EBX(%ecx)
332ac474627SDavid Greenman	movl	%esp,PCB_ESP(%ecx)
3330967373eSDavid Greenman	movl	%ebp,PCB_EBP(%ecx)
3340967373eSDavid Greenman	movl	%esi,PCB_ESI(%ecx)
3350967373eSDavid Greenman	movl	%edi,PCB_EDI(%ecx)
3367b3c8424SBruce Evans	movl	%gs,PCB_GS(%ecx)
3370967373eSDavid Greenman
338558226eaSPeter Wemm#ifdef DEV_NPX
3390967373eSDavid Greenman	/*
340b40ce416SJulian Elischer	 * If npxthread == NULL, then the npx h/w state is irrelevant and the
3410967373eSDavid Greenman	 * state had better already be in the pcb.  This is true for forks
3420967373eSDavid Greenman	 * but not for dumps (the old book-keeping with FP flags in the pcb
3430967373eSDavid Greenman	 * always lost for dumps because the dump pcb has 0 flags).
3440967373eSDavid Greenman	 *
345b40ce416SJulian Elischer	 * If npxthread != NULL, then we have to save the npx h/w state to
346b40ce416SJulian Elischer	 * npxthread's pcb and copy it to the requested pcb, or save to the
3470967373eSDavid Greenman	 * requested pcb and reload.  Copying is easier because we would
3480967373eSDavid Greenman	 * have to handle h/w bugs for reloading.  We used to lose the
3490967373eSDavid Greenman	 * parent's npx state for forks by forgetting to reload.
3500967373eSDavid Greenman	 */
351c2b095abSBruce Evans	pushfl
352c2b095abSBruce Evans	cli
353b40ce416SJulian Elischer	movl	PCPU(NPXTHREAD),%eax
3540967373eSDavid Greenman	testl	%eax,%eax
3550967373eSDavid Greenman	je	1f
3560967373eSDavid Greenman
3570967373eSDavid Greenman	pushl	%ecx
358b40ce416SJulian Elischer	movl	TD_PCB(%eax),%eax
3590967373eSDavid Greenman	leal	PCB_SAVEFPU(%eax),%eax
3600967373eSDavid Greenman	pushl	%eax
3610967373eSDavid Greenman	pushl	%eax
36202318dacSJake Burkholder	call	npxsave
363eabe0f9fSBruce Evans	addl	$4,%esp
3640967373eSDavid Greenman	popl	%eax
3650967373eSDavid Greenman	popl	%ecx
3660967373eSDavid Greenman
3670513ce7fSBruce Evans	pushl	$PCB_SAVEFPU_SIZE
3680967373eSDavid Greenman	leal	PCB_SAVEFPU(%ecx),%ecx
3690967373eSDavid Greenman	pushl	%ecx
3700967373eSDavid Greenman	pushl	%eax
37102318dacSJake Burkholder	call	bcopy
3720967373eSDavid Greenman	addl	$12,%esp
373c2b095abSBruce Evans1:
374c2b095abSBruce Evans	popfl
375558226eaSPeter Wemm#endif	/* DEV_NPX */
3760967373eSDavid Greenman
3770967373eSDavid Greenman	ret
378