418 lines · 8.9 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 9load - load next kernel from disk and start it */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"io.h"#include	"ureg.h"#include	"pool.h"#include	"../port/error.h"#include	"../port/netif.h"#include	"dosfs.h"#include	"../port/sd.h" /* from <libc.h> */#define	DIRMAX	(sizeof(Dir)+STATMAX)	/* max length of Dir structure */ #define	STATMAX	65535U	/* max length of machine-independent stat structure */ enum {	Bufsize = 8192,}; int	dosdirread(File *f, char ***nmarray);int	isconf(char *name); static int progress = 1;static Bootfs fs; /* * from 9load's bootp.c: */ static intdumpfile(char *file){	int n;	char *buf; 	buf = smalloc(Maxfile + 1);	n = readfile(file, buf, Maxfile);	if (n < 0)		return -1;	buf[n] = 0;	print("%s (%d bytes):\n", file, n);	print("%s\n", buf);	free(buf);	return 0;} longdirread0(Chan *c, uchar *p, long n){	long nn, nnn;	vlong off; 	/*	 * The offset is passed through on directories, normally.	 * Sysseek complains, but pread is used by servers like exportfs,	 * that shouldn't need to worry about this issue.	 *	 * Notice that c->devoffset is the offset that c's dev is seeing.	 * The number of bytes read on this fd (c->offset) may be different	 * due to rewritings in rockfix.	 */	/* use and maintain channel's offset */	off = c->offset;	if(off < 0)		error(Enegoff); 	if(off == 0){	/* rewind to the beginning of the directory */		c->offset = 0;		c->devoffset = 0;		mountrewind(c);		unionrewind(c);	} 	if(c->qid.type & QTDIR){		if(mountrockread(c, p, n, &nn)){			/* do nothing: mountrockread filled buffer */		}else if(c->umh)			nn = unionread(c, p, n);		else{			if(off != c->offset)				error(Edirseek);			nn = devtab[c->type]->read(c, p, n, c->devoffset);		}		nnn = mountfix(c, p, nn, n);	}else		nnn = nn = devtab[c->type]->read(c, p, n, off); 	lock(c);	c->devoffset += nn;	c->offset += nnn;	unlock(c); 	/* nnn == 54, sizeof(Dir) == 60 */	return nnn;} longdirread(Chan *c, Dir **d){	uchar *buf;	long ts; 	buf = malloc(DIRMAX);	if(buf == nil)		return -1;	ts = dirread0(c, buf, DIRMAX);	if(ts >= 0)		/* convert machine-independent representation to Dirs */		ts = dirpackage(buf, ts, d);	free(buf);	return ts;} static intaddsdev(Dir *dirp){	int n, f, lines, flds;	vlong start, end;	char *buf, *part;	char *line[64], *fld[5];	char ctl[64], disk[64]; 	buf = smalloc(Maxfile + 1);	snprint(ctl, sizeof ctl, "#S/%s/ctl", dirp->name);	n = readfile(ctl, buf, Maxfile);	if (n < 0) {		free(buf);		return -1;	}	buf[n] = 0; 	lines = getfields(buf, line, nelem(line), 0, "\r\n");	part = nil;	for (f = 0; f < lines; f++) {		flds = tokenize(line[f], fld, nelem(fld));		if (flds < 4 || strcmp(fld[0], "part") != 0)			continue;		kstrdup(&part, fld[1]);		start = strtoull(fld[2], nil, 0);		end   = strtoull(fld[3], nil, 0);		if (end > (vlong)100*(vlong)MB*MB) {			print("addsdev: implausible partition #S/%s/%s %lld %lld\n",				dirp->name, part, start, end);			continue;		}		/*		 * We are likely to only see a "data" partition on each disk.		 *		 * Read the on-disk partition tables & set in-core partitions		 * (disk, part, start, end).		 */		print("found partition #S/%s/%s %,lld %,lld\n",			dirp->name, part, start, end);		snprint(disk, sizeof disk, "#S/%s", dirp->name);		readparts(disk);	}	free(buf);	return 0;} static File file; /* * look for kernels on a 9fat; if there's just one, return it. * could treat x and x.gz as one kernel. */static char *findonekernel(Bootfs *fs){	int n, kerns;	char *bootfile, *name;	char **array; 	if(fswalk(fs, "", &file) <= 0) {		print("can't walk to ''\n");		return nil;	}	dosdirread(&file, &array);	bootfile = nil;	kerns = 0;	for (n = 0; (name = array[n]) != nil; n++)		if(strncmp(name, "9pc", 3) == 0 ||		   strncmp(name, "9k8", 3) == 0 ||		   strncmp(name, "9k10", 4) == 0){			bootfile = name;			kerns++;		}	if (kerns > 1) {		print("found these kernels:");		for (n = 0; (name = array[n]) != nil; n++)			print(" %s", name);		print("\n");	}	return kerns == 1? bootfile: nil;} intpartboot(char *path){	long n;	char *buf;	Boot boot;	Boot *b;	Chan *ch; 	b = &boot;	memset(b, 0, sizeof *b);	b->state = INITKERNEL;	ch = namecopen(path, OREAD);	if (ch == nil) {		print("can't open partition %s\n", path);		return -1;	}	print("loading %s\n", path);	buf = smalloc(Bufsize);	while((n = devtab[ch->type]->read(ch, buf, Bufsize, ch->offset)) > 0)		if(bootpass(b, buf, n) != MORE)			break;	bootpass(b, nil, 0);		/* attempts to boot */ 	free(buf);	cclose(ch);	return -1;} /* fsroot must be nil or a fat root directory already dosinit'ed */static voidtrybootfile(char *bootfile, Bootfs *fsroot){	int nf;	char fat[64];	char *disk, *part, *file, *bootcopy;	char *fields[4];	Boot boot;	static int didaddconf; 	bootcopy = file = nil;	kstrdup(&bootcopy, bootfile);	nf = getfields(bootcopy, fields, nelem(fields), 0, "!");	switch(nf){	case 3:		file = fields[2];		/* fall through */	case 2:		disk = fields[0];		part = fields[1];		break;	default:		print("bad bootfile syntax: %s\n", bootfile);		return;	} 	if(didaddconf == 0) {		didaddconf = 1;		sdaddallconfs(sdaddconf);	} 	snprint(fat, sizeof fat, "#S/%s/%s", disk, part);	if (file == nil) { /* if no file, try to load from partition directly */		partboot(fat);		return;	} 	if (fsroot == nil) {		fsroot = &fs;		memset(fsroot, 0, sizeof *fsroot);		if (dosinit(fsroot, fat) < 0) {			print("dosinit %s failed\n", fat);			return;		}	} 	/* load kernel and jump to it */	memset(&boot, 0, sizeof boot);	boot.state = INITKERNEL;	fsboot(fsroot, file, &boot); 	/* failed to boot */} /* * for a given disk's 9fat, find & load plan9.ini, parse it, * extract kernel filename, load that kernel and jump to it. */static voidtrydiskboot(char *disk){	int n;	char fat[80];	char *ini, *bootfile; 	/* mount the disk's 9fat */	memset(&fs, 0, sizeof fs);	snprint(fat, sizeof fat, "#S/%s/9fat", disk);	if (dosinit(&fs, fat) < 0) {		print("dosinit %s failed\n", fat);		return;	} 	/* open plan9.ini, read it */	ini = smalloc(Maxfile+1);	if(fswalk(&fs, "plan9.ini", &file) <= 0) {		print("no plan9.ini in %s\n", fat);		n = 0;	} else {		n = fsread(&file, ini, Maxfile);		if (n < 0)			panic("error reading %s", ini);	}	ini[n] = 0; 	/*	 * take note of plan9.ini contents.  consumes ini to make config vars,	 * thus we can't free ini.	 */	dotini(ini);	i8250console();			/* (re)configure serial port */ 	bootfile = nil;			/* for kstrdup in askbootfile */	if(isconf("bootfile")) {		kstrdup(&bootfile, getconf("bootfile"));		if(strcmp(bootfile, "manual") == 0)			askbootfile(fat, sizeof fat, &bootfile, 0, ""); 		/* pass arguments to kernels that can use them */		strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, bootfile);	} else if ((bootfile = findonekernel(&fs)) != nil) {  /* look in fat */		snprint(fat, sizeof fat, "%s!9fat!%s", disk, bootfile);		bootfile = fat;		print("no bootfile named in plan9.ini; found %s\n", bootfile);	} else {		/* if #S/disk/kernel partition exists, load from it. */		snprint(fat, sizeof fat, "#S/%s/kernel", disk);		partboot(fat);		/* last resort: ask the user */		askbootfile(fat, sizeof fat, &bootfile, Promptsecs,			"sdC0!9fat!9pccpu");	}	trybootfile(bootfile, &fs); 	/* failed; try again */} /* * find all the disks in #S, read their partition tables and set those * partitions in core, mainly so that we can access 9fat file systems. * for each disk's 9fat, read plan9.ini and boot the named kernel. */voidbootloadproc(void *){	int n, dirs, sdev;	char kern[64];	char *sdevs[128];	Chan *sdch;	Dir *dirp, *dp; 	memset(sdevs, 0, sizeof sdevs);	sdch = nil;	while(waserror()) {		print("error caught at top level in bootload\n");		if(sdch) {			cclose(sdch);			sdch = nil;		}	}	bind("#S", "/dev", MAFTER);		/* try to force an attach */	sdch = namecopen("#S", OREAD);	if (sdch == nil)		panic("no disks (no #S)");	sdev = 0;	while ((dirs = dirread(sdch, &dirp)) > 0) {		for (dp = dirp; dirs-- > 0; dp++)			if (strcmp(dp->name, "sdctl") != 0) {				addsdev(dp);				if (sdev >= nelem(sdevs))					print("too many sdevs; ignoring %s\n",						dp->name);				else					kstrdup(&sdevs[sdev++], dp->name);			}		free(dirp);	}	cclose(sdch);	sdch = nil;	if (sdev == 0)		panic("no disks (in #S)"); 	print("disks:");	for (n = 0; n < sdev; n++)		print(" %s", sdevs[n]);	print("\n"); 	for (n = 0; n < sdev; n++) {		print("trying %s...", sdevs[n]);		trydiskboot(sdevs[n]);	}	USED(sdch);	for (;;) {		askbootfile(kern, sizeof kern, nil, 0, "");		trybootfile(kern, nil);	}	// poperror();}