2.11BSD/sys/pdpstand/sys.c
/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)sys.c 2.4 (2.11BSD) 1996/3/8
*/
#include "../h/param.h"
#include "../h/dir.h"
#include "../machine/seg.h"
#include "../machine/iopage.h"
#include "../machine/psl.h"
#include "../machine/machparam.h"
#include "saio.h"
#undef KISA0
#define KISA0 ((u_short *) 0172340)
#define KDSA0 ((u_short *) 0172360)
extern struct devsw devsw[];
extern int ssr3copy, bootctlr;
extern long atol();
/*
* These buffers are only used in mapping inode blocks to disk sectors,
* no good reason to make them global.
*
* The strange initialization is due to the fact that the 0'th case is
* not legal since it would be used for quadruple indirect files. The
* use of the array 'b' is:
*
* b[0] - not allocated, saving 1kb of space
* b[1] - triple indirect block
* b[2] - double indirect block
* b[3] - single indirect block
*
* In the 1kb filesystem double indirect files can be up to ~65 megabytes
* (the old 512 byte filesystem double indirect files were up to ~8mb).
*/
static char tplind[DEV_BSIZE], dblind[DEV_BSIZE], sngind[DEV_BSIZE];
static char *b[4] = {
NULL, /* j = 0 */
tplind, /* j = 1 */
dblind, /* j = 2 */
sngind /* j = 3 */
};
static daddr_t blknos[4];
#define NFILES 4
struct iob iob[NFILES];
ino_t dlook();
struct direct *readdir();
struct dirstuff {
long loc;
struct iob *io;
};
/*
* wfj - mods to trap to explicitly detail why we stopped
*/
static
openi(n, io)
register ino_t n;
register struct iob *io;
{
register struct dinode *dp;
daddr_t tdp;
io->i_offset = 0;
tdp = itod(n);
io->i_bn = fsbtodb(tdp) + io->i_boff;
io->i_cc = DEV_BSIZE;
io->i_ma = io->i_buf;
devread(io);
dp = (struct dinode *)io->i_buf;
dp = &dp[itoo(n)];
io->i_ino.i_number = n;
io->i_ino.i_mode = dp->di_mode;
io->i_ino.i_size = dp->di_size;
bcopy(dp->di_addr, io->i_ino.i_addr, NADDR * sizeof (daddr_t));
}
static
find(path, file)
register char *path;
struct iob *file;
{
register char *q;
register char c;
ino_t n;
if (path==NULL || *path=='\0') {
printf("null path\n");
return(0);
}
openi((ino_t) ROOTINO, file);
while (*path) {
while (*path == '/')
path++;
q = path;
while (*q != '/' && *q != '\0')
q++;
c = *q;
*q = '\0';
if (q == path) path = "." ; /* "/" means "/." */
if ((n=dlook(path, file))!=0) {
if (c=='\0')
break;
openi(n, file);
*q = c;
path = q;
continue;
} else {
printf("%s not found\n", path);
return(0);
}
/*NOTREACHED*/
}
return(n);
}
static daddr_t
sbmap(io, bn)
register struct iob *io;
daddr_t bn;
{
register i;
register struct inode *ip;
int j, sh;
daddr_t nb, *bap;
ip = &io->i_ino;
if (bn < 0) {
printf("bn < 0\n");
return((daddr_t)0);
}
/*
* blocks 0..NADDR-4 are direct blocks
*/
if (bn < NADDR-3) {
i = bn;
nb = ip->i_addr[i];
return(nb);
}
/*
* addresses NADDR-3, NADDR-2, and NADDR-1
* have single, double, triple indirect blocks.
* the first step is to determine
* how many levels of indirection.
*/
sh = 0;
nb = 1;
bn -= NADDR-3;
for (j=3; j>0; j--) {
sh += NSHIFT;
nb <<= NSHIFT;
if (bn < nb)
break;
bn -= nb;
}
if (j == 0) {
printf("bn ovf %D\n", bn);
return((daddr_t)0);
}
/*
* At this point:
*
* j NADDR-j meaning
* --- ------- -------
* 0 7 illegal - tossed out in the 'if' above
* 1 6 triple indirect block number
* 2 5 double indirect block number
* 3 4 single indirect block number
*/
/*
* fetch the address from the inode
*/
nb = ip->i_addr[NADDR-j];
if (nb == 0)
goto bnvoid;
/*
* fetch through the indirect blocks
*/
for (; j<=3; j++) {
if (blknos[j] != nb) {
io->i_bn = fsbtodb(nb) + io->i_boff;
io->i_ma = b[j];
io->i_cc = DEV_BSIZE;
devread(io);
blknos[j] = nb;
}
bap = (daddr_t *)b[j];
sh -= NSHIFT;
i = (bn>>sh) & NMASK;
nb = bap[i];
if (nb == 0) {
bnvoid:
printf("bn void %D\n", bn);
return((daddr_t)0);
}
}
return(nb);
}
static ino_t
dlook(s, io)
char *s;
register struct iob *io;
{
register struct direct *dp;
register struct inode *ip;
struct dirstuff dirp;
int len;
if (s==NULL || *s=='\0')
return(0);
ip = &io->i_ino;
if ((ip->i_mode&IFMT) != IFDIR || !ip->i_size) {
printf("%s: !directory,mode %o size: %D ip %o\n", s,
ip->i_mode, ip->i_size, ip);
return(0);
}
len = strlen(s);
dirp.loc = 0;
dirp.io = io;
for (dp = readdir(&dirp); dp; dp = readdir(&dirp)) {
if (dp->d_ino == 0)
continue;
if (dp->d_namlen == len && !strcmp(s,dp->d_name))
return(dp->d_ino);
}
return(0);
}
struct direct *
readdir(dirp)
register struct dirstuff *dirp;
{
register struct direct *dp;
register struct iob *io;
daddr_t lbn, d, off;
io = dirp->io;
for (;;) {
if (dirp->loc >= io->i_ino.i_size)
return(NULL);
off = blkoff(dirp->loc);
if (off == 0) {
lbn = lblkno(dirp->loc);
d = sbmap(io, lbn);
if (d == 0)
return(NULL);
io->i_bn = fsbtodb(d) + io->i_boff;
io->i_ma = io->i_buf;
io->i_cc = DEV_BSIZE;
devread(io);
}
dp = (struct direct *)(io->i_buf + off);
dirp->loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
return(dp);
}
}
/*
* Modified to call the tape driver's seek routine. This only works when
* the record size is DEV_BSIZE (1kb).
*/
lseek(fdesc, addr, ptr)
register int fdesc;
off_t addr;
int ptr;
{
off_t new;
register struct iob *io;
if (ptr != 0) {
printf("lseek\n");
return(-1);
}
fdesc -= 3;
if (fdesc < 0 || fdesc >= NFILES ||
((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0)
return(-1);
new = addr / DEV_BSIZE;
if (io->i_flgs & F_TAPE)
{
#ifdef debug
printf("seek: new=%D i_bn=%D\n", new, io->i_bn);
#endif
/*
* Subtract 1 to acccount for the block currently in the buffer - the
* tape is currently positioned at the next record. Need to convert the
* block number from 512 byte to 1024 byte records (undo the fsbtodb).
*/
devseek(io, (int)(new - dbtofsb(io->i_bn) - 1));
}
io->i_offset = addr;
io->i_bn = fsbtodb(new) + io->i_boff;
io->i_cc = 0;
return(0);
}
/* if tape mark encountered, set flag (in driver) */
int tapemark;
getc(fdesc)
int fdesc;
{
register struct iob *io;
register unsigned char *p;
register int c;
int off;
daddr_t tbn;
if (fdesc >= 0 && fdesc <= 2)
return(getchar());
fdesc -= 3;
if (fdesc < 0 || fdesc >= NFILES || ((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0)
return(-1);
if (io->i_cc <= 0) {
io->i_bn = fsbtodb(io->i_offset/(off_t)DEV_BSIZE);
if (io->i_flgs&F_FILE) {
tbn = sbmap(io, dbtofsb(io->i_bn));
io->i_bn = fsbtodb(tbn) + io->i_boff;
}
io->i_ma = io->i_buf;
io->i_cc = DEV_BSIZE;
#ifdef DEBUG
printf("getc: fetch block %D, dev = %d\n", io->i_bn,
io->i_ino.i_dev);
#endif
tapemark = 0;
devread(io);
off = io->i_offset % (off_t)DEV_BSIZE;
if (io->i_flgs&F_FILE) {
if (io->i_offset+(DEV_BSIZE-off) >= io->i_ino.i_size)
io->i_cc = io->i_ino.i_size - io->i_offset + off;
}
if (tapemark)
return(-1);
io->i_cc -= off;
if (io->i_cc <= 0)
return(-1);
io->i_ma = &io->i_buf[off];
}
io->i_cc--;
io->i_offset++;
c = (int)(*(unsigned char *)io->i_ma++);
return(c);
}
getw(fdesc)
int fdesc;
{
register w, i;
register unsigned char *cp;
int val;
for (i = 0, val = 0, cp = (unsigned char *)&val; i < sizeof(val); i++) {
w = getc(fdesc);
if (w < 0) {
if (i == 0)
return(-1);
else
return(val);
}
*cp++ = w;
}
return(val);
}
read(fdesc, buf, count)
register int fdesc;
char *buf;
int count;
{
register i;
register struct iob *file;
if (fdesc >= 0 & fdesc <= 2) {
i = count;
do {
*buf = getchar();
} while (--i && *buf++ != '\n');
return(count - i);
}
fdesc -= 3;
if (fdesc < 0 || fdesc >= NFILES || ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0)
return(-1);
if ((file->i_flgs&F_READ) == 0)
return(-1);
if ((file->i_flgs&F_FILE) == 0) {
file->i_cc = count;
file->i_ma = buf;
i = devread(file);
if (i > 0)
file->i_bn += (i / NBPG);
return(i);
}
else {
if (file->i_offset+count > file->i_ino.i_size)
count = file->i_ino.i_size - file->i_offset;
if ((i = count) <= 0)
return(0);
do {
*buf++ = getc(fdesc+3);
} while (--i);
return(count);
}
}
write(fdesc, buf, count)
register int fdesc;
char *buf;
int count;
{
register i;
register struct iob *file;
if (fdesc >= 0 && fdesc <= 2) {
i = count;
while (i--)
putchar(*buf++);
return(count);
}
fdesc -= 3;
if (fdesc < 0 || fdesc >= NFILES || ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0)
return(-1);
if ((file->i_flgs&F_WRITE) == 0)
return(-1);
file->i_cc = count;
file->i_ma = buf;
i = devwrite(file);
file->i_bn += (count / NBPG);
return(i);
}
open(str, how)
char *str;
int how;
{
register char *cp;
int i, i2, i3;
register struct iob *file;
register struct devsw *dp;
int fdesc;
for (fdesc = 0; fdesc < NFILES; fdesc++)
if (iob[fdesc].i_flgs == 0)
goto gotfile;
_stop("No file slots");
gotfile:
(file = &iob[fdesc])->i_flgs |= F_ALLOC;
for (cp = str; *cp && *cp != '('; cp++)
;
if (*cp != '(') {
printf("Bad device\n");
file->i_flgs = 0;
return(-1);
}
*cp = '\0';
for (dp = devsw; dp->dv_name; dp++) {
if (strcmp(str, dp->dv_name) == 0)
goto gotdev;
}
printf("Unknown device\n");
file->i_flgs = 0;
return(-1);
gotdev:
/*
* The parse is a bit lengthier and complex now. The syntax is now:
*
* xx(ctlr, unit, partition)filename
* or
* xx(unit, partition)filename
*
*
* The controller number must be less than 4. NOTE: many drivers
* limit the number of controllers to 1 or 2. If the controller is not
* specified it defaults to 0.
*
* The unit number must be less than 8.
*
* The partition number is also used to specify the tapefile to be loaded.
* When loading a tapefile the 'filename' must not be specified. The partition
* number must be less than 8. This means that the number of standalone
* programs is limited to 7.
*/
*cp++ = '(';
file->i_ino.i_dev = dp-devsw;
for (i = 0; *cp >= '0' && *cp <= '9'; cp++) {
i *= 10;
i += (*cp - '0');
}
if (*cp++ != ',') {
badoff:
printf("Bad offset or ctlr, unit, part out of bounds\n");
file->i_flgs = 0;
return(-1);
}
for (i2 = 0; *cp >= '0' && *cp <= '9'; cp++)
{
i2 *= 10;
i2 += (*cp - '0');
}
/*
* If we encounter a ')' now it means we have the two arg form 'ra(x,y)'. If
* a ',' is seen then we have the three arg form 'xp(x,y,z)'. If neither a
* ',' or ')' it is an error.
*/
if (*cp == ')')
{
file->i_ctlr = bootctlr;
file->i_unit = i;
file->i_part = i2;
cp++; /* skip ) */
}
else if (*cp == ',')
{
for (i3 = 0, cp++; *cp >= '0' && *cp <= '9'; cp++)
{
i3 *= 10;
i3 += (*cp - '0');
}
file->i_ctlr = i;
file->i_unit = i2;
file->i_part = i3;
if (*cp++ != ')')
goto badoff;
}
else
goto badoff;
if (file->i_ctlr > 3 || file->i_unit > 7 || file->i_part > 7)
goto badoff;
if (devopen(file) < 0)
{
file->i_flgs = 0;
return(-1);
}
if (*cp == '\0') {
file->i_flgs |= how+1;
goto comret;
}
if ((i = find(cp, file)) == 0) {
file->i_flgs = 0;
return(-1);
}
if (how != 0) {
printf("Can't write files\n");
file->i_flgs = 0;
return(-1);
}
openi(i, file);
file->i_flgs |= F_FILE | (how+1);
comret:
file->i_offset = 0;
file->i_cc = 0;
file->i_bn = 0;
return(fdesc+3);
}
close(fdesc)
register int fdesc;
{
register struct iob *file;
fdesc -= 3;
if (fdesc < 0 || fdesc >= NFILES || ((file = &iob[fdesc])->i_flgs&F_ALLOC) == 0)
return(-1);
if ((file->i_flgs&F_FILE) == 0)
devclose(file);
file->i_flgs = 0;
return(0);
}
exit()
{
_stop("Exit called");
}
_stop(s)
char *s;
{
printf("%s\n", s);
_rtt();
}
extern char module[];
trap(r1, r0, nps, pc, ps)
int nps, r1, r0, pc, ps;
{
printf("Trap in %s,", module);
switch (nps&7) {
case 0:
printf("bus error");
break;
case 1:
printf("illegal instruction");
break;
case 2:
printf("bpt/trace");
break;
case 3:
printf("iot");
break;
case 4:
printf("power fail");
break;
case 5:
printf("emt");
break;
case 6:
printf("sys/trap");
break;
default:
printf("weird");
}
printf(" at loc %o\n", pc);
printf("registers: r0=%o, r1=%o, ps=%o, nps=%o\n", r0, r1, ps, nps);
for (;;)
;
}
genopen(maxctlr, io)
int maxctlr;
struct iob *io;
{
register struct devsw *dp = &devsw[io->i_ino.i_dev];
register char *cp;
register int ctlr = io->i_ctlr;
int csr;
char line[64];
if (ctlr >= maxctlr)
return(-1);
if (dp->dv_csr[ctlr])
return(0);
printf("%s%d csr[0%o]: ", dp->dv_name, ctlr, dp->dv_csr[ctlr]);
gets(line);
for (csr = 0, cp = line; *cp >= '0' && *cp <= '7'; cp++)
csr = csr * 8 + (*cp - '0');
if (csr == 0)
return(-1);
dp->dv_csr[ctlr] = (caddr_t *)csr;
/*
* Tapes don't need this. Disk drivers which support labels do not need
* this either but disk drivers which do not support labels _do_ need this.
* Doing it here is safe for everyone because label capable drivers will
* load i_boff from the label _after_ calling this routine.
*/
io->i_boff = 0;
return(0);
}
delay(i)
int i;
{
while (i)
i--;
}
char *
ltoa(l)
u_long l;
{
static char x[12];
register char *cp = x + sizeof (x);
do {
*--cp = (l % 10) + '0';
l /= 10;
} while (l);
return(cp);
}
char *
itoa(i)
register int i;
{
static char x[8];
register char *cp = x + sizeof (x);
do {
*--cp = (i % 10) + '0';
i /= 10;
} while (i);
return(cp);
}
/*
* Convert a 16 bit (virtual) address into a 18 bit (UNIBUS) address.
* The address extension bits (bits 16, 17) are placed into 'bae' while
* the lower order bits (bits 0 - 15) are placed in 'lo16'.
*
* This routine was primarily created to handle split I/D kernel mode
* utilities. Boot itself must run non split I/D and runs in user mode.
* A side effect of this routine is that Boot no longer must be loaded at
* a 64Kb boundary.
*
* Everything (Boot and standalone utilities) must still run in the low
* 248kb of memory which is mapped by the UNIBUS mapping registers.
*/
iomapadr(buf, bae, lo16)
memaddr buf;
u_short *bae, *lo16;
{
u_long uadr;
int curmode = *PS & PSL_CURMOD, segno;
u_int nb, ts;
extern char module[];
if (curmode == 0)
{ /* Kernel mode - we're in a utility */
/*
* For split I/D we need to emulate the kernel's method of looking at
* the segment registers and calculating the physical address. Whew, forgot
* that ;-)
*/
nb = ((int)buf >> 6) & 01777;
segno = nb >> 7;
if (ssr3copy & 4)
ts = KDSA0[segno]; /* kernel I/D is on */
else
ts = KISA0[segno]; /* kernel I/D is off */
ts += (nb & 0177);
uadr = ((u_long)ts << 6) + (buf & 077);
}
else if (curmode == PSL_CURMOD)
{ /* User mode - we're in Boot */
uadr = UISA[0]; /* No I/D for Boot! */
uadr <<= 6; /* clicks to bytes */
uadr += buf;
}
else
_stop("Bad PSL mode");
*bae = (uadr >> 16) & 3; /* 18 bit mode */
*lo16 = uadr & 0xffff;
}