~kris/9p

9hist

5a95dc0d9ca47d56ea38f79aa304a50c353be930 — David du Colombier 34 years ago 491995f
Plan 9 from Bell Labs 1992-08-02
10 files changed, 432 insertions(+), 91 deletions(-)

M port/sysproc.c
M power/fns.h
A power/fptrap.c
M power/trap.c
M ss/dat.h
M ss/fns.h
M ss/fptrap.c
M ss/l.s
M ss/main.c
M ss/trap.c
M port/sysproc.c => port/sysproc.c +6 -1
@@ 308,6 308,12 @@ sysexec(ulong *arg)
		nargs++;
	}
	ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1));
	/*
	 * 8-byte align SP for those (e.g. sparc) that need it.
	 * execregs() will subtract another 4 bytes for argc.
	 */
	if((ssize+4) & 7)
		ssize += 4;
	spage = (ssize+(BY2PG-1)) >> PGSHIFT;
	/*
	 * Build the stack segment, putting it in kernel virtual for the moment


@@ 315,7 321,6 @@ sysexec(ulong *arg)
	if(spage > TSTKSIZ)
		error(Enovmem);

	 
	p->seg[ESEG] = newseg(SG_STACK, TSTKTOP-USTKSIZE, USTKSIZE/BY2PG);

	/*

M power/fns.h => power/fns.h +1 -0
@@ 23,6 23,7 @@ ulong	fcr31(void);
void	firmware(int);
void	flushmmu(void);
#define	flushpage(s)	icflush((void*)(s), BY2PG)
int	fptrap(Ureg*, ulong);
void	gettlb(int, ulong*);
ulong	gettlbvirt(int);
void	gotopc(ulong);

A power/fptrap.c => power/fptrap.c +315 -0
@@ 0,0 1,315 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"ureg.h"
#include	"io.h"
#include	"../port/error.h"

typedef struct	FPinstr	FPinstr;
struct FPinstr
{
	ulong	op;
	ulong	load;
	ulong	store;
	ulong	branch;
	ulong	fmt;
	ulong	fs;
	ulong	fd;
	ulong	ft;
	ulong	cft;
};

enum	/* op */
{
	ABS =	5,
	ADD =	0,
	CVTD = 	65,
	CVTS =	64,
	CVTW =	68,
	DIV =	3,
	MOV =	6,
	MUL =	2,
	NEG =	7,
	SUB =	1,
};

static int	fpunimp(Ureg*, ulong, ulong);
static ulong	branch(Ureg*, ulong);

int
fptrap(Ureg *ur, ulong fcr31)
{
	ulong iw, x, npc;
	int i, ret;

	savefpregs(&u->fpsave);
	if(ur->cause & (1<<31))
		iw = *(ulong*)(ur->pc+4);
	else
		iw = *(ulong*)ur->pc;
	ret = 0;
	x = fcr31>>12;
	fcr31 &= ~(0x3F<<12);
	for(i=0; i<6; i++,x>>=1)
		if(x & 1)
			switch(i){
			case 0:	/* inexact */
pprint("inexact\n");
return 0;
			case 1: /* underflow */
pprint("underflow\n");
return 0;
			case 2:	/* overflow */
pprint("overflow\n");
return 0;
			case 3: /* division by zero */
				return 0;
			case 4: /* invalid operation */
pprint("invalid op\n");
return 0;
			case 5:	/* unimplemented operation */
				ret = fpunimp(ur, fcr31, iw);
			}
	if(ret){
		if(ur->cause & (1<<31)){
			npc = branch(ur, fcr31);
			if(npc)
				ur->pc = npc;
			else
				return 0;
		}else
			ur->pc += 4;
		restfpregs(&u->fpsave, fcr31);
	}
	return ret;
}

static int
fpdas(ulong iw, FPinstr *fp)
{
	memset(fp, ~0, sizeof(*fp));
	if((iw>>25) == 0x23){
		fp->op = iw & ((1<<5)-1);
		fp->fmt = (iw>>21) & ((1<<4)-1);
		fp->ft = (iw>>16) & ((1<<5)-1);
		fp->fs = (iw>>11) & ((1<<5)-1);
		fp->fd = (iw>>6) & ((1<<5)-1);
		fp->cft = (iw>>21) & ((1<<5)-1);
		return 1;
	}
	return 0;
}

static void
unpack(FPsave *f, int fmt, int reg, int *sign, int *exp)
{
	*sign = 1;
	if(f->fpreg[reg] & 0x80000000)
		*sign = -1;
	switch(fmt){
	case 0:
		*exp = ((f->fpreg[reg]>>23)&0xFF) - ((1<<7)-2);
		break;
	case 1:
		if(reg & 1){
			pprint("unaligned double fp register\n");
			reg &= ~1;
		}
pprint("%lux %lux\n", f->fpreg[reg], f->fpreg[reg+1]);
		*exp = ((f->fpreg[reg]>>20)&0x7FF) - ((1<<10)-2);
		break;
	}
}

static void
zeroreg(FPsave *f, int fmt, int reg, int sign)
{
	int size;

	size = 0;
	switch(fmt){
	case 0:
		size = 4;
		break;
	case 1:
		if(reg & 1)
			reg &= ~1;
		size = 8;
		break;
	}
	memset(&f->fpreg[reg], 0, size);
	if(sign < 0)
		f->fpreg[reg] |= 0x80000000;
}

static int
fpunimp(Ureg *ur, ulong fcr31, ulong iw)
{
	FPinstr instr;
	int ss, st, sd;
	int es, et, ed;
	int maxe, maxm;

pprint("fpunimp %lux %lux\n", iw, iw>>25);
	if(!fpdas(iw, &instr))
		return 0;
pprint("%d\n", instr.op);
	if(instr.op == ~0)
		return 0;
	unpack(&u->fpsave, instr.fmt, instr.fs, &ss, &es);
	unpack(&u->fpsave, instr.fmt, instr.ft, &st, &et);
	ed = 0;
	maxe = 0;
	maxm = 0;
	switch(instr.fmt){
	case 0:
		maxe = 1<<7;
		maxm = 24;
		break;
	case 1:
		maxe = 1<<10;
		maxm = 53;
		break;
	}
	switch(instr.op){
	case SUB:
		st = -st;
	case ADD:
		if(es<-(maxe-maxm) && et<-(maxe-maxm))
			ed = -maxe;
		if(es > et)
			sd = es;
		else
			sd = et;
		break;

	case DIV:
		et = -et;
	case MUL:
		sd = 1;
		if(ss != st)
			sd = -1;
		ed = es + et;
		break;
	default:
		pprint("unknown unimplemented fp op\n");
		return 0;
	}
	if(ed <= -(maxe-4)){	/* guess: underflow */
pprint("guess underflow\n");
		zeroreg(&u->fpsave, instr.fmt, instr.fd, sd);
		return 1;
	}
	return 0;
}

static ulong*
reg(Ureg *ur, int regno)
{
	/* regs go from R31 down in ureg, R29 is missing */
	if(regno == 31)
		return &ur->r31;
	if(regno == 30)
		return &ur->r30;
	if(regno == 29)
		return &ur->sp;
	return (&ur->r28) + (28-regno);
}

static ulong
branch(Ureg *ur, ulong fcr31)
{
	ulong iw, npc, rs, rt, rd, offset;

	iw = *(ulong*)ur->pc;
	rs = (iw>>21) & 0x1F;
	if(rs)
		rs = *reg(ur, rs);
	rt = (iw>>16) & 0x1F;
	if(rt)
		rt = *reg(ur, rt);
	offset = iw & ((1<<16)-1);
	if(offset & (1<<15))	/* sign extend */
		offset |= ~((1<<16)-1);
	offset <<= 2;
	/*
	 * Integer unit jumps first
	 */
	switch(iw>>26){
	case 0:			/* SPECIAL: JR or JALR */
		switch(iw&0x3F){
		case 0x09:	/* JALR */
			rd = (iw>>11) & 0x1F;
			if(rd)
				*reg(ur, rd) = ur->pc+8;
			/* fall through */
		case 0x08:	/* JR */
			return rs;
		default:
			return 0;
		}
	case 1:			/* BCOND */
		switch((iw>>16) & 0x1F){
		case 0x10:	/* BLTZAL */
			ur->r31 = ur->pc + 8;
			/* fall through */
		case 0x00:	/* BLTZ */
			if((long)rs < 0)
				return ur->pc+4 + offset;
			return ur->pc + 8;
		case 0x11:	/* BGEZAL */
			ur->r31 = ur->pc + 8;
			/* fall through */
		case 0x01:	/* BGEZ */
			if((long)rs >= 0)
				return ur->pc+4 + offset;
			return ur->pc + 8;
		default:
			return 0;
		}
	case 3:			/* JAL */
		ur->r31 = ur->pc+8;
		/* fall through */
	case 2:			/* JMP */
		npc = iw & ((1<<26)-1);
		npc <<= 2;
		return npc | (ur->pc&0xF0000000);
	case 4:			/* BEQ */
		if(rs == rt)
			return ur->pc+4 + offset;
		return ur->pc + 8;
	case 5:			/* BNE */
		if(rs != rt)
			return ur->pc+4 + offset;
		return ur->pc + 8;
	case 6:			/* BLEZ */
		if((long)rs <= 0)
			return ur->pc+4 + offset;
		return ur->pc + 8;
	case 7:			/* BGTZ */
		if((long)rs > 0)
			return ur->pc+4 + offset;
		return ur->pc + 8;
	}
	/*
	 * Floating point unit jumps
	 */
	if((iw>>26) == 0x11)	/* COP1 */
		switch((iw>>16) & 0x3C1){
		case 0x101:	/* BCT */
		case 0x181:	/* BCT */
			if(fcr31 & (1<<23))
				return ur->pc+4 + offset;
			return ur->pc + 8;
		case 0x100:	/* BCF */
		case 0x180:	/* BCF */
			if(!(fcr31 & (1<<23)))
				return ur->pc+4 + offset;
			return ur->pc + 8;
		}
	pprint("fptrap: can't do jump %lux\n", iw);
	return 0;

}

M power/trap.c => power/trap.c +11 -9
@@ 75,11 75,11 @@ void
trap(Ureg *ur)
{
	int ecode;
	int user;
	ulong x;
	int user, x;
	ulong fcr31;
	char buf[ERRLEN];

	SET(x);
	SET(fcr31);
	ecode = EXCCODE(ur->cause);
	LEDON(ecode);
	user = ur->status&KUP;


@@ 92,14 92,15 @@ trap(Ureg *ur)
		if(u && u->p->state==Running){
			if(u->p->fpstate == FPactive) {
				if(ur->cause & INTR3){	/* FP trap */
					x = clrfpintr();
					fcr31 = clrfpintr();
					if(user && fptrap(ur, fcr31))
						goto Return;
					ecode = FPEXC;
					goto Default;
				}
				savefpregs(&u->fpsave);
				u->p->fpstate = FPinactive;
				ur->status &= ~CU1;
				if(ecode == FPEXC)
					goto Default;
			}
		}
		intr(ur);


@@ 151,7 152,7 @@ trap(Ureg *ur)
		if(user){
			spllo();
			if(ecode == FPEXC)
				sprint(buf, "sys: fp: %s FCR31 %lux", fpexcname(x), x);
				sprint(buf, "sys: fp: %s FCR31 %lux", fpexcname(fcr31), fcr31);
			else
				sprint(buf, "sys: %s", excname[ecode]);



@@ 159,7 160,7 @@ trap(Ureg *ur)
		}else{
			print("%s %s pc=%lux\n", user? "user": "kernel", excname[ecode], ur->pc);
			if(ecode == FPEXC)
				print("fp: %s FCR31 %lux\n", fpexcname(x), x);
				print("fp: %s FCR31 %lux\n", fpexcname(fcr31), fcr31);
			dumpregs(ur);
			if(m->machno == 0)
				spllo();


@@ 168,6 169,7 @@ trap(Ureg *ur)
	}

	splhi();
    Return:
	if(user) {
		notify(ur);
		if(u->p->fpstate == FPinactive) {


@@ 600,7 602,7 @@ setvmevec(int v, void (*f)(int))
}

/* This routine must save the values of registers the user is not permitted to write
 * from devproc and the restore the saved values before returning
 * from devproc and then restore the saved values before returning
 */
void
setregisters(Ureg *xp, char *pureg, char *uva, int n)

M ss/dat.h => ss/dat.h +2 -0
@@ 124,6 124,8 @@ struct Mach
	int	cs;
	int	syscall;
	int	load;
	int	fpunsafe;		/* FP unsaved */
	int	fptrap;			/* FP trap occurred while unsafe */
	int	intr;

	int	stack[1];

M ss/fns.h => ss/fns.h +1 -0
@@ 13,6 13,7 @@ void	faultsparc(Ureg*);
void	flushcpucache(void);
#define	flushpage(x)
int	fpcr(int);
int	fpquiet(void);
void	fpregrestore(char*);
void	fpregsave(char*);
void	fprestore(FPsave*);

M ss/fptrap.c => ss/fptrap.c +66 -37
@@ 8,7 8,7 @@
#include	"../port/error.h"

static	int	unfinished(FPsave*);
static	int	fixq(FPsave*, int);
static	void	fixq(FPsave*, int);
extern	void*	bbmalloc(int);

int


@@ 35,9 35,7 @@ fptrap(void)
		clearftt(fsr & ~0x1F);	/* clear ftt and cexc */
		restfpregs(&u->fpsave);
		enabfp();	/* restfpregs disables it */
		n = fixq(&u->fpsave, n);
		if(n > 0)
			goto again;
		fixq(&u->fpsave, n);
	}
	return ret;
}


@@ 172,52 170,83 @@ unfinished(FPsave *f)
	return 0;
}

static int
static void
fixq(FPsave *f, int n)
{
	ulong instr, fsr;
	ulong *ip;

	while(n > 1){
		memmove(&f->q[0], &f->q[1], (n-1)*sizeof f->q[0]);
		--n;
		memmove(&f->q[0], &f->q[1], n*sizeof f->q[0]);
		memset(&f->q[n], 0, (NFPQ-n)*sizeof f->q[0]);
		instr = f->q[0].i;
		/*
		 * Sparc says you have to emulate.  To hell with that.
		 * Let's compile!
		 */
		ip = bbmalloc(3*sizeof(ulong));
		ip[0] = instr;
		ip[1] = 0x81c3e008;	/* JMPL #8(R15), R0 [RETURN] */
		ip[2] = 0x01000000;	/* SETHI #0, R0 [NOP] */
		(*(void(*)(void))ip)();
		/*
		 * WARNING: This code is wrong (and I don't know how
		 * to fix it without emulating the entire FPU in
		 * software--please let me knw) if the queued
		 * instruction gets an exception: the getfsr() generates
		 * a trap and the staggeringly inept SPARC design
		 * translates that to a reset, as you can't have
		 * nested exceptions.  So here's the fairly solid but
		 * not fail-safe solution: jump out NOW if there's
		 * nothing else pending.  If this last instruction
		 * causes an exception, it will fire when the *user*
		 * executes the next FP instruction.  The system
		 * avoids executing any FP on the way out.  The
		 * user will trap and we'll be back but will have
		 * made progress.  The FQ will point to kernel space
		 * but the trap will happen in user space, and that's
		 * what matters.
		 *
		 * This same botch may cause the system to reset
		 * if a trap is pending when we call savefpregs() in
		 * trap.c.  I don't know; the documentation is unclear.
		 * You can always pump one instruction without immediate trap.
		 * Therefore run one and wait for it to complete or trap.
		 * If it traps, we'll recurse but we won't overwrite
		 * our own data structures because there will be only
		 * one instruction uncompleted in the FPU.
		 * If that instruction generates an unfixable trap,
		 * stop the processing, which means a buglet: if the user
		 * process is attempting to resolve FP errors, it must
		 * find and extract the uncompleted instructions behind
		 * the trap by examining the non-zero members of the FPQ.
		 * Tough.
		 */
		if(n == 1)
			return 0;
		for(;;){
			fsr = getfsr();
			if((fsr & (1<<13)) == 0)	/* qne */
				break;
			if(fsr & 0x1F)			/* cexc */
				return n;
		(*(void(*)(void))ip)();
		if(!fpquiet())
			break;
	}
}

/*
 * Must only be called splhi() when it is safe to spllo().  Because the FP unit
 * traps if you touch it when an exception is pending, and because if you
 * trap with ET==0 you halt, this routine sets some global flags to enable
 * the rest of the system to handle the trap that might occur here without
 * upsetting the kernel.
 */
int
fpquiet(void)
{
	int i, notrap;
	ulong fsr;
	char buf[128];

	i = 0;
	notrap = 1;
	m->fpunsafe = 1;
	u->p->fpstate = FPinactive;
	for(;;){
		m->fptrap = 0;
		spllo();
		fsr = getfsr();
		splhi();
		if(m->fptrap){
			/* trap occurred and u->fpsave contains state */
			spllo();
			sprint(buf, "sys: %s", excname(8));
			postnote(u->p, 1, buf, NDebug);
			notrap = 0;
			break;
		}
		if((fsr&(1<<13)) == 0)
			break;
		if(++i > 1000){
			print("fp not quiescent\n");
			break;
		}
		--n;
	}
	return 0;
	m->fpunsafe = 0;
	u->p->fpstate = FPactive;
	return notrap;
}

M ss/l.s => ss/l.s +1 -0
@@ 598,6 598,7 @@ getfpq1:
	MOVW	(R9), R10
	ANDCC	$(1<<13), R10		/* queue not empty? */
	BE	getfpq2
	MOVW	(R8), R0		/* SS2 bug fix */
	MOVD	FQ, (R8)
	ADD	$1, R7
	ADD	$8, R8

M ss/main.c => ss/main.c +1 -0
@@ 149,6 149,7 @@ userinit(void)
	 */
	p->sched.pc = (((ulong)init0) - 8);	/* 8 because of RETURN in gotolabel */
	p->sched.sp = USERADDR+BY2PG-(1+MAXSYSARG)*BY2WD;
	p->sched.sp &= ~7;		/* SP must be 8-byte aligned */
	p->upage = newpage(1, 0, USERADDR|(p->pid&0xFFFF));

	/*

M ss/trap.c => ss/trap.c +28 -44
@@ 52,11 52,11 @@ excname(ulong tbr)

	switch(tbr){
	case 8:
		if(u == 0){
			fsr = getfsr();
			sprint(excbuf, "fp: %s FSR %lux",
				fptrapname[(fsr>>14)&7], fsr);
		}else{
		if(u == 0)
			panic("fptrap in kernel\n");
		else{
			if(m->fpunsafe==0 && u->p->fpstate!=FPactive)
				panic("fptrap not active\n");
			fsr = u->fpsave.fsr;
			sprint(excbuf, "fp: %s FQpc=0x%lux",
				fptrapname[(fsr>>14)&7],


@@ 95,33 95,10 @@ excname(ulong tbr)
	return excbuf;
}

/*
 * This routine is frightening.  See comment in fptrap.c
 */
void
fpquiet(void)
{
	int i;
	ulong fsr;

	i = 0;
	fsr = getfsr();
	while(fsr & (1<<13)){
		if(fsr & 0x1F){
			print("trap in fpquiet\n");
			break;
		}
		if(++i > 1000){
			print("fp not quiescent\n");
			break;
		}
	}
}

void
trap(Ureg *ur)
{
	int user, x;
	int user, x, fpunsafe;
	char buf[64];
	ulong tbr, iw;



@@ 130,6 107,7 @@ trap(Ureg *ur)
		u->dbgreg = ur;
	}

	fpunsafe = 0;
	user = !(ur->psr&PSRPSUPER);
	tbr = (ur->tbr&0xFFF)>>4;
	/* SS2 bug: flush cache line holding trap entry in table */


@@ 137,11 115,11 @@ trap(Ureg *ur)
		putw2(CACHETAGS+((TRAPS+16*tbr)&(VACSIZE-1)), 0);
	if(tbr > 16){			/* interrupt */
		if(u && u->p->state==Running){
			/* if active, FPop at head of Q is probably an excep */
			if(u->p->fpstate == FPactive){
				fpquiet();
				savefpregs(&u->fpsave);
				fpunsafe = 1;
				m->fpunsafe = 1;
				u->p->fpstate = FPinactive;
				ur->psr &= ~PSREF;
			}
		}
		switch(tbr-16){


@@ 165,10 143,9 @@ trap(Ureg *ur)
		case 1:				/* instr. access */
		case 9:				/* data access */
			if(u && u->p->fpstate==FPactive) {
				fpquiet();
				savefpregs(&u->fpsave);
				fpunsafe = 1;
				m->fpunsafe = 1;
				u->p->fpstate = FPinactive;
				ur->psr &= ~PSREF;
			}
			if(u){
				x = u->p->insyscall;


@@ 200,10 177,13 @@ trap(Ureg *ur)
			}
			break;
		case 8:				/* floating point exception */
			if(fptrap()){
				/* do NOT talk to the FPU: see fptrap.c */
			/* if unsafe, trap happened shutting down FPU; just return */
			if(m->fpunsafe){
				m->fptrap = (fptrap()==0);
				return;
			}
			if(fptrap())
				goto Return;	/* handled the problem */
			break;
		default:
			break;


@@ 213,14 193,16 @@ trap(Ureg *ur)
			spllo();
			sprint(buf, "sys: %s", excname(tbr));
			postnote(u->p, 1, buf, NDebug);
		}else{
			print("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc);
			dumpregs(ur);
			for(;;);
		}
		}else
			panic("kernel trap: %s pc=0x%lux\n", excname(tbr), ur->pc);
	}
    Return:
	if(user) {
	/*
	 * Handled the interrupt; now it's safe to look at the FPQ
	 */
	if(fpunsafe)
		fpquiet();
	if(user){
		notify(ur);
		if(u->p->fpstate == FPinactive) {
			restfpregs(&u->fpsave);


@@ 443,7 425,9 @@ notify(Ureg *ur)
		sent = 1;
		u->svpsr = ur->psr;
		sp = ur->usp;
		sp -= sizeof(Ureg);
		sp -= sizeof(Ureg)+ERRLEN+3*BY2WD;
		sp &= ~7;	/* SP must be 8-byte aligned */
		sp += ERRLEN+3*BY2WD;
		if(!okaddr((ulong)u->notify, 1, 0)
		|| !okaddr(sp-ERRLEN-3*BY2WD, sizeof(Ureg)+ERRLEN-3*BY2WD, 0)){
			pprint("suicide: bad address in notify\n");