svn commit: r208833 - in head/sys: amd64/acpica amd64/amd64
amd64/ia32 amd64/include i386/i386 i386/include i386/isa
i386/linux pc98/pc98
Konstantin Belousov
kib at FreeBSD.org
Sat Jun 5 16:00:16 UTC 2010
Author: kib
Date: Sat Jun 5 15:59:59 2010
New Revision: 208833
URL: http://svn.freebsd.org/changeset/base/208833
Log:
Introduce the x86 kernel interfaces to allow kernel code to use
FPU/SSE hardware. Caller should provide a save area that is chained
into the stack of the areas; pcb save_area for usermode FPU state is
on top. The pcb now contains a pointer to the current FPU saved area,
used during FPUDNA handling and context switches. There is also a
facility to allow the kernel thread to use pcb save_area.
Change the dreaded warnings "npxdna in kernel mode!" into the panics
when FPU usage is not registered.
KPI discussed with: fabient
Tested by: pho, fabient
Hardware provided by: Sentex Communications
MFC after: 1 month
Modified:
head/sys/amd64/acpica/acpi_wakeup.c
head/sys/amd64/amd64/cpu_switch.S
head/sys/amd64/amd64/fpu.c
head/sys/amd64/amd64/machdep.c
head/sys/amd64/amd64/mp_machdep.c
head/sys/amd64/amd64/trap.c
head/sys/amd64/amd64/vm_machdep.c
head/sys/amd64/ia32/ia32_reg.c
head/sys/amd64/include/fpu.h
head/sys/amd64/include/pcb.h
head/sys/i386/i386/machdep.c
head/sys/i386/i386/ptrace_machdep.c
head/sys/i386/i386/swtch.s
head/sys/i386/i386/trap.c
head/sys/i386/i386/vm_machdep.c
head/sys/i386/include/npx.h
head/sys/i386/include/pcb.h
head/sys/i386/isa/npx.c
head/sys/i386/linux/linux_ptrace.c
head/sys/pc98/pc98/machdep.c
Modified: head/sys/amd64/acpica/acpi_wakeup.c
==============================================================================
--- head/sys/amd64/acpica/acpi_wakeup.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/acpica/acpi_wakeup.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -245,7 +245,7 @@ acpi_sleep_machdep(struct acpi_softc *sc
cr3 = rcr3();
load_cr3(KPML4phys);
- stopfpu = &stopxpcbs[0]->xpcb_pcb.pcb_save;
+ stopfpu = stopxpcbs[0]->xpcb_pcb.pcb_save;
if (acpi_savecpu(stopxpcbs[0])) {
fpugetregs(curthread, stopfpu);
Modified: head/sys/amd64/amd64/cpu_switch.S
==============================================================================
--- head/sys/amd64/amd64/cpu_switch.S Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/cpu_switch.S Sat Jun 5 15:59:59 2010 (r208833)
@@ -116,7 +116,7 @@ done_store_dr:
/* have we used fp, and need a save? */
cmpq %rdi,PCPU(FPCURTHREAD)
jne 1f
- addq $PCB_SAVEFPU,%r8
+ movq PCB_SAVEFPU(%r8),%r8
clts
fxsave (%r8)
smsw %ax
@@ -341,7 +341,7 @@ ENTRY(savectx)
je 1f
movq TD_PCB(%rax),%rdi
- leaq PCB_SAVEFPU(%rdi),%rdi
+ movq PCB_SAVEFPU(%rdi),%rdi
clts
fxsave (%rdi)
smsw %ax
@@ -349,7 +349,7 @@ ENTRY(savectx)
lmsw %ax
movq $PCB_SAVEFPU_SIZE,%rdx /* arg 3 */
- leaq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */
+ movq PCB_SAVEFPU(%rcx),%rsi /* arg 2 */
/* arg 1 (%rdi) already loaded */
call bcopy
1:
Modified: head/sys/amd64/amd64/fpu.c
==============================================================================
--- head/sys/amd64/amd64/fpu.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/fpu.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -91,8 +91,8 @@ void stop_emulating(void);
#endif /* __GNUCLIKE_ASM && !lint */
-#define GET_FPU_CW(thread) ((thread)->td_pcb->pcb_save.sv_env.en_cw)
-#define GET_FPU_SW(thread) ((thread)->td_pcb->pcb_save.sv_env.en_sw)
+#define GET_FPU_CW(thread) ((thread)->td_pcb->pcb_save->sv_env.en_cw)
+#define GET_FPU_SW(thread) ((thread)->td_pcb->pcb_save->sv_env.en_sw)
typedef u_char bool_t;
@@ -146,7 +146,7 @@ fpuexit(struct thread *td)
savecrit = intr_disable();
if (curthread == PCPU_GET(fpcurthread)) {
stop_emulating();
- fxsave(&PCPU_GET(curpcb)->pcb_save);
+ fxsave(PCPU_GET(curpcb)->pcb_save);
start_emulating();
PCPU_SET(fpcurthread, 0);
}
@@ -424,8 +424,10 @@ fpudna(void)
if (pcb->pcb_initial_fpucw != __INITIAL_FPUCW__)
fldcw(&pcb->pcb_initial_fpucw);
pcb->pcb_flags |= PCB_FPUINITDONE;
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_USERFPUINITDONE;
} else
- fxrstor(&pcb->pcb_save);
+ fxrstor(pcb->pcb_save);
intr_restore(s);
}
@@ -449,13 +451,39 @@ fpudrop()
* It returns the FPU ownership status.
*/
int
+fpugetuserregs(struct thread *td, struct savefpu *addr)
+{
+ register_t s;
+ struct pcb *pcb;
+
+ pcb = td->td_pcb;
+ if ((pcb->pcb_flags & PCB_USERFPUINITDONE) == 0) {
+ bcopy(&fpu_initialstate, addr, sizeof(fpu_initialstate));
+ addr->sv_env.en_cw = pcb->pcb_initial_fpucw;
+ return (_MC_FPOWNED_NONE);
+ }
+ s = intr_disable();
+ if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
+ fxsave(addr);
+ intr_restore(s);
+ return (_MC_FPOWNED_FPU);
+ } else {
+ intr_restore(s);
+ bcopy(&pcb->pcb_user_save, addr, sizeof(*addr));
+ return (_MC_FPOWNED_PCB);
+ }
+}
+
+int
fpugetregs(struct thread *td, struct savefpu *addr)
{
register_t s;
+ struct pcb *pcb;
- if ((td->td_pcb->pcb_flags & PCB_FPUINITDONE) == 0) {
+ pcb = td->td_pcb;
+ if ((pcb->pcb_flags & PCB_FPUINITDONE) == 0) {
bcopy(&fpu_initialstate, addr, sizeof(fpu_initialstate));
- addr->sv_env.en_cw = td->td_pcb->pcb_initial_fpucw;
+ addr->sv_env.en_cw = pcb->pcb_initial_fpucw;
return (_MC_FPOWNED_NONE);
}
s = intr_disable();
@@ -465,7 +493,7 @@ fpugetregs(struct thread *td, struct sav
return (_MC_FPOWNED_FPU);
} else {
intr_restore(s);
- bcopy(&td->td_pcb->pcb_save, addr, sizeof(*addr));
+ bcopy(pcb->pcb_save, addr, sizeof(*addr));
return (_MC_FPOWNED_PCB);
}
}
@@ -474,19 +502,44 @@ fpugetregs(struct thread *td, struct sav
* Set the state of the FPU.
*/
void
+fpusetuserregs(struct thread *td, struct savefpu *addr)
+{
+ register_t s;
+ struct pcb *pcb;
+
+ pcb = td->td_pcb;
+ s = intr_disable();
+ if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
+ fxrstor(addr);
+ intr_restore(s);
+ pcb->pcb_flags |= PCB_FPUINITDONE | PCB_USERFPUINITDONE;
+ } else {
+ intr_restore(s);
+ bcopy(addr, &td->td_pcb->pcb_user_save, sizeof(*addr));
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_FPUINITDONE;
+ pcb->pcb_flags |= PCB_USERFPUINITDONE;
+ }
+}
+
+void
fpusetregs(struct thread *td, struct savefpu *addr)
{
register_t s;
+ struct pcb *pcb;
+ pcb = td->td_pcb;
s = intr_disable();
if (td == PCPU_GET(fpcurthread)) {
fxrstor(addr);
intr_restore(s);
} else {
intr_restore(s);
- bcopy(addr, &td->td_pcb->pcb_save, sizeof(*addr));
+ bcopy(addr, td->td_pcb->pcb_save, sizeof(*addr));
}
- curthread->td_pcb->pcb_flags |= PCB_FPUINITDONE;
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_USERFPUINITDONE;
+ pcb->pcb_flags |= PCB_FPUINITDONE;
}
/*
@@ -575,3 +628,74 @@ static devclass_t fpupnp_devclass;
DRIVER_MODULE(fpupnp, acpi, fpupnp_driver, fpupnp_devclass, 0, 0);
#endif /* DEV_ISA */
+
+int
+fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
+{
+ struct pcb *pcb;
+
+ pcb = td->td_pcb;
+ KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save == &pcb->pcb_user_save,
+ ("mangled pcb_save"));
+ ctx->flags = 0;
+ if ((pcb->pcb_flags & PCB_FPUINITDONE) != 0)
+ ctx->flags |= FPU_KERN_CTX_FPUINITDONE;
+ fpuexit(td);
+ ctx->prev = pcb->pcb_save;
+ pcb->pcb_save = &ctx->hwstate;
+ pcb->pcb_flags |= PCB_KERNFPU;
+ pcb->pcb_flags &= ~PCB_FPUINITDONE;
+ return (0);
+}
+
+int
+fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
+{
+ struct pcb *pcb;
+ register_t savecrit;
+
+ pcb = td->td_pcb;
+ savecrit = intr_disable();
+ if (curthread == PCPU_GET(fpcurthread))
+ fpudrop();
+ intr_restore(savecrit);
+ pcb->pcb_save = ctx->prev;
+ if (pcb->pcb_save == &pcb->pcb_user_save) {
+ if ((pcb->pcb_flags & PCB_USERFPUINITDONE) != 0)
+ pcb->pcb_flags |= PCB_FPUINITDONE;
+ else
+ pcb->pcb_flags &= ~PCB_FPUINITDONE;
+ pcb->pcb_flags &= ~PCB_KERNFPU;
+ } else {
+ if ((ctx->flags & FPU_KERN_CTX_FPUINITDONE) != 0)
+ pcb->pcb_flags |= PCB_FPUINITDONE;
+ else
+ pcb->pcb_flags &= ~PCB_FPUINITDONE;
+ KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave"));
+ }
+ return (0);
+}
+
+int
+fpu_kern_thread(u_int flags)
+{
+ struct pcb *pcb;
+
+ pcb = PCPU_GET(curpcb);
+ KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0,
+ ("Only kthread may use fpu_kern_thread"));
+ KASSERT(pcb->pcb_save == &pcb->pcb_user_save, ("mangled pcb_save"));
+ KASSERT(PCB_USER_FPU(pcb), ("recursive call"));
+
+ pcb->pcb_flags |= PCB_KERNFPU;
+ return (0);
+}
+
+int
+is_fpu_kern_thread(u_int flags)
+{
+
+ if ((curthread->td_pflags & TDP_KTHREAD) == 0)
+ return (0);
+ return ((PCPU_GET(curpcb)->pcb_flags & PCB_KERNFPU) != 0);
+}
Modified: head/sys/amd64/amd64/machdep.c
==============================================================================
--- head/sys/amd64/amd64/machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -1960,7 +1960,7 @@ int
fill_fpregs(struct thread *td, struct fpreg *fpregs)
{
- fill_fpregs_xmm(&td->td_pcb->pcb_save, fpregs);
+ fill_fpregs_xmm(&td->td_pcb->pcb_user_save, fpregs);
return (0);
}
@@ -1969,7 +1969,7 @@ int
set_fpregs(struct thread *td, struct fpreg *fpregs)
{
- set_fpregs_xmm(fpregs, &td->td_pcb->pcb_save);
+ set_fpregs_xmm(fpregs, &td->td_pcb->pcb_user_save);
return (0);
}
@@ -2084,7 +2084,8 @@ static void
get_fpcontext(struct thread *td, mcontext_t *mcp)
{
- mcp->mc_ownedfp = fpugetregs(td, (struct savefpu *)&mcp->mc_fpstate);
+ mcp->mc_ownedfp = fpugetuserregs(td,
+ (struct savefpu *)&mcp->mc_fpstate);
mcp->mc_fpformat = fpuformat();
}
@@ -2109,7 +2110,7 @@ set_fpcontext(struct thread *td, const m
*/
fpstate = (struct savefpu *)&mcp->mc_fpstate;
fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask;
- fpusetregs(td, fpstate);
+ fpusetuserregs(td, fpstate);
} else
return (EINVAL);
return (0);
@@ -2120,6 +2121,7 @@ fpstate_drop(struct thread *td)
{
register_t s;
+ KASSERT(PCB_USER_FPU(td->td_pcb), ("fpstate_drop: kernel-owned fpu"));
s = intr_disable();
if (PCPU_GET(fpcurthread) == td)
fpudrop();
@@ -2133,7 +2135,8 @@ fpstate_drop(struct thread *td)
* sendsig() is the only caller of fpugetregs()... perhaps we just
* have too many layers.
*/
- curthread->td_pcb->pcb_flags &= ~PCB_FPUINITDONE;
+ curthread->td_pcb->pcb_flags &= ~(PCB_FPUINITDONE |
+ PCB_USERFPUINITDONE);
intr_restore(s);
}
Modified: head/sys/amd64/amd64/mp_machdep.c
==============================================================================
--- head/sys/amd64/amd64/mp_machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/mp_machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -1247,7 +1247,7 @@ cpususpend_handler(void)
rf = intr_disable();
cr3 = rcr3();
- stopfpu = &stopxpcbs[cpu]->xpcb_pcb.pcb_save;
+ stopfpu = stopxpcbs[cpu]->xpcb_pcb.pcb_save;
if (savectx2(stopxpcbs[cpu])) {
fpugetregs(curthread, stopfpu);
wbinvd();
Modified: head/sys/amd64/amd64/trap.c
==============================================================================
--- head/sys/amd64/amd64/trap.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/trap.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -425,6 +425,8 @@ trap(struct trapframe *frame)
case T_DNA:
/* transparent fault (due to context switch "late") */
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("kernel FPU ctx has leaked"));
fpudna();
goto userout;
@@ -449,16 +451,19 @@ trap(struct trapframe *frame)
goto out;
case T_DNA:
+ KASSERT(!PCB_USER_FPU(td->td_pcb),
+ ("Unregistered use of FPU in kernel"));
+ fpudna();
+ goto out;
+
+ case T_ARITHTRAP: /* arithmetic trap */
+ case T_XMMFLT: /* SIMD floating-point exception */
+ case T_FPOPFLT: /* FPU operand fetch fault */
/*
- * The kernel is apparently using fpu for copying.
- * XXX this should be fatal unless the kernel has
- * registered such use.
+ * XXXKIB for now disable any FPU traps in kernel
+ * handler registration seems to be overkill
*/
- printf("fpudna in kernel mode!\n");
-#ifdef KDB
- kdb_backtrace();
-#endif
- fpudna();
+ trap_fatal(frame, 0);
goto out;
case T_STKFLT: /* stack fault */
@@ -603,6 +608,8 @@ trap(struct trapframe *frame)
user:
userret(td, frame);
mtx_assert(&Giant, MA_NOTOWNED);
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("Return from trap with kernel FPU ctx leaked"));
userout:
out:
return;
@@ -891,5 +898,12 @@ syscall(struct trapframe *frame)
trapsignal(td, &ksi);
}
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("System call %s returing with kernel FPU ctx leaked",
+ syscallname(td->td_proc, sa.code)));
+ KASSERT(td->td_pcb->pcb_save == &td->td_pcb->pcb_user_save,
+ ("System call %s returning with mangled pcb_save",
+ syscallname(td->td_proc, sa.code)));
+
syscallret(td, error, &sa);
}
Modified: head/sys/amd64/amd64/vm_machdep.c
==============================================================================
--- head/sys/amd64/amd64/vm_machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/amd64/vm_machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -122,7 +122,7 @@ cpu_fork(td1, p2, td2, flags)
return;
}
- /* Ensure that p1's pcb is up to date. */
+ /* Ensure that td1's pcb is up to date. */
fpuexit(td1);
/* Point the pcb to the top of the stack */
@@ -130,9 +130,12 @@ cpu_fork(td1, p2, td2, flags)
td2->td_kstack_pages * PAGE_SIZE) - 1;
td2->td_pcb = pcb2;
- /* Copy p1's pcb */
+ /* Copy td1's pcb */
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
+ /* Properly initialize pcb_save */
+ pcb2->pcb_save = &pcb2->pcb_user_save;
+
/* Point mdproc and then copy over td1's contents */
mdp2 = &p2->p_md;
bcopy(&p1->p_md, mdp2, sizeof(*mdp2));
@@ -308,6 +311,7 @@ cpu_thread_alloc(struct thread *td)
td->td_pcb = (struct pcb *)(td->td_kstack +
td->td_kstack_pages * PAGE_SIZE) - 1;
td->td_frame = (struct trapframe *)td->td_pcb - 1;
+ td->td_pcb->pcb_save = &td->td_pcb->pcb_user_save;
}
void
@@ -381,7 +385,8 @@ cpu_set_upcall(struct thread *td, struct
* values here.
*/
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2));
- pcb2->pcb_flags &= ~PCB_FPUINITDONE;
+ pcb2->pcb_flags &= ~(PCB_FPUINITDONE | PCB_USERFPUINITDONE);
+ pcb2->pcb_save = &pcb2->pcb_user_save;
pcb2->pcb_full_iret = 1;
/*
Modified: head/sys/amd64/ia32/ia32_reg.c
==============================================================================
--- head/sys/amd64/ia32/ia32_reg.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/ia32/ia32_reg.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -147,7 +147,7 @@ fill_fpregs32(struct thread *td, struct
{
struct save87 *sv_87 = (struct save87 *)regs;
struct env87 *penv_87 = &sv_87->sv_env;
- struct savefpu *sv_fpu = &td->td_pcb->pcb_save;
+ struct savefpu *sv_fpu = &td->td_pcb->pcb_user_save;
struct envxmm *penv_xmm = &sv_fpu->sv_env;
int i;
@@ -182,7 +182,7 @@ set_fpregs32(struct thread *td, struct f
{
struct save87 *sv_87 = (struct save87 *)regs;
struct env87 *penv_87 = &sv_87->sv_env;
- struct savefpu *sv_fpu = &td->td_pcb->pcb_save;
+ struct savefpu *sv_fpu = &td->td_pcb->pcb_user_save;
struct envxmm *penv_xmm = &sv_fpu->sv_env;
int i;
Modified: head/sys/amd64/include/fpu.h
==============================================================================
--- head/sys/amd64/include/fpu.h Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/include/fpu.h Sat Jun 5 15:59:59 2010 (r208833)
@@ -73,6 +73,17 @@ struct savefpu {
u_char sv_pad[96];
} __aligned(16);
+#ifdef _KERNEL
+struct fpu_kern_ctx {
+ struct savefpu hwstate;
+ struct savefpu *prev;
+ uint32_t flags;
+};
+#define FPU_KERN_CTX_FPUINITDONE 0x01
+
+#define PCB_USER_FPU(pcb) (((pcb)->pcb_flags & PCB_KERNFPU) == 0)
+#endif
+
/*
* The hardware default control word for i387's and later coprocessors is
* 0x37F, giving:
@@ -102,9 +113,22 @@ void fpudrop(void);
void fpuexit(struct thread *td);
int fpuformat(void);
int fpugetregs(struct thread *td, struct savefpu *addr);
+int fpugetuserregs(struct thread *td, struct savefpu *addr);
void fpuinit(void);
void fpusetregs(struct thread *td, struct savefpu *addr);
+void fpusetuserregs(struct thread *td, struct savefpu *addr);
int fputrap(void);
+int fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx,
+ u_int flags);
+int fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx);
+int fpu_kern_thread(u_int flags);
+int is_fpu_kern_thread(u_int flags);
+
+/*
+ * Flags for fpu_kern_enter() and fpu_kern_thread().
+ */
+#define FPU_KERN_NORMAL 0x0000
+
#endif
#endif /* !_MACHINE_FPU_H_ */
Modified: head/sys/amd64/include/pcb.h
==============================================================================
--- head/sys/amd64/include/pcb.h Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/amd64/include/pcb.h Sat Jun 5 15:59:59 2010 (r208833)
@@ -57,7 +57,9 @@ struct pcb {
register_t pcb_gsbase;
u_long pcb_flags;
#define PCB_DBREGS 0x02 /* process using debug registers */
+#define PCB_KERNFPU 0x04 /* kernel uses fpu */
#define PCB_FPUINITDONE 0x08 /* fpu state is initialized */
+#define PCB_USERFPUINITDONE 0x10 /* fpu user state is initialized */
#define PCB_GS32BIT 0x20 /* linux gs switch */
#define PCB_32BIT 0x40 /* process has 32 bit context (segs etc) */
#define PCB_FULLCTX 0x80 /* full context restore on sysret */
@@ -69,7 +71,7 @@ struct pcb {
u_int64_t pcb_dr6;
u_int64_t pcb_dr7;
- struct savefpu pcb_save;
+ struct savefpu pcb_user_save;
uint16_t pcb_initial_fpucw;
caddr_t pcb_onfault; /* copyin/out fault recovery */
@@ -78,6 +80,7 @@ struct pcb {
struct user_segment_descriptor pcb_gs32sd;
/* local tss, with i/o bitmap; NULL for common */
struct amd64tss *pcb_tssp;
+ struct savefpu *pcb_save;
char pcb_full_iret;
};
Modified: head/sys/i386/i386/machdep.c
==============================================================================
--- head/sys/i386/i386/machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/i386/machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -3185,12 +3185,12 @@ fill_fpregs(struct thread *td, struct fp
{
#ifdef CPU_ENABLE_SSE
if (cpu_fxsr) {
- fill_fpregs_xmm(&td->td_pcb->pcb_save.sv_xmm,
+ fill_fpregs_xmm(&td->td_pcb->pcb_user_save.sv_xmm,
(struct save87 *)fpregs);
return (0);
}
#endif /* CPU_ENABLE_SSE */
- bcopy(&td->td_pcb->pcb_save.sv_87, fpregs, sizeof *fpregs);
+ bcopy(&td->td_pcb->pcb_user_save.sv_87, fpregs, sizeof *fpregs);
return (0);
}
@@ -3200,11 +3200,11 @@ set_fpregs(struct thread *td, struct fpr
#ifdef CPU_ENABLE_SSE
if (cpu_fxsr) {
set_fpregs_xmm((struct save87 *)fpregs,
- &td->td_pcb->pcb_save.sv_xmm);
+ &td->td_pcb->pcb_user_save.sv_xmm);
return (0);
}
#endif /* CPU_ENABLE_SSE */
- bcopy(fpregs, &td->td_pcb->pcb_save.sv_87, sizeof *fpregs);
+ bcopy(fpregs, &td->td_pcb->pcb_user_save.sv_87, sizeof *fpregs);
return (0);
}
@@ -3331,7 +3331,7 @@ get_fpcontext(struct thread *td, mcontex
addr = (void *)((char *)addr + 4);
while ((uintptr_t)(void *)addr & 0xF);
}
- mcp->mc_ownedfp = npxgetregs(td, addr);
+ mcp->mc_ownedfp = npxgetuserregs(td, addr);
if (addr != (union savefpu *)&mcp->mc_fpstate) {
bcopy(addr, &mcp->mc_fpstate, sizeof(mcp->mc_fpstate));
bzero(&mcp->mc_spare2, sizeof(mcp->mc_spare2));
@@ -3376,7 +3376,7 @@ set_fpcontext(struct thread *td, const m
* XXX we violate the dubious requirement that npxsetregs()
* be called with interrupts disabled.
*/
- npxsetregs(td, addr);
+ npxsetuserregs(td, addr);
#endif
/*
* Don't bother putting things back where they were in the
@@ -3393,6 +3393,7 @@ fpstate_drop(struct thread *td)
{
register_t s;
+ KASSERT(PCB_USER_FPU(td->td_pcb), ("fpstate_drop: kernel-owned fpu"));
s = intr_disable();
#ifdef DEV_NPX
if (PCPU_GET(fpcurthread) == td)
@@ -3408,7 +3409,8 @@ fpstate_drop(struct thread *td)
* sendsig() is the only caller of npxgetregs()... perhaps we just
* have too many layers.
*/
- curthread->td_pcb->pcb_flags &= ~PCB_NPXINITDONE;
+ curthread->td_pcb->pcb_flags &= ~(PCB_NPXINITDONE |
+ PCB_NPXUSERINITDONE);
intr_restore(s);
}
Modified: head/sys/i386/i386/ptrace_machdep.c
==============================================================================
--- head/sys/i386/i386/ptrace_machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/i386/ptrace_machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -51,7 +51,7 @@ cpu_ptrace(struct thread *td, int req, v
if (!cpu_fxsr)
return (EINVAL);
- fpstate = &td->td_pcb->pcb_save.sv_xmm;
+ fpstate = &td->td_pcb->pcb_user_save.sv_xmm;
switch (req) {
case PT_GETXMMREGS:
error = copyout(fpstate, addr, sizeof(*fpstate));
Modified: head/sys/i386/i386/swtch.s
==============================================================================
--- head/sys/i386/i386/swtch.s Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/i386/swtch.s Sat Jun 5 15:59:59 2010 (r208833)
@@ -156,8 +156,7 @@ ENTRY(cpu_switch)
/* have we used fp, and need a save? */
cmpl %ecx,PCPU(FPCURTHREAD)
jne 1f
- addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complicated */
- pushl %edx
+ pushl PCB_SAVEFPU(%edx) /* h/w bugs make saving complicated */
call npxsave /* do it in a big C function */
popl %eax
1:
@@ -408,7 +407,7 @@ ENTRY(savectx)
pushl %ecx
movl TD_PCB(%eax),%eax
- leal PCB_SAVEFPU(%eax),%eax
+ movl PCB_SAVEFPU(%eax),%eax
pushl %eax
pushl %eax
call npxsave
@@ -417,7 +416,7 @@ ENTRY(savectx)
popl %ecx
pushl $PCB_SAVEFPU_SIZE
- leal PCB_SAVEFPU(%ecx),%ecx
+ movl PCB_SAVEFPU(%ecx),%ecx
pushl %ecx
pushl %eax
call bcopy
Modified: head/sys/i386/i386/trap.c
==============================================================================
--- head/sys/i386/i386/trap.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/i386/trap.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -501,6 +501,8 @@ trap(struct trapframe *frame)
case T_DNA:
#ifdef DEV_NPX
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("kernel FPU ctx has leaked"));
/* transparent fault (due to context switch "late") */
if (npxdna())
goto userout;
@@ -533,20 +535,23 @@ trap(struct trapframe *frame)
case T_DNA:
#ifdef DEV_NPX
- /*
- * The kernel is apparently using npx for copying.
- * XXX this should be fatal unless the kernel has
- * registered such use.
- */
- printf("npxdna in kernel mode!\n");
-#ifdef KDB
- kdb_backtrace();
-#endif
+ KASSERT(!PCB_USER_FPU(td->td_pcb),
+ ("Unregistered use of FPU in kernel"));
if (npxdna())
goto out;
#endif
break;
+ case T_ARITHTRAP: /* arithmetic trap */
+ case T_XMMFLT: /* SIMD floating-point exception */
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ /*
+ * XXXKIB for now disable any FPU traps in kernel
+ * handler registration seems to be overkill
+ */
+ trap_fatal(frame, 0);
+ goto out;
+
/*
* The following two traps can happen in
* vm86 mode, and, if so, we want to handle
@@ -752,6 +757,8 @@ trap(struct trapframe *frame)
user:
userret(td, frame);
mtx_assert(&Giant, MA_NOTOWNED);
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("Return from trap with kernel FPU ctx leaked"));
userout:
out:
return;
@@ -1064,5 +1071,12 @@ syscall(struct trapframe *frame)
trapsignal(td, &ksi);
}
+ KASSERT(PCB_USER_FPU(td->td_pcb),
+ ("System call %s returning with kernel FPU ctx leaked",
+ syscallname(td->td_proc, sa.code)));
+ KASSERT(td->td_pcb->pcb_save == &td->td_pcb->pcb_user_save,
+ ("System call %s returning with mangled pcb_save",
+ syscallname(td->td_proc, sa.code)));
+
syscallret(td, error, &sa);
}
Modified: head/sys/i386/i386/vm_machdep.c
==============================================================================
--- head/sys/i386/i386/vm_machdep.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/i386/vm_machdep.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -176,13 +176,13 @@ cpu_fork(td1, p2, td2, flags)
return;
}
- /* Ensure that p1's pcb is up to date. */
+ /* Ensure that td1's pcb is up to date. */
if (td1 == curthread)
td1->td_pcb->pcb_gs = rgs();
#ifdef DEV_NPX
savecrit = intr_disable();
if (PCPU_GET(fpcurthread) == td1)
- npxsave(&td1->td_pcb->pcb_save);
+ npxsave(td1->td_pcb->pcb_save);
intr_restore(savecrit);
#endif
@@ -191,9 +191,12 @@ cpu_fork(td1, p2, td2, flags)
td2->td_kstack_pages * PAGE_SIZE) - 1;
td2->td_pcb = pcb2;
- /* Copy p1's pcb */
+ /* Copy td1's pcb */
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
+ /* Properly initialize pcb_save */
+ pcb2->pcb_save = &pcb2->pcb_user_save;
+
/* Point mdproc and then copy over td1's contents */
mdp2 = &p2->p_md;
bcopy(&p1->p_md, mdp2, sizeof(*mdp2));
@@ -372,6 +375,7 @@ cpu_thread_alloc(struct thread *td)
td->td_kstack_pages * PAGE_SIZE) - 1;
td->td_frame = (struct trapframe *)((caddr_t)td->td_pcb - 16) - 1;
td->td_pcb->pcb_ext = NULL;
+ td->td_pcb->pcb_save = &td->td_pcb->pcb_user_save;
}
void
@@ -437,7 +441,8 @@ cpu_set_upcall(struct thread *td, struct
* values here.
*/
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2));
- pcb2->pcb_flags &= ~(PCB_NPXTRAP|PCB_NPXINITDONE);
+ pcb2->pcb_flags &= ~(PCB_NPXTRAP|PCB_NPXINITDONE|PCB_NPXUSERINITDONE);
+ pcb2->pcb_save = &pcb2->pcb_user_save;
/*
* Create a new fresh stack for the new thread.
Modified: head/sys/i386/include/npx.h
==============================================================================
--- head/sys/i386/include/npx.h Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/include/npx.h Sat Jun 5 15:59:59 2010 (r208833)
@@ -143,6 +143,15 @@ union savefpu {
#define IRQ_NPX 13
+struct fpu_kern_ctx {
+ union savefpu hwstate;
+ union savefpu *prev;
+ uint32_t flags;
+};
+#define FPU_KERN_CTX_NPXINITDONE 0x01
+
+#define PCB_USER_FPU(pcb) (((pcb)->pcb_flags & PCB_KERNNPX) == 0)
+
/* full reset on some systems, NOP on others */
#define npx_full_reset() outb(IO_NPX + 1, 0)
@@ -151,10 +160,22 @@ void npxdrop(void);
void npxexit(struct thread *td);
int npxformat(void);
int npxgetregs(struct thread *td, union savefpu *addr);
+int npxgetuserregs(struct thread *td, union savefpu *addr);
void npxinit(void);
void npxsave(union savefpu *addr);
void npxsetregs(struct thread *td, union savefpu *addr);
+void npxsetuserregs(struct thread *td, union savefpu *addr);
int npxtrap(void);
+int fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx,
+ u_int flags);
+int fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx);
+int fpu_kern_thread(u_int flags);
+int is_fpu_kern_thread(u_int flags);
+
+/*
+ * Flags for fpu_kern_enter() and fpu_kern_thread().
+ */
+#define FPU_KERN_NORMAL 0x0000
#endif
Modified: head/sys/i386/include/pcb.h
==============================================================================
--- head/sys/i386/include/pcb.h Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/include/pcb.h Sat Jun 5 15:59:59 2010 (r208833)
@@ -60,7 +60,7 @@ struct pcb {
int pcb_dr6;
int pcb_dr7;
- union savefpu pcb_save;
+ union savefpu pcb_user_save;
uint16_t pcb_initial_npxcw;
u_int pcb_flags;
#define FP_SOFTFP 0x01 /* process using software fltng pnt emulator */
@@ -68,6 +68,8 @@ struct pcb {
#define PCB_NPXTRAP 0x04 /* npx trap pending */
#define PCB_NPXINITDONE 0x08 /* fpu state is initialized */
#define PCB_VM86CALL 0x10 /* in vm86 call */
+#define PCB_NPXUSERINITDONE 0x20 /* user fpu state is initialized */
+#define PCB_KERNNPX 0x40 /* kernel uses npx */
caddr_t pcb_onfault; /* copyin/out fault recovery */
int pcb_gs;
@@ -76,6 +78,7 @@ struct pcb {
struct pcb_ext *pcb_ext; /* optional pcb extension */
int pcb_psl; /* process status long */
u_long pcb_vm86[2]; /* vm86bios scratch space */
+ union savefpu *pcb_save;
};
#ifdef _KERNEL
Modified: head/sys/i386/isa/npx.c
==============================================================================
--- head/sys/i386/isa/npx.c Sat Jun 5 14:53:34 2010 (r208832)
+++ head/sys/i386/isa/npx.c Sat Jun 5 15:59:59 2010 (r208833)
@@ -135,12 +135,12 @@ void stop_emulating(void);
#ifdef CPU_ENABLE_SSE
#define GET_FPU_CW(thread) \
(cpu_fxsr ? \
- (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_cw : \
- (thread)->td_pcb->pcb_save.sv_87.sv_env.en_cw)
+ (thread)->td_pcb->pcb_save->sv_xmm.sv_env.en_cw : \
+ (thread)->td_pcb->pcb_save->sv_87.sv_env.en_cw)
#define GET_FPU_SW(thread) \
(cpu_fxsr ? \
- (thread)->td_pcb->pcb_save.sv_xmm.sv_env.en_sw : \
- (thread)->td_pcb->pcb_save.sv_87.sv_env.en_sw)
+ (thread)->td_pcb->pcb_save->sv_xmm.sv_env.en_sw : \
+ (thread)->td_pcb->pcb_save->sv_87.sv_env.en_sw)
#define SET_FPU_CW(savefpu, value) do { \
if (cpu_fxsr) \
(savefpu)->sv_xmm.sv_env.en_cw = (value); \
@@ -149,9 +149,9 @@ void stop_emulating(void);
} while (0)
#else /* CPU_ENABLE_SSE */
#define GET_FPU_CW(thread) \
- (thread->td_pcb->pcb_save.sv_87.sv_env.en_cw)
+ (thread->td_pcb->pcb_save->sv_87.sv_env.en_cw)
#define GET_FPU_SW(thread) \
- (thread->td_pcb->pcb_save.sv_87.sv_env.en_sw)
+ (thread->td_pcb->pcb_save->sv_87.sv_env.en_sw)
#define SET_FPU_CW(savefpu, value) \
(savefpu)->sv_87.sv_env.en_cw = (value)
#endif /* CPU_ENABLE_SSE */
@@ -502,7 +502,7 @@ npxexit(td)
savecrit = intr_disable();
if (curthread == PCPU_GET(fpcurthread))
- npxsave(&PCPU_GET(curpcb)->pcb_save);
+ npxsave(PCPU_GET(curpcb)->pcb_save);
intr_restore(savecrit);
#ifdef NPX_DEBUG
if (npx_exists) {
@@ -809,6 +809,8 @@ npxdna(void)
if (pcb->pcb_initial_npxcw != __INITIAL_NPXCW__)
fldcw(&pcb->pcb_initial_npxcw);
pcb->pcb_flags |= PCB_NPXINITDONE;
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_NPXUSERINITDONE;
} else {
/*
* The following fpurstor() may cause an IRQ13 when the
@@ -824,7 +826,7 @@ npxdna(void)
* fnclex if it is the first FPU instruction after a context
* switch.
*/
- fpurstor(&pcb->pcb_save);
+ fpurstor(pcb->pcb_save);
}
intr_restore(s);
@@ -895,18 +897,18 @@ npxdrop()
* It returns the FPU ownership status.
*/
int
-npxgetregs(td, addr)
- struct thread *td;
- union savefpu *addr;
+npxgetregs(struct thread *td, union savefpu *addr)
{
+ struct pcb *pcb;
register_t s;
if (!npx_exists)
return (_MC_FPOWNED_NONE);
- if ((td->td_pcb->pcb_flags & PCB_NPXINITDONE) == 0) {
+ pcb = td->td_pcb;
+ if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) {
bcopy(&npx_initialstate, addr, sizeof(npx_initialstate));
- SET_FPU_CW(addr, td->td_pcb->pcb_initial_npxcw);
+ SET_FPU_CW(addr, pcb->pcb_initial_npxcw);
return (_MC_FPOWNED_NONE);
}
s = intr_disable();
@@ -925,7 +927,43 @@ npxgetregs(td, addr)
return (_MC_FPOWNED_FPU);
} else {
intr_restore(s);
- bcopy(&td->td_pcb->pcb_save, addr, sizeof(*addr));
+ bcopy(pcb->pcb_save, addr, sizeof(*addr));
+ return (_MC_FPOWNED_PCB);
+ }
+}
+
+int
+npxgetuserregs(struct thread *td, union savefpu *addr)
+{
+ struct pcb *pcb;
+ register_t s;
+
+ if (!npx_exists)
+ return (_MC_FPOWNED_NONE);
+
+ pcb = td->td_pcb;
+ if ((pcb->pcb_flags & PCB_NPXUSERINITDONE) == 0) {
+ bcopy(&npx_initialstate, addr, sizeof(npx_initialstate));
+ SET_FPU_CW(addr, pcb->pcb_initial_npxcw);
+ return (_MC_FPOWNED_NONE);
+ }
+ s = intr_disable();
+ if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
+ fpusave(addr);
+#ifdef CPU_ENABLE_SSE
+ if (!cpu_fxsr)
+#endif
+ /*
+ * fnsave initializes the FPU and destroys whatever
+ * context it contains. Make sure the FPU owner
+ * starts with a clean state next time.
+ */
+ npxdrop();
+ intr_restore(s);
+ return (_MC_FPOWNED_FPU);
+ } else {
+ intr_restore(s);
+ bcopy(&pcb->pcb_user_save, addr, sizeof(*addr));
return (_MC_FPOWNED_PCB);
}
}
@@ -934,15 +972,15 @@ npxgetregs(td, addr)
* Set the state of the FPU.
*/
void
-npxsetregs(td, addr)
- struct thread *td;
- union savefpu *addr;
+npxsetregs(struct thread *td, union savefpu *addr)
{
+ struct pcb *pcb;
register_t s;
if (!npx_exists)
return;
+ pcb = td->td_pcb;
s = intr_disable();
if (td == PCPU_GET(fpcurthread)) {
#ifdef CPU_ENABLE_SSE
@@ -953,9 +991,39 @@ npxsetregs(td, addr)
intr_restore(s);
} else {
intr_restore(s);
- bcopy(addr, &td->td_pcb->pcb_save, sizeof(*addr));
+ bcopy(addr, pcb->pcb_save, sizeof(*addr));
+ }
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_NPXUSERINITDONE;
+ pcb->pcb_flags |= PCB_NPXINITDONE;
+}
+
+void
+npxsetuserregs(struct thread *td, union savefpu *addr)
+{
+ struct pcb *pcb;
+ register_t s;
+
+ if (!npx_exists)
+ return;
+
+ pcb = td->td_pcb;
+ s = intr_disable();
+ if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
+#ifdef CPU_ENABLE_SSE
+ if (!cpu_fxsr)
+#endif
+ fnclex(); /* As in npxdrop(). */
+ fpurstor(addr);
+ intr_restore(s);
+ pcb->pcb_flags |= PCB_NPXUSERINITDONE | PCB_NPXINITDONE;
+ } else {
+ intr_restore(s);
+ bcopy(addr, &pcb->pcb_user_save, sizeof(*addr));
+ if (PCB_USER_FPU(pcb))
+ pcb->pcb_flags |= PCB_NPXINITDONE;
+ pcb->pcb_flags |= PCB_NPXUSERINITDONE;
}
- curthread->td_pcb->pcb_flags |= PCB_NPXINITDONE;
}
static void
@@ -1124,3 +1192,74 @@ DRIVER_MODULE(npxisa, isa, npxisa_driver
DRIVER_MODULE(npxisa, acpi, npxisa_driver, npxisa_devclass, 0, 0);
#endif
#endif /* DEV_ISA */
+
+int
+fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
+{
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-head
mailing list