shithub: riscv

Download patch

ref: cf00494038eb41f37ff22c13fbef80ef65fbf281
parent: 3269ee741eab76bd486b178af66499800acc7efb
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Apr 5 18:55:17 EDT 2025

kernel: allow use of fpu in note handlers

Instead of making it illegal to use FPU in note-handler,
we save, disable and mark the fpstate with FPnotify.
Once in emu-trap, we call notefpsave() which will
make a copy of the original register state once
the note-handler returns (which is also exposed as
/proc/n/notefpregs).

This patch applies to arm64, amd64, 386 and most
arm32 kernels. The remaining kernels are left as
an exercise to the reader.

--- a/sys/man/3/proc
+++ b/sys/man/3/proc
@@ -10,9 +10,11 @@
 .BI /proc/ n /ctl
 .BI /proc/ n /fd
 .BI /proc/ n /fpregs
+.BI /proc/ n /kfpregs
 .BI /proc/ n /kregs
 .BI /proc/ n /mem
 .BI /proc/ n /note
+.BI /proc/ n /notefpregs
 .BI /proc/ n /noteid
 .BI /proc/ n /notepg
 .BI /proc/ n /ns
@@ -64,13 +66,21 @@
 The files
 .BR regs ,
 .BR fpregs ,
+.BR kfpregs ,
+.B kregs
 and
-.BR kregs
+.B notefpregs
 hold representations of the user-level registers, floating-point registers,
 and kernel registers in machine-dependent form.
 The
 .B kregs
-file is read-only.
+and
+.B kfpregs
+files are read-only.
+The
+.B notefpregs
+file holds the saved and to be restored floating-point registers
+while the program is executing its note handler.
 .PP
 The read-only
 .B fd
--- a/sys/src/9/arm64/dat.h
+++ b/sys/src/9/arm64/dat.h
@@ -20,6 +20,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -74,6 +75,13 @@
 	ulong	status;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;	/* when context nests */
+};
+
 #define KFPSTATE
 
 struct PFPU
@@ -80,8 +88,8 @@
 {
 	int	fpstate;
 	int	kfpstate;
-	FPsave	*fpsave;
-	FPsave	*kfpsave;
+	FPalloc	*fpsave;
+	FPalloc	*kfpsave;
 };
 
 enum
@@ -88,11 +96,11 @@
 {
 	FPinit,
 	FPactive,
-	FPinactive,
 	FPprotected,
+	FPinactive,		/* fpsave valid when fpstate >= FPincative */
 
 	/* bits or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
@@ -158,7 +166,7 @@
 	PMach;
 
 	int	fpstate;
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 
 	int	cputype;
 	ulong	delayloop;
--- a/sys/src/9/arm64/fns.h
+++ b/sys/src/9/arm64/fns.h
@@ -93,8 +93,10 @@
 extern void fpuprocfork(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpuprocrestore(Proc*);
-extern FPsave* fpukenter(Ureg*);
-extern void fpukexit(Ureg*, FPsave*);
+extern FPalloc* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, FPalloc*);
+extern void fpunotify(Proc*);
+extern void fpunoted(Proc*);
 extern void mathtrap(Ureg*);
 
 /* trap */
--- a/sys/src/9/arm64/fpu.c
+++ b/sys/src/9/arm64/fpu.c
@@ -47,23 +47,24 @@
 	fpoff();
 }
 
-static FPsave*
-fpalloc(void)
+static FPalloc*
+fpalloc(FPalloc *link)
 {
-	FPsave *save;
+	FPalloc *a;
 
-	while((save = mallocalign(sizeof(FPsave), 16, 0, 0)) == nil){
-		spllo();
-		resrcwait("no memory for FPsave");
-		splhi();
+	while((a = mallocalign(sizeof(FPalloc), 16, 0, 0)) == nil){
+		int x = spllo();
+		resrcwait("no memory for FPalloc");
+		splx(x);
 	}
-	return save;
+	a->link = link;
+	return a;
 }
 
 static void
-fpfree(FPsave *save)
+fpfree(FPalloc *a)
 {
-	free(save);
+	free(a);
 }
 
 
@@ -74,7 +75,7 @@
  *  are handled between fpukenter() and fpukexit(),
  *  so they can use floating point and vector instructions.
  */
-FPsave*
+FPalloc*
 fpukenter(Ureg*)
 {
 	if(up == nil){
@@ -110,7 +111,7 @@
 }
 
 void
-fpukexit(Ureg *ureg, FPsave *save)
+fpukexit(Ureg *ureg, FPalloc *o)
 {
 	if(up == nil){
 		switch(m->fpstate){
@@ -121,8 +122,8 @@
 			fpfree(m->fpsave);
 			m->fpstate = FPinit;
 		}
-		m->fpsave = save;
-		if(save != nil)
+		m->fpsave = o;
+		if(o != nil)
 			m->fpstate = FPinactive;
 		return;
 	}
@@ -143,8 +144,8 @@
 		fpfree(up->kfpsave);
 		up->kfpstate = FPinit;
 	}
-	up->kfpsave = save;
-	if(save != nil)
+	up->kfpsave = o;
+	if(o != nil)
 		up->kfpstate = FPinactive;
 }
 
@@ -151,7 +152,13 @@
 void
 fpuprocsetup(Proc *p)
 {
+	FPalloc *a;
+
 	p->fpstate = FPinit;
+	while((a = p->fpsave) != nil) {
+		p->fpsave = a->link;
+		fpfree(a);
+	}
 }
 
 void
@@ -160,7 +167,7 @@
 	int s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPprotected:
 		fpon();
 		/* wet floor */
@@ -170,7 +177,7 @@
 		/* wet floor */
 	case FPinactive:
 		if(p->fpsave == nil)
-			p->fpsave = fpalloc();
+			p->fpsave = fpalloc(nil);
 		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
 		p->fpstate = FPinactive;
 	}
@@ -181,12 +188,19 @@
 fpuprocsave(Proc *p)
 {
 	if(p->state == Moribund){
+		FPalloc *a;
+
 		if(p->fpstate == FPactive || p->kfpstate == FPactive)
 			fpoff();
-		fpfree(p->fpsave);
-		fpfree(p->kfpsave);
-		p->fpsave = p->kfpsave = nil;
 		p->fpstate = p->kfpstate = FPinit;
+		while((a = p->fpsave) != nil) {
+			p->fpsave = a->link;
+			fpfree(a);
+		}
+		while((a = p->kfpsave) != nil) {
+			p->kfpsave = a->link;
+			fpfree(a);
+		}
 		return;
 	}
 	if(p->kfpstate == FPactive){
@@ -221,6 +235,42 @@
 }
 
 void
+fpunotify(Proc *p)
+{
+	fpuprocsave(p);
+	p->fpstate |= FPnotify;
+}
+
+void
+fpunoted(Proc *p)
+{
+	FPalloc *o;
+
+	if(p->fpstate & FPnotify) {
+		p->fpstate &= ~FPnotify;
+	} else if((o = p->fpsave->link) != nil) {
+		fpfree(p->fpsave);
+		p->fpsave = o;
+		p->fpstate = FPinactive;
+	} else {
+		p->fpstate = FPinit;
+	}
+}
+
+FPsave*
+notefpsave(Proc *p)
+{
+	if(p->fpsave == nil)
+		return nil;
+	if(p->fpstate == (FPinactive|FPnotify)){
+		p->fpsave = fpalloc(p->fpsave);
+		memmove(p->fpsave, p->fpsave->link, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	return p->fpsave->link;
+}
+
+void
 mathtrap(Ureg *ureg)
 {
 	if(!userureg(ureg)){
@@ -227,7 +277,7 @@
 		if(up == nil){
 			switch(m->fpstate){
 			case FPinit:
-				m->fpsave = fpalloc();
+				m->fpsave = fpalloc(m->fpsave);
 				m->fpstate = FPactive;
 				fpinit();
 				break;
@@ -249,7 +299,7 @@
 
 		switch(up->kfpstate){
 		case FPinit:
-			up->kfpsave = fpalloc();
+			up->kfpsave = fpalloc(up->kfpsave);
 			up->kfpstate = FPactive;
 			fpinit();
 			break;
@@ -263,17 +313,22 @@
 		return;
 	}
 
-	if(up->fpstate & FPillegal){
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
 	switch(up->fpstate){
+	case FPinit|FPnotify:
+		/* wet floor */
 	case FPinit:
 		if(up->fpsave == nil)
-			up->fpsave = fpalloc();
+			up->fpsave = fpalloc(nil);
 		up->fpstate = FPactive;
 		fpinit();
 		break;
+	case FPinactive|FPnotify:
+		spllo();
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		splhi();
+		/* wet floor */
 	case FPinactive:
 		fprestore(up->fpsave);
 		up->fpstate = FPactive;
--- a/sys/src/9/arm64/trap.c
+++ b/sys/src/9/arm64/trap.c
@@ -97,7 +97,7 @@
 void
 trap(Ureg *ureg)
 {
-	FPsave *f = nil;
+	FPalloc *f = nil;
 	u32int type, intr;
 	int user;
 
@@ -265,13 +265,9 @@
 		 * to it when returning form syscall()
 		 */
 		returnto(noteret);
-
-		splhi();
-		up->fpstate &= ~FPillegal;
 	}
-	else
-		splhi();
 
+	splhi();
 	if(scallnr != RFORK && (up->procctl || up->nnote))
 		notify(ureg);
 
@@ -329,11 +325,11 @@
 	ureg->sp = sp;
 	ureg->pc = (uintptr) up->notify;
 	ureg->link = 0;
-	qunlock(&up->debug);
 
 	splhi();
-	fpuprocsave(up);
-	up->fpstate |= FPillegal;
+	fpunotify(up);
+	qunlock(&up->debug);
+
 	return 1;
 }
 
@@ -350,7 +346,11 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
-	
+
+	splhi();
+	fpunoted(up);
+	spllo();
+
 	nureg = up->ureg;
 	
 	oureg = (uintptr) nureg;
--- a/sys/src/9/bcm/dat.h
+++ b/sys/src/9/bcm/dat.h
@@ -14,6 +14,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -74,8 +75,14 @@
 	 * each must be able to hold an Internal from fpi.h for sw emulation.
 	 */
 	ulong	regs[Maxfpregs][3];
+};
 
-	int	fpstate;
+struct FPalloc
+{
+	FPsave;
+
+	/* the following fields are not visible to devproc */
+	int	fpstate;	/* FPinactive or FPemu */
 	uintptr	pc;		/* of failed fp instr. */
 };
 
@@ -82,7 +89,8 @@
 struct PFPU
 {
 	int	fpstate;
-	FPsave	fpsave[1];
+	FPalloc	*fpsave;
+	FPalloc	*ofpsave;
 };
 
 enum
@@ -89,11 +97,11 @@
 {
 	FPinit,
 	FPactive,
-	FPinactive,
+	FPinactive,		/* fpsave valid when fpstate >= FPincative */
 	FPemu,
 
 	/* bits or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
--- a/sys/src/9/bcm/fns.h
+++ b/sys/src/9/bcm/fns.h
@@ -109,14 +109,16 @@
  */
 extern int fpiarm(Ureg*);
 extern int fpudevprocio(Proc*, void*, long, uintptr, int);
+extern FPalloc *fpalloc(void);
 extern void fpuinit(void);
 extern void fpunoted(void);
-extern void fpunotify(Ureg*);
+extern void fpunotify(void);
 extern void fpuprocrestore(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpuprocfork(Proc*);
 extern void fpusysprocsetup(Proc*);
 extern int fpuemu(Ureg*);
+
 /*
  * Things called from port.
  */
--- a/sys/src/9/bcm/fpiarm.c
+++ b/sys/src/9/bcm/fpiarm.c
@@ -644,7 +644,7 @@
 fpiarm(Ureg *ur)
 {
 	ulong op, o, cp;
-	FPsave *ufp;
+	FPalloc *ufp;
 	int n;
 	void (*fpemu)(ulong , ulong , Ureg *, FPsave *);
 
@@ -651,21 +651,28 @@
 	if(up == nil)
 		panic("fpiarm not in a process");
 	ufp = up->fpsave;
-	/*
-	 * because all the emulated fp state is in the proc structure,
-	 * it need not be saved/restored
-	 */
 	switch(up->fpstate){
 	case FPactive:
 	case FPinactive:
+	case FPinactive|FPnotify:
 		error("illegal instruction: emulated fpu opcode in VFP mode");
 	case FPinit:
-		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
-		up->fpstate = FPemu;
+	case FPinit|FPnotify:
+		if(ufp == nil)
+			ufp = fpalloc();
+		ufp->fpstate = FPemu;
 		ufp->control = 0;
 		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
 		for(n = 0; n < Nfpctlregs; n++)
 			FR(ufp, n) = fpconst[0];
+		up->fpsave = ufp;
+		up->fpstate = FPemu;
+		break;
+	case FPemu|FPnotify:
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		break;
 	}
 	for(n=0; ;n++){
 		validaddr(ur->pc, 4, 0);
--- a/sys/src/9/bcm/softfpu.c
+++ /dev/null
@@ -1,1 +1,0 @@
-#include "../teg2/softfpu.c"
--- a/sys/src/9/bcm/vfp3.c
+++ b/sys/src/9/bcm/vfp3.c
@@ -213,6 +213,17 @@
 	fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
 }
 
+FPalloc*
+fpalloc(void)
+{
+	FPalloc *f;
+	while((f = mallocalign(sizeof(FPalloc), 16, 0, 0)) == nil){
+		int x = spllo();
+		resrcwait("no memory for FPalloc");
+		splx(x);
+	}
+	return f;
+}
 
 /*
  * Called when a note is about to be delivered to a
@@ -222,13 +233,13 @@
  * checked in the Device Not Available handler.
  */
 void
-fpunotify(Ureg*)
+fpunotify(void)
 {
 	if(up->fpstate == FPactive){
 		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
+		up->fpstate = up->fpsave->fpstate = FPinactive;
 	}
-	up->fpstate |= FPillegal;
+	up->fpstate |= FPnotify;
 }
 
 /*
@@ -239,9 +250,37 @@
 void
 fpunoted(void)
 {
-	up->fpstate &= ~FPillegal;
+	if(up->fpstate & FPnotify){
+		up->fpstate &= ~FPnotify;
+	} else {
+		FPalloc *o;
+
+		if(up->fpstate == FPactive)
+			fpoff();
+		if((o = up->ofpsave) != nil) {
+			up->ofpsave = nil;
+			free(up->fpsave);
+			up->fpsave = o;
+			up->fpstate = o->fpstate;
+		} else {
+			up->fpstate = FPinit;
+		}
+	}
 }
 
+FPsave*
+notefpsave(Proc *p)
+{
+	switch(p->fpstate){
+	case FPinactive|FPnotify:
+	case FPemu|FPnotify:
+		p->ofpsave = fpalloc();
+		memmove(p->ofpsave, p->fpsave, sizeof(FPalloc));
+		p->fpstate &= ~FPnotify;
+	}
+	return p->ofpsave;
+}
+
 /* should only be called if p->fpstate == FPactive */
 void
 fpsave(FPsave *fps)
@@ -279,21 +318,24 @@
 void
 fpuprocsave(Proc *p)
 {
-	if(p->fpstate == FPactive){
-		if(p->state == Moribund)
+	if(p->state == Moribund){
+		if(p->fpstate == FPactive)
 			fpoff();
-		else{
-			/*
-			 * Fpsave() stores without handling pending
-			 * unmasked exeptions. Postnote() can't be called
-			 * so the handling of pending exceptions is delayed
-			 * until the process runs again and generates an
-			 * emulation fault to activate the FPU.
-			 */
-			fpsave(p->fpsave);
+		p->fpstate = FPinit;
+		if(p->fpsave != nil){
+			free(p->fpsave);
+			p->fpsave = nil;
 		}
-		p->fpstate = FPinactive;
+		if(p->ofpsave != nil){
+			free(p->ofpsave);
+			p->ofpsave = nil;
+		}
+		return;
 	}
+	if(p->fpstate == FPactive){
+		fpsave(p->fpsave);
+		p->fpstate = p->fpsave->fpstate = FPinactive;
+	}
 }
 
 /*
@@ -317,14 +359,17 @@
 	int s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPactive:
 		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
+		up->fpstate = up->fpsave->fpstate = FPinactive;
 		/* no break */
 	case FPinactive:
-		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
-		p->fpstate = FPinactive;
+	case FPemu:
+		if(p->fpsave == nil)
+			p->fpsave = fpalloc();
+		memmove(p->fpsave, up->fpsave, sizeof(FPalloc));
+		p->fpstate = up->fpsave->fpstate;
 	}
 	splx(s);
 }
@@ -343,6 +388,15 @@
 	p->fpstate = FPinit;
 	fpoff();
 	splx(s);
+
+	if(p->fpsave != nil){
+		free(p->fpsave);
+		p->fpsave = nil;
+	}
+	if(p->ofpsave != nil){
+		free(p->ofpsave);
+		p->ofpsave = nil;
+	}
 }
 
 static void
@@ -379,11 +433,22 @@
 {
 	switch(up->fpstate){
 	case FPemu:
+	case FPemu|FPnotify:
 		error("illegal instruction: VFP opcode in emulated mode");
 	case FPinit:
+	case FPinit|FPnotify:
+		if(up->fpsave == nil)
+			up->fpsave = fpalloc();
 		fpinit();
 		up->fpstate = FPactive;
 		break;
+	case FPinactive|FPnotify:
+		spllo();
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		splhi();
+		/* wet floor */
 	case FPinactive:
 		/*
 		 * Before restoring the state, check for any pending
@@ -476,7 +541,6 @@
 {
 	int s, nfp, cop, op;
 	uintptr pc;
-	static int already;
 
 	if(waserror()){
 		postnote(up, 1, up->errstr, NDebug);
@@ -483,9 +547,6 @@
 		return 1;
 	}
 
-	if(up->fpstate & FPillegal)
-		error("floating point in note handler");
-
 	nfp = 0;
 	pc = ureg->pc;
 	validaddr(pc, 4, 0);
@@ -495,8 +556,6 @@
 		fpstuck(pc);		/* debugging; could move down 1 line */
 	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
 		s = spllo();
-		if(!already++)
-			pprint("warning: emulated arm7500 fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
 		if(waserror()){
 			splx(s);
 			nexterror();
--- a/sys/src/9/bcm64/dat.h
+++ b/sys/src/9/bcm64/dat.h
@@ -14,6 +14,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -68,6 +69,13 @@
 	ulong	status;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;	/* when context nests */
+};
+
 #define KFPSTATE
 
 struct PFPU
@@ -74,8 +82,8 @@
 {
 	int	fpstate;
 	int	kfpstate;
-	FPsave	*fpsave;
-	FPsave	*kfpsave;
+	FPalloc	*fpsave;
+	FPalloc	*kfpsave;
 };
 
 enum
@@ -86,7 +94,7 @@
 	FPprotected,
 
 	/* bits or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
@@ -154,7 +162,7 @@
 	PMach;
 
 	int	fpstate;
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 
 	int	cputype;
 	ulong	delayloop;
--- a/sys/src/9/bcm64/fns.h
+++ b/sys/src/9/bcm64/fns.h
@@ -92,8 +92,10 @@
 extern void fpuprocfork(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpuprocrestore(Proc*);
-extern FPsave* fpukenter(Ureg*);
-extern void fpukexit(Ureg*, FPsave*);
+extern FPalloc* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, FPalloc*);
+extern void fpunotify(Proc*);
+extern void fpunoted(Proc*);
 extern void mathtrap(Ureg*);
 
 /* trap */
--- a/sys/src/9/cycv/dat.h
+++ b/sys/src/9/cycv/dat.h
@@ -55,7 +55,7 @@
 	FPinit,
 	FPactive,
 	FPinactive,
-	FPillegal = 0x100
+	FPnotify = 0x100
 };
 
 struct Confmem
--- a/sys/src/9/cycv/main.c
+++ b/sys/src/9/cycv/main.c
@@ -49,7 +49,7 @@
 	ulong s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPactive:
 		fpsave(up->fpsave);
 		up->fpstate = FPinactive;
--- a/sys/src/9/cycv/trap.c
+++ b/sys/src/9/cycv/trap.c
@@ -121,7 +121,7 @@
 {
 	int s;
 
-	if((up->fpstate & FPillegal) != 0){
+	if(up->fpstate & FPnotify){
 		postnote(up, 1, "sys: floating point in note handler", NDebug);
 		return;
 	}
@@ -278,10 +278,32 @@
 	kexit(ureg);
 }
 
+void
+fpunotify(void)
+{
+	if(up->fpstate == FPactive){
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPnotify;
+}
+
+void
+fpunoted(void)
+{
+	up->fpstate &= ~FPnotify;
+}
+
+FPsave*
+notefpsave(Proc*)
+{
+	return nil;
+}
+
 int
 notify(Ureg *ureg)
 {
-	ulong s, sp;
+	ulong sp;
 	char *msg;
 
 	if(up->procctl)
@@ -289,13 +311,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	if(up->fpstate == FPactive){
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
-	}
-	up->fpstate |= FPillegal;
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -329,8 +345,10 @@
 	ureg->sp = sp;
 	ureg->pc = (uintptr) up->notify;
 	ureg->r14 = 0;
+
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 	return 1;
 }
 
@@ -347,10 +365,12 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
+
+	splhi();
+	fpunoted();
+	spllo();
 	
 	nureg = up->ureg;
-	up->fpstate &= ~FPillegal;
-	
 	oureg = (ulong) nureg;
 	if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 3) != 0){
 		qunlock(&up->debug);
@@ -485,7 +505,7 @@
 }
 
 void
-procrestore(Proc *p)
+procrestore(Proc*)
 {
 }
 
--- a/sys/src/9/imx8/dat.h
+++ b/sys/src/9/imx8/dat.h
@@ -20,6 +20,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -74,6 +75,13 @@
 	ulong	status;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;	/* when context nests */
+};
+
 #define KFPSTATE
 
 struct PFPU
@@ -80,8 +88,8 @@
 {
 	int	fpstate;
 	int	kfpstate;
-	FPsave	*fpsave;
-	FPsave	*kfpsave;
+	FPalloc	*fpsave;
+	FPalloc	*kfpsave;
 };
 
 enum
@@ -92,7 +100,7 @@
 	FPprotected,
 
 	/* bits or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
@@ -160,7 +168,7 @@
 	PMach;
 
 	int	fpstate;
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 
 	int	cputype;
 	ulong	delayloop;
--- a/sys/src/9/imx8/fns.h
+++ b/sys/src/9/imx8/fns.h
@@ -95,8 +95,10 @@
 extern void fpuprocfork(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpuprocrestore(Proc*);
-extern FPsave* fpukenter(Ureg*);
-extern void fpukexit(Ureg*, FPsave*);
+extern FPalloc* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, FPalloc*);
+extern void fpunotify(Proc*);
+extern void fpunoted(Proc*);
 extern void mathtrap(Ureg*);
 
 /* trap */
--- a/sys/src/9/kw/dat.h
+++ b/sys/src/9/kw/dat.h
@@ -73,7 +73,7 @@
 	FPactive,
 	FPinactive,
 
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
--- a/sys/src/9/kw/fns.h
+++ b/sys/src/9/kw/fns.h
@@ -91,7 +91,7 @@
 extern int fpudevprocio(Proc*, void*, long, uintptr, int);
 extern void fpuinit(void);
 extern void fpunoted(void);
-extern void fpunotify(Ureg*);
+extern void fpunotify(void);
 extern void fpuprocrestore(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpusysprocsetup(Proc*);
--- a/sys/src/9/kw/softfpu.c
+++ b/sys/src/9/kw/softfpu.c
@@ -20,7 +20,7 @@
 }
 
 void
-fpunotify(Ureg*)
+fpunotify(void)
 {
 	/*
 	 * Called when a note is about to be delivered to a
@@ -39,6 +39,12 @@
 	 * noted() routine.
 	 * Clear the flag set above in fpunotify().
 	 */
+}
+
+FPsave*
+notefpsave(Proc*)
+{
+	return nil;
 }
 
 void
--- a/sys/src/9/kw/syscall.c
+++ b/sys/src/9/kw/syscall.c
@@ -36,7 +36,10 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
+
+	splhi();
 	fpunoted();
+	spllo();
 
 	nf = up->ureg;
 
@@ -98,7 +101,6 @@
 int
 notify(Ureg* ureg)
 {
-	u32int s;
 	uintptr sp;
 	NFrame *nf;
 	char *msg;
@@ -108,9 +110,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	fpunotify(ureg);
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -145,8 +145,9 @@
 	ureg->pc = (uintptr)up->notify;
 	ureg->r0 = (uintptr)nf->arg0;
 
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 
 	return 1;
 }
--- a/sys/src/9/lx2k/dat.h
+++ b/sys/src/9/lx2k/dat.h
@@ -20,6 +20,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -74,6 +75,13 @@
 	ulong	status;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;	/* when context nests */
+};
+
 #define KFPSTATE
 
 struct PFPU
@@ -80,8 +88,8 @@
 {
 	int	fpstate;
 	int	kfpstate;
-	FPsave	*fpsave;
-	FPsave	*kfpsave;
+	FPalloc	*fpsave;
+	FPalloc	*kfpsave;
 };
 
 enum
@@ -92,7 +100,7 @@
 	FPprotected,
 
 	/* bits or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
@@ -160,7 +168,7 @@
 	PMach;
 
 	int	fpstate;
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 
 	int	cputype;
 	ulong	delayloop;
--- a/sys/src/9/lx2k/fns.h
+++ b/sys/src/9/lx2k/fns.h
@@ -95,8 +95,10 @@
 extern void fpuprocfork(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpuprocrestore(Proc*);
-extern FPsave* fpukenter(Ureg*);
-extern void fpukexit(Ureg*, FPsave*);
+extern FPalloc* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, FPalloc*);
+extern void fpunotify(Proc*);
+extern void fpunoted(Proc*);
 extern void mathtrap(Ureg*);
 
 /* trap */
--- a/sys/src/9/mt7688/dat.h
+++ b/sys/src/9/mt7688/dat.h
@@ -130,11 +130,11 @@
 	/* floating point state */
 	FPinit,
 	FPactive,
-	FPinactive,
+	FPinactive,	/* fpsave valid when fpstate >= FPincative */
 	FPemu,
 
-	/* bit meaning floating point illegal */
-	FPillegal= 0x100,
+	/* bit meaning floating point in note handler */
+	FPnotify = 0x100,
 };
 
 /*
--- a/sys/src/9/mt7688/fns.h
+++ b/sys/src/9/mt7688/fns.h
@@ -42,7 +42,6 @@
 ulong	rdcompare(void);
 ulong	rdcount(void);
 ulong*	reg(Ureg*, int);
-void	restfpregs(FPsave*, ulong);
 void	intrenable(int, void(*)(Ureg *, void *), void *, int, char*);
 void	intrdisable(int, void (*)(Ureg*, void *), void*, int, char*);
 void	screeninit(void);
--- a/sys/src/9/mt7688/fpimips.c
+++ b/sys/src/9/mt7688/fpimips.c
@@ -1289,7 +1289,7 @@
 		return -1;
 	}
 
-	if(up->fpstate & FPillegal)
+	if(up->fpstate & FPnotify)
 		error("floating point in note handler");
 	if(up->fpsave->fpdelayexec)
 		panic("fpuemu: entered with outstanding watch trap");
--- a/sys/src/9/mt7688/main.c
+++ b/sys/src/9/mt7688/main.c
@@ -31,7 +31,7 @@
 static void
 checkconf0(void)
 {
-	iprint("frc0 check = %uX \n", getfcr0);
+	iprint("frc0 check = %p\n", getfcr0);
 // for debug stuff
 }
 
@@ -110,8 +110,6 @@
 void
 main(void)
 {
-	savefpregs(&initfp);
-
 	uartconsinit();
 	quotefmtinstall();
 
--- a/sys/src/9/mt7688/syscall.c
+++ b/sys/src/9/mt7688/syscall.c
@@ -118,11 +118,27 @@
 
 }
 
+void
+fpunotify(void)
+{
+	up->fpstate |= FPnotify;
+}
 
+void
+fpunoted(void)
+{
+	up->fpstate &= ~FPnotify;
+}
+
+FPsave*
+notefpsave(Proc *p)
+{
+	return nil;
+}
+
 int
 notify(Ureg *ur)
 {
-	int s;
 	ulong sp;
 	char *msg;
 
@@ -131,17 +147,15 @@
 	if(up->nnote == 0)
 		return 0;
 
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
-	up->fpstate |= FPillegal;
 	msg = popnote(ur);
 	if(msg == nil){
 		qunlock(&up->debug);
-		splx(s);
+		splhi();
 		return 0;
 	}
 
-
 	sp = ur->usp - sizeof(Ureg) - BY2WD; /* spim libc */
 
 	if(!okaddr((ulong)up->notify, BY2WD, 0) ||
@@ -170,8 +184,9 @@
 	 */
 	ur->pc = (ulong)up->notify;
 
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
+	splhi();
 	return 1;
 }
 
@@ -193,7 +208,7 @@
 	}
 	up->notified = 0;
 
-	up->fpstate &= ~FPillegal;
+	fpunoted();
 
 	nur = up->ureg;
 
--- a/sys/src/9/mt7688/trap.c
+++ b/sys/src/9/mt7688/trap.c
@@ -195,7 +195,7 @@
 	case CCPU:
 		cop = (ur->cause>>28)&3;
 		if(user && up && cop == 1) {
-			if(up->fpstate & FPillegal) {
+			if(up->fpstate & FPnotify) {
 				/* someone used floating point in a note handler */
 				postnote(up, 1,
 					"sys: floating point in note handler",
--- a/sys/src/9/omap/dat.h
+++ b/sys/src/9/omap/dat.h
@@ -91,7 +91,7 @@
 	FPactive,
 	FPinactive,
 
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
--- a/sys/src/9/omap/fns.h
+++ b/sys/src/9/omap/fns.h
@@ -106,7 +106,7 @@
 extern int fpudevprocio(Proc*, void*, long, uintptr, int);
 extern void fpuinit(void);
 extern void fpunoted(void);
-extern void fpunotify(Ureg*);
+extern void fpunotify(void);
 extern void fpuprocrestore(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpusysprocsetup(Proc*);
--- a/sys/src/9/omap/softfpu.c
+++ b/sys/src/9/omap/softfpu.c
@@ -20,7 +20,7 @@
 }
 
 void
-fpunotify(Ureg*)
+fpunotify(void)
 {
 	/*
 	 * Called when a note is about to be delivered to a
@@ -39,6 +39,12 @@
 	 * noted() routine.
 	 * Clear the flag set above in fpunotify().
 	 */
+}
+
+FPsave*
+notefpsave(Proc*)
+{
+	return nil;
 }
 
 void
--- a/sys/src/9/omap/syscall.c
+++ b/sys/src/9/omap/syscall.c
@@ -36,7 +36,10 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
+
+	splhi();
 	fpunoted();
+	spllo();
 
 	nf = up->ureg;
 
@@ -98,7 +101,6 @@
 int
 notify(Ureg* ureg)
 {
-	u32int s;
 	uintptr sp;
 	NFrame *nf;
 	char *msg;
@@ -108,9 +110,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	fpunotify(ureg);
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -145,8 +145,9 @@
 	ureg->pc = (uintptr)up->notify;
 	ureg->r0 = (uintptr)nf->arg0;
 
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 
 	return 1;
 }
--- a/sys/src/9/pc/dat.h
+++ b/sys/src/9/pc/dat.h
@@ -2,6 +2,7 @@
 typedef struct BIOS32ci	BIOS32ci;
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef union FPsave	FPsave;
 typedef struct FPx87state FPx87state;
 typedef struct FPssestate FPssestate;
@@ -90,10 +91,17 @@
 	FPssestate;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;
+};
+
 struct PFPU
 {
 	int	fpstate;
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 };
 
 enum
@@ -101,10 +109,10 @@
 	/* this is a state */
 	FPinit=		0,
 	FPactive=	1,
-	FPinactive=	2,
+	FPinactive=	2,	/* fpsave valid when fpstate >= FPincative */
 
 	/* the following is a bit that can be or'd into the state */
-	FPillegal=	0x100,
+	FPnotify=	0x100,
 };
 
 struct Confmem
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -20,6 +20,8 @@
 void	fpuprocfork(Proc*);
 void	fpuprocsave(Proc*);
 void	fpuprocrestore(Proc*);
+void	fpunotify(Proc*);
+void	fpunoted(Proc*);
 int	cpuidentify(void);
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
--- a/sys/src/9/pc/fpu.c
+++ b/sys/src/9/pc/fpu.c
@@ -224,6 +224,26 @@
 	mathnote(up->fpsave->mxcsr & 0x3f, ureg->pc);
 }
 
+static FPalloc*
+fpalloc(FPalloc *link)
+{
+	FPalloc *a;
+
+	while((a = mallocalign(sizeof(FPalloc), FPalign, 0, 0)) == nil){
+		int x = spllo();
+		resrcwait("no memory for FPalloc");
+		splx(x);
+	}
+	a->link = link;
+	return a;
+}
+
+static void
+fpfree(FPalloc *a)
+{
+	free(a);
+}
+
 /*
  *  math coprocessor emulation fault
  */
@@ -232,20 +252,24 @@
 {
 	ulong status, control;
 
-	if(up->fpstate & FPillegal){
-		/* someone did floating point in a note handler */
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
 	switch(up->fpstate){
+	case FPinit|FPnotify:
+		/* wet floor */
 	case FPinit:
+		if(up->fpsave == nil)
+			up->fpsave = fpalloc(nil);
 		fpinit();
 		if(fpsave == fpssesave)
 			ldmxcsr(0x1f80);	/* no simd exceptions on 386 */
-		while(up->fpsave == nil)
-			up->fpsave = mallocalign(sizeof(FPsave), FPalign, 0, 0);
 		up->fpstate = FPactive;
 		break;
+	case FPinactive|FPnotify:
+		spllo();
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		splhi();
+		/* wet floor */
 	case FPinactive:
 		/*
 		 * Before restoring the state, check for any pending
@@ -320,8 +344,18 @@
 void
 fpuprocsetup(Proc *p)
 {
+	FPalloc *a;
+	int s;
+
+	s = splhi();
 	p->fpstate = FPinit;
 	fpoff();
+	splx(s);
+
+	while((a = p->fpsave) != nil) {
+		p->fpsave = a->link;
+		fpfree(a);
+	}
 }
 
 void
@@ -330,13 +364,14 @@
 	int s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPactive:
 		fpsave(up->fpsave);
 		up->fpstate = FPinactive;
+		/* wet floor */
 	case FPinactive:
-		while(p->fpsave == nil)
-			p->fpsave = mallocalign(sizeof(FPsave), FPalign, 0, 0);
+		if(p->fpsave == nil)
+			p->fpsave = fpalloc(nil);
 		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
 		p->fpstate = FPinactive;
 	}
@@ -346,19 +381,27 @@
 void
 fpuprocsave(Proc *p)
 {
-	if(p->fpstate == FPactive){
-		if(p->state == Moribund)
+	if(p->state == Moribund){
+		FPalloc *a;
+
+		if(p->fpstate == FPactive)
 			fpclear();
-		else{
-			/*
-			 * Fpsave() stores without handling pending
-			 * unmasked exeptions. Postnote() can't be called
-			 * so the handling of pending exceptions is delayed
-			 * until the process runs again and generates an
-			 * emulation fault to activate the FPU.
-			 */
-			fpsave(p->fpsave);
+		p->fpstate = FPinit;
+		while((a = p->fpsave) != nil) {
+			p->fpsave = a->link;
+			fpfree(a);
 		}
+		return;
+	}
+	if(p->fpstate == FPactive){
+		/*
+		 * Fpsave() stores without handling pending
+		 * unmasked exeptions. Postnote() can't be called
+		 * so the handling of pending exceptions is delayed
+		 * until the process runs again and generates an
+		 * emulation fault to activate the FPU.
+		 */
+		fpsave(p->fpsave);
 		p->fpstate = FPinactive;
 	}
 }
@@ -366,4 +409,44 @@
 void
 fpuprocrestore(Proc*)
 {
+}
+
+void
+fpunotify(Proc *p)
+{
+	fpuprocsave(p);
+	p->fpstate |= FPnotify;
+}
+
+void
+fpunoted(Proc *p)
+{
+	if(p->fpstate & FPnotify){
+		p->fpstate &= ~FPnotify;
+	} else {
+		FPalloc *o;
+
+		if(p->fpstate == FPactive)
+			fpclear();
+		if((o = p->fpsave->link) != nil) {
+			fpfree(p->fpsave);
+			p->fpsave = o;
+			p->fpstate = FPinactive;
+		} else {
+			p->fpstate = FPinit;
+		}
+	}
+}
+
+FPsave*
+notefpsave(Proc *p)
+{
+	if(p->fpsave == nil)
+		return nil;
+	if(p->fpstate == (FPinactive|FPnotify)){
+		p->fpsave = fpalloc(p->fpsave);
+		memmove(p->fpsave, p->fpsave->link, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	return p->fpsave->link;
 }
--- a/sys/src/9/pc/trap.c
+++ b/sys/src/9/pc/trap.c
@@ -558,7 +558,7 @@
 int
 notify(Ureg* ureg)
 {
-	ulong s, sp;
+	ulong sp;
 	char *msg;
 
 	if(up->procctl)
@@ -566,13 +566,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	if(up->fpstate == FPactive){
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
-	}
-	up->fpstate |= FPillegal;
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -607,8 +601,10 @@
 	ureg->pc = (ulong)up->notify;
 	ureg->cs = UESEL;
 	ureg->ss = ureg->ds = ureg->es = UDSEL;
+
+	splhi();
+	fpunotify(up);
 	qunlock(&up->debug);
-	splx(s);
 	return 1;
 }
 
@@ -629,9 +625,11 @@
 	}
 	up->notified = 0;
 
-	nureg = up->ureg;	/* pointer to user returned Ureg struct */
+	splhi();
+	fpunoted(up);
+	spllo();
 
-	up->fpstate &= ~FPillegal;
+	nureg = up->ureg;	/* pointer to user returned Ureg struct */
 
 	/* sanity clause */
 	oureg = (ulong)nureg;
--- a/sys/src/9/pc64/dat.h
+++ b/sys/src/9/pc64/dat.h
@@ -2,6 +2,7 @@
 typedef struct Confmem	Confmem;
 typedef struct FPssestate	FPssestate;
 typedef struct FPavxstate	FPavxstate;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -76,14 +77,21 @@
 	FPavxstate;
 };
 
+struct FPalloc
+{
+	FPsave;
+
+	FPalloc	*link;		/* when context nests */
+};
+
 enum
 {
 	FPinit,
 	FPactive,
-	FPinactive,
 	FPprotected,
+	FPinactive,		/* fpsave valid when fpstate >= FPincative */
 
-	FPillegal=	0x100,	/* fp forbidden in note handler */
+	FPnotify = 0x100,	/* fp in note handler */
 };
 
 #define KFPSTATE
@@ -92,8 +100,8 @@
 {
 	int	fpstate;
 	int	kfpstate;
-	FPsave	*fpsave;
-	FPsave	*kfpsave;
+	FPalloc	*fpsave;
+	FPalloc	*kfpsave;
 };
 
 struct Confmem
@@ -204,7 +212,7 @@
 	char	havenx;
 
 	int	fpstate;		/* FPU state for interrupts */
-	FPsave	*fpsave;
+	FPalloc	*fpsave;
 
 	u64int*	pml4;			/* pml4 base for this processor (va) */
 	Tss*	tss;			/* tss for this processor */
--- a/sys/src/9/pc64/fns.h
+++ b/sys/src/9/pc64/fns.h
@@ -35,13 +35,14 @@
 void	(*fprestore)(FPsave*);
 void	(*fpsave)(FPsave*);
 void	fpinit(void);
-FPsave*	fpukenter(Ureg*);
-void	fpukexit(Ureg*, FPsave*);
+FPalloc*fpukenter(Ureg*);
+void	fpukexit(Ureg*, FPalloc*);
 void	fpuprocfork(Proc*);
 void	fpuprocrestore(Proc*);
 void	fpuprocsave(Proc*);
 void	fpuprocsetup(Proc*);
-
+void	fpunotify(Proc*);
+void	fpunoted(Proc*);
 u64int	getcr0(void);
 u64int	getcr2(void);
 u64int	getcr3(void);
--- a/sys/src/9/pc64/fpu.c
+++ b/sys/src/9/pc64/fpu.c
@@ -243,29 +243,36 @@
 	_stts();
 }
 
-static FPsave*
-fpalloc(void)
+static FPalloc*
+fpalloc(FPalloc *link)
 {
-	FPsave *save;
+	FPalloc *a;
 
-	while((save = mallocalign(sizeof(FPsave), FPalign, 0, 0)) == nil){
-		spllo();
-		resrcwait("no memory for FPsave");
-		splhi();
+	while((a = mallocalign(sizeof(FPalloc), FPalign, 0, 0)) == nil){
+		int x = spllo();
+		resrcwait("no memory for FPalloc");
+		splx(x);
 	}
-	return save;
+	a->link = link;
+	return a;
 }
 
 static void
-fpfree(FPsave *save)
+fpfree(FPalloc *a)
 {
-	free(save);
+	free(a);
 }
 
 void
 fpuprocsetup(Proc *p)
 {
+	FPalloc *a;
+
 	p->fpstate = FPinit;
+	while((a = p->fpsave) != nil){
+		p->fpsave = a->link;
+		fpfree(a);
+	}
 }
 
 void
@@ -274,7 +281,7 @@
 	int s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPprotected:
 		_clts();
 		/* wet floor */
@@ -284,7 +291,7 @@
 		/* wet floor */
 	case FPinactive:
 		if(p->fpsave == nil)
-			p->fpsave = fpalloc();
+			p->fpsave = fpalloc(nil);
 		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
 		p->fpstate = FPinactive;
 	}
@@ -295,14 +302,21 @@
 fpuprocsave(Proc *p)
 {
 	if(p->state == Moribund){
+		FPalloc *a;
+
 		if(p->fpstate == FPactive || p->kfpstate == FPactive){
 			_fnclex();
 			_stts();
 		}
-		fpfree(p->fpsave);
-		fpfree(p->kfpsave);
-		p->fpsave = p->kfpsave = nil;
 		p->fpstate = p->kfpstate = FPinit;
+		while((a = p->fpsave) != nil){
+			p->fpsave = a->link;
+			fpfree(a);
+		}
+		while((a = p->kfpsave) != nil){
+			p->kfpsave = a->link;
+			fpfree(a);
+		}
 		return;
 	}
 	if(p->kfpstate == FPactive){
@@ -344,7 +358,7 @@
  *  are handled between fpukenter() and fpukexit(),
  *  so they can use floating point and vector instructions.
  */
-FPsave*
+FPalloc*
 fpukenter(Ureg *)
 {
 	if(up == nil){
@@ -380,7 +394,7 @@
 }
 
 void
-fpukexit(Ureg *ureg, FPsave *save)
+fpukexit(Ureg *ureg, FPalloc *o)
 {
 	if(up == nil){
 		switch(m->fpstate){
@@ -392,8 +406,8 @@
 			fpfree(m->fpsave);
 			m->fpstate = FPinit;
 		}
-		m->fpsave = save;
-		if(save != nil)
+		m->fpsave = o;
+		if(o != nil)
 			m->fpstate = FPinactive;
 		return;
 	}
@@ -415,11 +429,47 @@
 		fpfree(up->kfpsave);
 		up->kfpstate = FPinit;
 	}
-	up->kfpsave = save;
-	if(save != nil)
+	up->kfpsave = o;
+	if(o != nil)
 		up->kfpstate = FPinactive;
 }
 
+void
+fpunotify(Proc *p)
+{
+	fpuprocsave(p);
+	p->fpstate |= FPnotify;
+}
+
+void
+fpunoted(Proc *p)
+{
+	FPalloc *o;
+
+	if(p->fpstate & FPnotify) {
+		p->fpstate &= ~FPnotify;
+	} else if((o = p->fpsave->link) != nil) {
+		fpfree(p->fpsave);
+		p->fpsave = o;
+		p->fpstate = FPinactive;
+	} else {
+		p->fpstate = FPinit;
+	}
+}
+
+FPsave*
+notefpsave(Proc *p)
+{
+	if(p->fpsave == nil)
+		return nil;
+	if(p->fpstate == (FPinactive|FPnotify)){
+		p->fpsave = fpalloc(p->fpsave);
+		memmove(p->fpsave, p->fpsave->link, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	return p->fpsave->link;
+}
+
 /*
  *  Before restoring the state, check for any pending
  *  exceptions, there's no way to restore the state without
@@ -451,7 +501,7 @@
 		if(up == nil){
 			switch(m->fpstate){
 			case FPinit:
-				m->fpsave = fpalloc();
+				m->fpsave = fpalloc(m->fpsave);
 				m->fpstate = FPactive;
 				fpinit();
 				break;
@@ -474,7 +524,7 @@
 
 		switch(up->kfpstate){
 		case FPinit:
-			up->kfpsave = fpalloc();
+			up->kfpsave = fpalloc(up->kfpsave);
 			up->kfpstate = FPactive;
 			fpinit();
 			break;
@@ -489,17 +539,22 @@
 		return;
 	}
 
-	if(up->fpstate & FPillegal){
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
-	}
 	switch(up->fpstate){
+	case FPinit|FPnotify:
+		/* wet floor */
 	case FPinit:
 		if(up->fpsave == nil)
-			up->fpsave = fpalloc();
+			up->fpsave = fpalloc(nil);
 		up->fpstate = FPactive;
 		fpinit();
 		break;
+	case FPinactive|FPnotify:
+		spllo();
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		splhi();
+		/* wet floor */
 	case FPinactive:
 		if(fpcheck(up->fpsave, 0))
 			break;
--- a/sys/src/9/pc64/trap.c
+++ b/sys/src/9/pc64/trap.c
@@ -133,7 +133,7 @@
 trap(Ureg *ureg)
 {
 	int vno, user;
-	FPsave *f = nil;
+	FPalloc *f = nil;
 
 	vno = ureg->type;
 	user = kenter(ureg);
@@ -522,14 +522,10 @@
 		 * to it when returning form syscall()
 		 */
 		((void**)&ureg)[-1] = (void*)noteret;
-
 		noted(ureg, *((ulong*)up->s.args));
-		splhi();
-		up->fpstate &= ~FPillegal;
 	}
-	else
-		splhi();
 
+	splhi();
 	if(scallnr != RFORK && (up->procctl || up->nnote) && notify(ureg))
 		((void**)&ureg)[-1] = (void*)noteret;	/* loads RARG */
 
@@ -595,10 +591,11 @@
 	ureg->bp = (uintptr)up->ureg;		/* arg1 passed in RARG */
 	ureg->cs = UESEL;
 	ureg->ss = UDSEL;
-	qunlock(&up->debug);
+
 	splhi();
-	fpuprocsave(up);
-	up->fpstate |= FPillegal;
+	fpunotify(up);
+	qunlock(&up->debug);
+
 	return 1;
 }
 
@@ -618,6 +615,10 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
+
+	splhi();
+	fpunoted(up);
+	spllo();
 
 	nureg = up->ureg;	/* pointer to user returned Ureg struct */
 
--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -24,6 +24,7 @@
 	Qkregs,
 	Qmem,
 	Qnote,
+	Qnotefpregs,
 	Qnoteid,
 	Qnotepg,
 	Qns,
@@ -95,6 +96,7 @@
 	"kregs",	{Qkregs},	sizeof(Ureg),		0400,
 	"mem",		{Qmem},		0,			0000,
 	"note",		{Qnote},	0,			0000,
+	"notefpregs",	{Qnotefpregs},	sizeof(FPsave),		0000,
 	"noteid",	{Qnoteid},	0,			0664,
 	"notepg",	{Qnotepg},	0,			0000,
 	"ns",		{Qns},		0,			0444,
@@ -455,6 +457,7 @@
 	case Qmem:
 	case Qregs:
 	case Qfpregs:
+	case Qnotefpregs:
 	case Qprofile:
 	case Qsyscall:	
 	case Qwatchpt:
@@ -978,16 +981,23 @@
 		goto regread;
 #ifdef KFPSTATE
 	case Qkfpregs:
-		if(p->kfpstate != FPinactive)
+		if(p->kfpstate < FPinactive)
 			error(Enoreg);
 		rptr = (uchar*)p->kfpsave;
-		rsize = sizeof(FPsave);
-		goto regread;
+		goto fpregread;
 #endif
+	case Qnotefpregs:
+		if(!p->notified)
+			error(Enoreg);
+		rptr = (uchar*)notefpsave(p);
+		if(rptr != nil)
+			goto fpregread;
+		/* wet floor */
 	case Qfpregs:
-		if(p->fpstate != FPinactive)
+		if((p->fpstate & ~FPnotify) < FPinactive)
 			error(Enoreg);
 		rptr = (uchar*)p->fpsave;
+	fpregread:
 		rsize = sizeof(FPsave);
 	regread:
 		if(rptr == nil)
@@ -1167,6 +1177,7 @@
 {
 	char buf[ERRMAX];
 	ulong offset;
+	uchar *rptr;
 	Proc *p;
 
 	offset = off;
@@ -1219,14 +1230,28 @@
 		setregisters(p->dbgreg, (char*)(p->dbgreg)+offset, va, n);
 		break;
 
+	case Qnotefpregs:
+		if(!p->notified)
+			error(Enoreg);
+		rptr = (uchar*)notefpsave(p);
+		if(rptr != nil)
+			goto fpregwrite;
+		else {
 	case Qfpregs:
+			if(p->notified)
+				notefpsave(p);
+		}
+		if((p->fpstate & ~FPnotify) < FPinactive)
+			error(Enoreg);
+		rptr = (uchar*)p->fpsave;
+	fpregwrite:
+		if(rptr == nil)
+			error(Enoreg);
 		if(offset >= sizeof(FPsave))
 			n = 0;
 		else if(offset+n > sizeof(FPsave))
 			n = sizeof(FPsave) - offset;
-		if(p->fpstate != FPinactive || p->fpsave == nil)
-			error(Enoreg);
-		memmove((uchar*)p->fpsave+offset, va, n);
+		memmove(rptr+offset, va, n);
 		break;
 
 	case Qctl:
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -221,6 +221,7 @@
 Proc*		newproc(void);
 _Noreturn void	nexterror(void);
 int		notify(Ureg*);
+FPsave*		notefpsave(Proc*);
 ulong		nkpages(Confmem*);
 uvlong		ns2fastticks(uvlong);
 int		okaddr(uintptr, ulong, int);
@@ -325,7 +326,6 @@
 long		rtctime(void);
 void		runlock(RWlock*);
 Proc*		runproc(void);
-void		savefpregs(FPsave*);
 void		sched(void);
 _Noreturn void	schedinit(void);
 void		(*screenputs)(char*, int);
--- a/sys/src/9/sgi/dat.h
+++ b/sys/src/9/sgi/dat.h
@@ -122,10 +122,10 @@
 	FPinit,
 	FPactive,
 	FPinactive,
-	FPemu,
+	FPemu,		/* fpsave valid when fpstate >= FPincative */
 
-	/* bit meaning floating point illegal */
-	FPillegal= 0x100,
+	/* bit meaning floating point in note handler */
+	FPnotify = 0x100,
 };
 
 /*
--- a/sys/src/9/sgi/fns.h
+++ b/sys/src/9/sgi/fns.h
@@ -46,6 +46,7 @@
 ulong*	reg(Ureg*, int);
 void	restfpregs(FPsave*, ulong);
 void	intrenable(int, void(*)(Ureg *, void *), void *);
+void	savefpregs(FPsave*);
 void	screeninit(void);
 void	setpagemask(ulong);
 void	setwired(ulong);
--- a/sys/src/9/sgi/main.c
+++ b/sys/src/9/sgi/main.c
@@ -301,7 +301,7 @@
 	int s;
 
 	s = splhi();
-	switch(up->fpstate & ~FPillegal){
+	switch(up->fpstate & ~FPnotify){
 	case FPactive:
 		savefpregs(up->fpsave);
 		up->fpstate = FPinactive;
--- a/sys/src/9/sgi/trap.c
+++ b/sys/src/9/sgi/trap.c
@@ -210,7 +210,7 @@
 	case CCPU:
 		cop = (ur->cause>>28)&3;
 		if(user && up && cop == 1) {
-			if(up->fpstate & FPillegal) {
+			if(up->fpstate & FPnotify) {
 				/* someone used floating point in a note handler */
 				postnote(up, 1,
 					"sys: floating point in note handler",
@@ -442,10 +442,31 @@
 			regname[i+1].name, R(ur, i+1));
 }
 
+void
+fpunotify(void)
+{
+	if(up->fpstate == FPactive){
+		savefpregs(up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPnotify;
+}
+
+void
+fpunoted(void)
+{
+	up->fpstate &= ~FPnotify;
+}
+
+FPsave*
+notefpsave(Proc*)
+{
+	return nil;
+}
+
 int
 notify(Ureg *ur)
 {
-	int s;
 	ulong sp;
 	char *msg;
 
@@ -454,18 +475,12 @@
 	if(up->nnote == 0)
 		return 0;
 
-	if(up->fpstate == FPactive){
-		savefpregs(up->fpsave);
-		up->fpstate = FPinactive;
-	}
-	up->fpstate |= FPillegal;
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ur);
 	if(msg == nil){
 		qunlock(&up->debug);
-		splx(s);
+		splhi();
 		return 0;
 	}
 
@@ -498,8 +513,9 @@
 	 */
 	ur->pc = (ulong)up->notify;
 
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 	return 1;
 }
 
@@ -520,10 +536,11 @@
 	}
 	up->notified = 0;
 
-	up->fpstate &= ~FPillegal;
+	splhi();
+	fpunoted();
+	spllo();
 
 	nur = up->ureg;
-
 	oureg = (ulong)nur;
 	if((oureg & (BY2WD-1)) || !okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
 		pprint("bad up->ureg in noted or call to noted() when not notified\n");
--- a/sys/src/9/teg2/dat.h
+++ b/sys/src/9/teg2/dat.h
@@ -21,6 +21,7 @@
 
 typedef struct Conf	Conf;
 typedef struct Confmem	Confmem;
+typedef struct FPalloc	FPalloc;
 typedef struct FPsave	FPsave;
 typedef struct PFPU	PFPU;
 typedef struct ISAConf	ISAConf;
@@ -87,8 +88,14 @@
 	 * each must be able to hold an Internal from fpi.h for sw emulation.
 	 */
 	ulong	regs[Maxfpregs][3];
+};
 
-	int	fpstate;
+struct FPalloc
+{
+	FPsave;
+
+	/* the following fields are not visible to devproc */
+	int	fpstate;	/* FPinactive or FPemu */
 	uintptr	pc;		/* of failed fp instr. */
 };
 
@@ -95,7 +102,8 @@
 struct PFPU
 {
 	int	fpstate;
-	FPsave	fpsave[1];
+	FPalloc	*fpsave;
+	FPalloc	*ofpsave;
 };
 
 enum
@@ -102,11 +110,11 @@
 {
 	FPinit,
 	FPactive,
-	FPinactive,
+	FPinactive,	/* fpsave valid when fpstate >= FPincative */
 	FPemu,
 
 	/* bit or'd with the state */
-	FPillegal= 0x100,
+	FPnotify = 0x100,
 };
 
 struct Confmem
--- a/sys/src/9/teg2/fns.h
+++ b/sys/src/9/teg2/fns.h
@@ -144,7 +144,7 @@
 extern int fpudevprocio(Proc*, void*, long, uintptr, int);
 extern void fpuinit(void);
 extern void fpunoted(void);
-extern void fpunotify(Ureg*);
+extern void fpunotify(void);
 extern void fpuprocrestore(Proc*);
 extern void fpuprocsave(Proc*);
 extern void fpusysprocsetup(Proc*);
--- a/sys/src/9/teg2/softfpu.c
+++ b/sys/src/9/teg2/softfpu.c
@@ -20,7 +20,7 @@
 }
 
 void
-fpunotify(Ureg*)
+fpunotify(void)
 {
 	/*
 	 * Called when a note is about to be delivered to a
--- a/sys/src/9/teg2/syscall.c
+++ b/sys/src/9/teg2/syscall.c
@@ -45,7 +45,6 @@
 	fpunoted();
 
 	nf = up->ureg;
-
 	/* sanity clause */
 	if(!okaddr((uintptr)nf, sizeof(NFrame), 0)){
 		qunlock(&up->debug);
@@ -104,7 +103,6 @@
 int
 notify(Ureg* ureg)
 {
-	u32int s;
 	uintptr sp;
 	NFrame *nf;
 	char *msg;
@@ -114,9 +112,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	fpunotify(ureg);
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -151,8 +147,9 @@
 	ureg->pc = (uintptr)up->notify;
 	ureg->r0 = (uintptr)nf->arg0;
 
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 
 	l1cache->wb();				/* is this needed? */
 	return 1;
--- a/sys/src/9/xen/fns.h
+++ b/sys/src/9/xen/fns.h
@@ -15,6 +15,8 @@
 void	fpuprocfork(Proc*);
 void	fpuprocsave(Proc*);
 void	fpuprocrestore(Proc*);
+void	fpunotify(Proc*);
+void	fpunoted(Proc*);
 int		cpuidentify(void);
 void	cpuidprint(void);
 void	(*cycles)(uvlong*);
--- a/sys/src/9/xen/trap.c
+++ b/sys/src/9/xen/trap.c
@@ -470,7 +470,7 @@
 int
 notify(Ureg* ureg)
 {
-	ulong s, sp;
+	ulong sp;
 	char *msg;
 
 	if(up->procctl)
@@ -478,13 +478,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	if(up->fpstate == FPactive){
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
-	}
-	up->fpstate |= FPillegal;
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -515,8 +509,9 @@
 	*(ulong*)(sp+0*BY2WD) = 0;			/* arg 0 is pc */
 	ureg->usp = sp;
 	ureg->pc = (ulong)up->notify;
+	splhi();
+	fpunotify(up);
 	qunlock(&up->debug);
-	splx(s);
 	return 1;
 }
 
@@ -537,9 +532,11 @@
 	}
 	up->notified = 0;
 
-	nureg = up->ureg;	/* pointer to user returned Ureg struct */
+	splhi();
+	fpunoted(up);
+	spllo();
 
-	up->fpstate &= ~FPillegal;
+	nureg = up->ureg;	/* pointer to user returned Ureg struct */
 
 	/* sanity clause */
 	oureg = (ulong)nureg;
--- a/sys/src/9/zynq/dat.h
+++ b/sys/src/9/zynq/dat.h
@@ -46,7 +46,8 @@
 struct PFPU
 {
 	int	fpstate;
-	FPsave	fpsave[1];
+	FPsave	*fpsave;
+	FPsave	*ofpsave;
 };
 
 enum
@@ -54,7 +55,7 @@
 	FPinit,
 	FPactive,
 	FPinactive,
-	FPillegal = 0x100
+	FPnotify = 0x100
 };
 
 struct Confmem
--- a/sys/src/9/zynq/main.c
+++ b/sys/src/9/zynq/main.c
@@ -43,30 +43,6 @@
 	}
 }
 
-void
-procfork(Proc *p)
-{
-	ulong s;
-
-	s = splhi();
-	switch(up->fpstate & ~FPillegal){
-	case FPactive:
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
-	case FPinactive:
-		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
-		p->fpstate = FPinactive;
-	}
-	splx(s);
-}
-
-void
-procsetup(Proc *p)
-{
-	p->fpstate = FPinit;
-	fpoff();
-}
-
 ulong *l2;
 
 void
--- a/sys/src/9/zynq/trap.c
+++ b/sys/src/9/zynq/trap.c
@@ -116,27 +116,47 @@
 	up->insyscall = insyscall;
 }
 
-static void
-mathtrap(Ureg *, ulong)
+static FPsave*
+fpalloc(void)
 {
-	int s;
+	FPsave *f;
 
-	if((up->fpstate & FPillegal) != 0){
-		postnote(up, 1, "sys: floating point in note handler", NDebug);
-		return;
+	while((f = mallocalign(sizeof(FPsave), FPalign, 0, 0)) == nil){
+		int x = spllo();
+		resrcwait("no memory for FPsave");
+		splx(x);
 	}
+	return f;
+}
+
+static void
+fpfree(FPsave *f)
+{
+	free(f);
+}
+
+static void
+mathtrap(Ureg *, ulong)
+{
 	switch(up->fpstate){
+	case FPinit|FPnotify:
+		/* wet floor */
 	case FPinit:
-		s = splhi();
+		if(up->fpsave == nil)
+			up->fpsave = fpalloc();
 		fpinit();
 		up->fpstate = FPactive;
-		splx(s);
 		break;
+	case FPinactive|FPnotify:
+		spllo();
+		qlock(&up->debug);
+		notefpsave(up);
+		qunlock(&up->debug);
+		splhi();
+		/* wet floor */
 	case FPinactive:
-		s = splhi();
 		fprestore(up->fpsave);
 		up->fpstate = FPactive;
-		splx(s);
 		break;
 	case FPactive:
 		postnote(up, 1, "sys: floating point error", NDebug);
@@ -155,7 +175,6 @@
 	case PsrMund:
 		ureg->pc -= 4;
 		if(user){
-			spllo();
 			if(okaddr(ureg->pc, 4, 0)){
 				opc = *(ulong*)ureg->pc;
 				if((opc & 0x0f000000) == 0x0e000000 || (opc & 0x0e000000) == 0x0c000000){
@@ -277,10 +296,52 @@
 	kexit(ureg);
 }
 
+static void
+fpunotify(void)
+{
+	if(up->fpstate == FPactive){
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPnotify;
+}
+
+static void
+fpunoted(void)
+{
+	if(up->fpstate & FPnotify){
+		up->fpstate &= ~FPnotify;
+	} else {
+		FPsave *o;
+
+		if(up->fpstate == FPactive)
+			fpclear();
+		if((o = up->ofpsave) != nil) {
+			up->ofpsave = nil;
+			fpfree(up->fpsave);
+			up->fpsave = o;
+			up->fpstate = FPinactive;
+		} else {
+			up->fpstate = FPinit;
+		}
+	}
+}
+
+FPsave*
+notefpsave(Proc *p)
+{
+	if(p->fpstate == (FPinactive|FPnotify)){
+		p->ofpsave = fpalloc();
+		memmove(p->ofpsave, p->fpsave, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	return p->ofpsave;
+}
+
 int
 notify(Ureg *ureg)
 {
-	ulong s, sp;
+	ulong sp;
 	char *msg;
 
 	if(up->procctl)
@@ -288,13 +349,7 @@
 	if(up->nnote == 0)
 		return 0;
 
-	if(up->fpstate == FPactive){
-		fpsave(up->fpsave);
-		up->fpstate = FPinactive;
-	}
-	up->fpstate |= FPillegal;
-
-	s = spllo();
+	spllo();
 	qlock(&up->debug);
 	msg = popnote(ureg);
 	if(msg == nil){
@@ -328,8 +383,10 @@
 	ureg->sp = sp;
 	ureg->pc = (uintptr) up->notify;
 	ureg->r14 = 0;
+
+	splhi();
+	fpunotify();
 	qunlock(&up->debug);
-	splx(s);
 	return 1;
 }
 
@@ -346,10 +403,12 @@
 		pexit("Suicide", 0);
 	}
 	up->notified = 0;
-	
-	nureg = up->ureg;
-	up->fpstate &= ~FPillegal;
-	
+
+	splhi();
+	fpunoted();
+	spllo();
+
+	nureg = up->ureg;	
 	oureg = (ulong) nureg;
 	if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 3) != 0){
 		qunlock(&up->debug);
@@ -472,11 +531,20 @@
 void
 procsave(Proc *p)
 {
-	if(p->fpstate == FPactive){
-		if(p->state == Moribund)
+	if(p->state == Moribund){
+		if(p->fpstate == FPactive)
 			fpclear();
-		else
-			fpsave(p->fpsave);
+		p->fpstate = FPinit;
+		if(p->fpsave != nil){
+			fpfree(p->fpsave);
+			p->fpsave = nil;
+		}
+		if(p->ofpsave != nil){
+			fpfree(p->ofpsave);
+			p->ofpsave = nil;
+		}
+	} else if(p->fpstate == FPactive){
+		fpsave(p->fpsave);
 		p->fpstate = FPinactive;
 	}
 	l1switch(&m->l1, 0);
@@ -485,6 +553,46 @@
 void
 procrestore(Proc*)
 {
+}
+
+void
+procfork(Proc *p)
+{
+	int s;
+
+	s = splhi();
+	switch(up->fpstate & ~FPnotify){
+	case FPactive:
+		fpsave(up->fpsave);
+		up->fpstate = FPinactive;
+		/* wet floor */
+	case FPinactive:
+		if(p->fpsave == nil)
+			p->fpsave = fpalloc();
+		memmove(p->fpsave, up->fpsave, sizeof(FPsave));
+		p->fpstate = FPinactive;
+	}
+	splx(s);
+}
+
+void
+procsetup(Proc *p)
+{
+	int s;
+
+	s = splhi();
+	p->fpstate = FPinit;
+	fpoff();
+	splx(s);
+
+	if(p->fpsave != nil){
+		fpfree(p->fpsave);
+		p->fpsave = nil;
+	}
+	if(p->ofpsave != nil){
+		fpfree(p->ofpsave);
+		p->ofpsave = nil;
+	}
 }
 
 void
--