/* * 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();}
/*
* 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 int
dumpfile(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;
}
long
dirread0(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;
}
long
dirread(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 int
addsdev(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;
}
int
partboot(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 void
trybootfile(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 void
trydiskboot(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.
*/
void
bootloadproc(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();
}