shithub: riscv

Download patch

ref: 3f62e65193766b537a20514c31cc25e527fb1457
parent: b0377e84cf31c48afed765c433abe7eebb4135a5
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jul 8 06:27:43 EDT 2023

imx8: port new FPU code, making FPU available to interrupts

--- a/sys/src/9/imx8/dat.h
+++ b/sys/src/9/imx8/dat.h
@@ -74,11 +74,14 @@
 	ulong	status;
 };
 
+#define KFPSTATE
+
 struct PFPU
 {
-	FPsave	fpsave[1];
-
 	int	fpstate;
+	int	kfpstate;
+	FPsave	*fpsave;
+	FPsave	*kfpsave;
 };
 
 enum
@@ -86,6 +89,7 @@
 	FPinit,
 	FPactive,
 	FPinactive,
+	FPprotected,
 
 	/* bits or'd with the state */
 	FPillegal= 0x100,
@@ -154,6 +158,9 @@
 	MMMU;
 
 	PMach;
+
+	int	fpstate;
+	FPsave	*fpsave;
 
 	int	cputype;
 	ulong	delayloop;
--- a/sys/src/9/imx8/fns.h
+++ b/sys/src/9/imx8/fns.h
@@ -90,11 +90,12 @@
 
 /* fpu */
 extern void fpuinit(void);
-extern void fpoff(void);
-extern void fpinit(void);
-extern void fpclear(void);
-extern void fpsave(FPsave*);
-extern void fprestore(FPsave*);
+extern void fpuprocsetup(Proc*);
+extern void fpuprocfork(Proc*);
+extern void fpuprocsave(Proc*);
+extern void fpuprocrestore(Proc*);
+extern FPsave* fpukenter(Ureg*);
+extern void fpukexit(Ureg*, FPsave*);
 extern void mathtrap(Ureg*);
 
 /* trap */
--- a/sys/src/9/imx8/fpu.c
+++ b/sys/src/9/imx8/fpu.c
@@ -13,65 +13,274 @@
 extern ulong getfsr(void);
 extern void setfsr(ulong fsr);
 
-void
-fpuinit(void)
+static FPsave fpsave0;
+
+static void
+fpsave(FPsave *p)
 {
+	p->control = getfcr();
+	p->status = getfsr();
+	fpsaveregs(p->regs);
 	fpoff();
 }
 
-void
-fpinit(void)
+static void
+fprestore(FPsave *p)
 {
 	fpon();
-	setfcr(0);
-	setfsr(0);
+	setfcr(p->control);
+	setfsr(p->status);
+	fploadregs(p->regs);
 }
 
+static void
+fpinit(void)
+{
+	fprestore(&fpsave0);
+}
+
 void
-fpclear(void)
+fpuinit(void)
 {
+	m->fpstate = FPinit;
+	m->fpsave = nil;
 	fpoff();
 }
 
+static FPsave*
+fpalloc(void)
+{
+	FPsave *save;
+
+	while((save = mallocalign(sizeof(FPsave), 16, 0, 0)) == nil){
+		spllo();
+		resrcwait("no memory for FPsave");
+		splhi();
+	}
+	return save;
+}
+
+static void
+fpfree(FPsave *save)
+{
+	free(save);
+}
+
+
+/*
+ *  Protect or save FPU state and setup new state
+ *  (lazily in the case of user process) for the kernel.
+ *  All syscalls, traps and interrupts (except mathtrap()!)
+ *  are handled between fpukenter() and fpukexit(),
+ *  so they can use floating point and vector instructions.
+ */
+FPsave*
+fpukenter(Ureg*)
+{
+	if(up == nil){
+		switch(m->fpstate){
+		case FPactive:
+			fpsave(m->fpsave);
+			/* wet floor */
+		case FPinactive:
+			m->fpstate = FPinit;
+			return m->fpsave;
+		}
+		return nil;
+	}
+
+	switch(up->fpstate){
+	case FPactive:
+		up->fpstate = FPprotected;
+		fpoff();
+		/* wet floor */
+	case FPprotected:
+		return nil;
+	}
+
+	switch(up->kfpstate){
+	case FPactive:
+		fpsave(up->kfpsave);
+		/* wet floor */
+	case FPinactive:
+		up->kfpstate = FPinit;
+		return up->kfpsave;
+	}
+	return nil;
+}
+
 void
-fpsave(FPsave *p)
+fpukexit(Ureg *ureg, FPsave *save)
 {
-	p->control = getfcr();
-	p->status = getfsr();
-	fpsaveregs(p->regs);
-	fpoff();
+	if(up == nil){
+		switch(m->fpstate){
+		case FPactive:
+			fpoff();
+			/* wet floor */
+		case FPinactive:
+			fpfree(m->fpsave);
+			m->fpstate = FPinit;
+		}
+		m->fpsave = save;
+		if(save != nil)
+			m->fpstate = FPinactive;
+		return;
+	}
+
+	if(up->fpstate == FPprotected){
+		if(userureg(ureg)){
+			up->fpstate = FPactive;
+			fpon();
+		}
+		return;
+	}
+
+	switch(up->kfpstate){
+	case FPactive:
+		fpoff();
+		/* wet floor */
+	case FPinactive:
+		fpfree(up->kfpsave);
+		up->kfpstate = FPinit;
+	}
+	up->kfpsave = save;
+	if(save != nil)
+		up->kfpstate = FPinactive;
 }
 
 void
-fprestore(FPsave *p)
+fpuprocsetup(Proc *p)
 {
-	fpon();
-	setfcr(p->control);
-	setfsr(p->status);
-	fploadregs(p->regs);
+	p->fpstate = FPinit;
 }
 
 void
-mathtrap(Ureg*)
+fpuprocfork(Proc *p)
 {
 	int s;
 
-	if((up->fpstate & FPillegal) != 0){
+	s = splhi();
+	switch(up->fpstate & ~FPillegal){
+	case FPprotected:
+		fpon();
+		/* wet floor */
+	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
+fpuprocsave(Proc *p)
+{
+	if(p->state == Moribund){
+		if(p->fpstate == FPactive || p->kfpstate == FPactive)
+			fpoff();
+		fpfree(p->fpsave);
+		fpfree(p->kfpsave);
+		p->fpsave = p->kfpsave = nil;
+		p->fpstate = p->kfpstate = FPinit;
+		return;
+	}
+	if(p->kfpstate == FPactive){
+		fpsave(p->kfpsave);
+		p->kfpstate = FPinactive;
+		return;
+	}
+	if(p->fpstate == FPprotected)
+		fpon();
+	else if(p->fpstate != FPactive)
+		return;
+	fpsave(p->fpsave);
+	p->fpstate = FPinactive;
+}
+
+void
+fpuprocrestore(Proc*)
+{
+	/*
+	 * when the scheduler switches,
+	 * we can discard its fp state.
+	 */
+	switch(m->fpstate){
+	case FPactive:
+		fpoff();
+		/* wet floor */
+	case FPinactive:
+		fpfree(m->fpsave);
+		m->fpsave = nil;
+		m->fpstate = FPinit;
+	}
+}
+
+void
+mathtrap(Ureg *ureg)
+{
+	if(!userureg(ureg)){
+		if(up == nil){
+			switch(m->fpstate){
+			case FPinit:
+				m->fpsave = fpalloc();
+				m->fpstate = FPactive;
+				fpinit();
+				break;
+			case FPinactive:
+				fprestore(m->fpsave);
+				m->fpstate = FPactive;
+				break;
+			default:
+				panic("floating point error in irq");
+			}
+			return;
+		}
+
+		if(up->fpstate == FPprotected){
+			fpon();
+			fpsave(up->fpsave);
+			up->fpstate = FPinactive;
+		}
+
+		switch(up->kfpstate){
+		case FPinit:
+			up->kfpsave = fpalloc();
+			up->kfpstate = FPactive;
+			fpinit();
+			break;
+		case FPinactive:
+			fprestore(up->kfpsave);
+			up->kfpstate = FPactive;
+			break;
+		default:
+			panic("floating point error in trap");
+		}
+		return;
+	}
+
+	if(up->fpstate & FPillegal){
 		postnote(up, 1, "sys: floating point in note handler", NDebug);
 		return;
 	}
 	switch(up->fpstate){
 	case FPinit:
-		s = splhi();
-		fpinit();
+		if(up->fpsave == nil)
+			up->fpsave = fpalloc();
 		up->fpstate = FPactive;
-		splx(s);
+		fpinit();
 		break;
 	case FPinactive:
-		s = splhi();
 		fprestore(up->fpsave);
 		up->fpstate = FPactive;
-		splx(s);
+		break;
+	case FPprotected:
+		up->fpstate = FPactive;
+		fpon();
 		break;
 	case FPactive:
 		postnote(up, 1, "sys: floating point error", NDebug);
--- a/sys/src/9/imx8/main.c
+++ b/sys/src/9/imx8/main.c
@@ -161,6 +161,9 @@
 	sp[3] = sp[2] = sp[1] = nil;
 	strcpy(sp[1] = (char*)&sp[4], "boot");
 	sp[0] = (void*)&sp[1];
+
+	splhi();
+	fpukexit(nil, nil);
 	touser((uintptr)sp);
 }
 
--- a/sys/src/9/imx8/trap.c
+++ b/sys/src/9/imx8/trap.c
@@ -97,6 +97,7 @@
 void
 trap(Ureg *ureg)
 {
+	FPsave *f = nil;
 	u32int type, intr;
 	int user;
 
@@ -113,6 +114,7 @@
 	case 0x21:	// instruction abort from same level
 	case 0x24:	// data abort from lower level
 	case 0x25:	// data abort from same level
+		f = fpukenter(ureg);
 		faultarm64(ureg);
 		break;
 	case 0x07:	// SIMD/FP
@@ -121,6 +123,7 @@
 		break;
 	case 0x00:	// unknown
 		if(intr == 1){
+			f = fpukenter(ureg);
 			if(irq(ureg) && up != nil && up->delaysched)
 				sched();
 			break;
@@ -127,6 +130,7 @@
 		}
 		if(intr == 3){
 	case 0x2F:	// SError interrupt
+			f = fpukenter(ureg);
 			if(buserror != nil && (*buserror)(ureg))
 				break;
 			dumpregs(ureg);
@@ -162,6 +166,7 @@
 	case 0x3A:	// vector catch exception (A32 only)
 	case 0x3C:	// BRK instruction (A64 only)
 	default:
+		f = fpukenter(ureg);
 		if(!userureg(ureg)){
 			dumpregs(ureg);
 			panic("unhandled trap");
@@ -170,6 +175,7 @@
 		postnote(up, 1, traps[type], NDebug);
 		break;
 	}
+
 	splhi();
 	if(user){
 		if(up->procctl || up->nnote)
@@ -176,6 +182,8 @@
 			notify(ureg);
 		kexit(ureg);
 	}
+	if(type != 0x07 && type != 0x2C)
+		fpukexit(ureg, f);
 }
 
 void
@@ -189,6 +197,7 @@
 
 	if(!kenter(ureg))
 		panic("syscall from  kernel");
+	fpukenter(ureg);
 	
 	m->syscall++;
 	up->insyscall = 1;
@@ -196,7 +205,6 @@
 	
 	sp = ureg->sp;
 	up->scallnr = scallnr = ureg->r0;
-
 	spllo();
 	
 	up->nerrlab = 0;
@@ -246,9 +254,9 @@
 		procctl();
 		splx(s);
 	}
-	
 	up->insyscall = 0;
 	up->psstate = 0;
+
 	if(scallnr == NOTED){
 		noted(ureg, *((ulong*) up->s.args));
 		/*
@@ -260,21 +268,27 @@
 		 * to it when returning form syscall()
 		 */
 		returnto(noteret);
-	}
 
-	if(scallnr != RFORK && (up->procctl || up->nnote)){
 		splhi();
-		notify(ureg);
+		up->fpstate &= ~FPillegal;
 	}
+	else
+		splhi();
+
+	if(scallnr != RFORK && (up->procctl || up->nnote))
+		notify(ureg);
+
 	if(up->delaysched)
 		sched();
+
 	kexit(ureg);
+	fpukexit(ureg, nil);
 }
 
 int
 notify(Ureg *ureg)
 {
-	uintptr s, sp;
+	uintptr sp;
 	char *msg;
 
 	if(up->procctl)
@@ -281,13 +295,8 @@
 		procctl();
 	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){
@@ -324,7 +333,10 @@
 	ureg->pc = (uintptr) up->notify;
 	ureg->link = 0;
 	qunlock(&up->debug);
-	splx(s);
+
+	splhi();
+	fpuprocsave(up);
+	up->fpstate |= FPillegal;
 	return 1;
 }
 
@@ -333,7 +345,7 @@
 {
 	Ureg *nureg;
 	uintptr oureg, sp;
-	
+
 	qlock(&up->debug);
 	if(arg0 != NRSTR && !up->notified){
 		qunlock(&up->debug);
@@ -343,7 +355,6 @@
 	up->notified = 0;
 	
 	nureg = up->ureg;
-	up->fpstate &= ~FPillegal;
 	
 	oureg = (uintptr) nureg;
 	if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 7) != 0){
@@ -376,7 +387,6 @@
 		}
 		qunlock(&up->debug);
 		sp = oureg - 4 * BY2WD - ERRMAX;
-		splhi();
 		ureg->sp = sp;
 		ureg->r0 = (uintptr) oureg;
 		((uintptr *) sp)[1] = oureg;
@@ -399,26 +409,24 @@
 {
 	extern void checkpages(void);
 	char buf[ERRMAX];
-	int read, insyscall;
+	int user, read;
 	uintptr addr;
 
-	insyscall = up->insyscall;
-	up->insyscall = 1;
-
-	if(!userureg(ureg)){
+	user = userureg(ureg);
+	if(user)
+		up->insyscall = 1;
+	else {
 		extern void _peekinst(void);
 
 		if(ureg->pc == (uintptr)_peekinst){
 			ureg->pc = ureg->link;
-			goto out;
+			return;
 		}
-
 		if(waserror()){
 			if(up->nerrlab == 0){
 				pprint("suicide: sys: %s\n", up->errstr);
 				pexit(up->errstr, 1);
 			}
-			up->insyscall = insyscall;
 			nexterror();
 		}
 	}
@@ -446,7 +454,7 @@
 	case 61:				// first level domain fault
 	case 62:				// second level domain fault
 	default:
-		if(!userureg(ureg)){
+		if(!user){
 			dumpregs(ureg);
 			panic("fault: %s addr=%#p", read ? "read" : "write", addr);
 		}
@@ -455,11 +463,10 @@
 		postnote(up, 1, buf, NDebug);
 	}
 
-	if(!userureg(ureg))
+	if(user)
+		up->insyscall = 0;
+	else
 		poperror();
-
-out:
-	up->insyscall = insyscall;
 }
 
 int
@@ -487,19 +494,7 @@
 void
 procfork(Proc *p)
 {
-	int 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);
-
+	fpuprocfork(p);
 	p->tpidr = up->tpidr;
 }
 
@@ -506,9 +501,7 @@
 void
 procsetup(Proc *p)
 {
-	p->fpstate = FPinit;
-	fpoff();
-
+	fpuprocsetup(p);
 	p->tpidr = 0;
 	syswr(TPIDR_EL0, p->tpidr);
 }
@@ -516,17 +509,9 @@
 void
 procsave(Proc *p)
 {
-	if(p->fpstate == FPactive){
-		if(p->state == Moribund)
-			fpclear();
-		else
-			fpsave(p->fpsave);
-		p->fpstate = FPinactive;
-	}
-
+	fpuprocsave(p);
 	if(p->kp == 0)
 		p->tpidr = sysrd(TPIDR_EL0);
-
 	putasid(p);	// release asid
 }
 
@@ -533,6 +518,7 @@
 void
 procrestore(Proc *p)
 {
+	fpuprocrestore(p);
 	if(p->kp == 0)
 		syswr(TPIDR_EL0, p->tpidr);
 }