125210b06SDavid du Colombier /*
225210b06SDavid du Colombier * 9load - load next kernel from disk and start it
325210b06SDavid du Colombier */
425210b06SDavid du Colombier #include "u.h"
525210b06SDavid du Colombier #include "../port/lib.h"
625210b06SDavid du Colombier #include "mem.h"
725210b06SDavid du Colombier #include "dat.h"
825210b06SDavid du Colombier #include "fns.h"
925210b06SDavid du Colombier #include "io.h"
1025210b06SDavid du Colombier #include "ureg.h"
1125210b06SDavid du Colombier #include "pool.h"
1225210b06SDavid du Colombier #include "../port/error.h"
1325210b06SDavid du Colombier #include "../port/netif.h"
1425210b06SDavid du Colombier #include "dosfs.h"
1525210b06SDavid du Colombier #include "../port/sd.h"
1625210b06SDavid du Colombier
1725210b06SDavid du Colombier /* from <libc.h> */
1825210b06SDavid du Colombier #define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */
1925210b06SDavid du Colombier #define STATMAX 65535U /* max length of machine-independent stat structure */
2025210b06SDavid du Colombier
2125210b06SDavid du Colombier enum {
2225210b06SDavid du Colombier Bufsize = 8192,
2325210b06SDavid du Colombier };
2425210b06SDavid du Colombier
2525210b06SDavid du Colombier int dosdirread(File *f, char ***nmarray);
2625210b06SDavid du Colombier int isconf(char *name);
2725210b06SDavid du Colombier
2825210b06SDavid du Colombier static int progress = 1;
2925210b06SDavid du Colombier static Bootfs fs;
3025210b06SDavid du Colombier
3125210b06SDavid du Colombier /*
3225210b06SDavid du Colombier * from 9load's bootp.c:
3325210b06SDavid du Colombier */
3425210b06SDavid du Colombier
3525210b06SDavid du Colombier static int
dumpfile(char * file)3625210b06SDavid du Colombier dumpfile(char *file)
3725210b06SDavid du Colombier {
3825210b06SDavid du Colombier int n;
3925210b06SDavid du Colombier char *buf;
4025210b06SDavid du Colombier
4125210b06SDavid du Colombier buf = smalloc(Maxfile + 1);
4225210b06SDavid du Colombier n = readfile(file, buf, Maxfile);
4325210b06SDavid du Colombier if (n < 0)
4425210b06SDavid du Colombier return -1;
4525210b06SDavid du Colombier buf[n] = 0;
4625210b06SDavid du Colombier print("%s (%d bytes):\n", file, n);
4725210b06SDavid du Colombier print("%s\n", buf);
4825210b06SDavid du Colombier free(buf);
4925210b06SDavid du Colombier return 0;
5025210b06SDavid du Colombier }
5125210b06SDavid du Colombier
5225210b06SDavid du Colombier long
dirread0(Chan * c,uchar * p,long n)5325210b06SDavid du Colombier dirread0(Chan *c, uchar *p, long n)
5425210b06SDavid du Colombier {
5525210b06SDavid du Colombier long nn, nnn;
5625210b06SDavid du Colombier vlong off;
5725210b06SDavid du Colombier
5825210b06SDavid du Colombier /*
5925210b06SDavid du Colombier * The offset is passed through on directories, normally.
6025210b06SDavid du Colombier * Sysseek complains, but pread is used by servers like exportfs,
6125210b06SDavid du Colombier * that shouldn't need to worry about this issue.
6225210b06SDavid du Colombier *
6325210b06SDavid du Colombier * Notice that c->devoffset is the offset that c's dev is seeing.
6425210b06SDavid du Colombier * The number of bytes read on this fd (c->offset) may be different
6525210b06SDavid du Colombier * due to rewritings in rockfix.
6625210b06SDavid du Colombier */
6725210b06SDavid du Colombier /* use and maintain channel's offset */
6825210b06SDavid du Colombier off = c->offset;
6925210b06SDavid du Colombier if(off < 0)
7025210b06SDavid du Colombier error(Enegoff);
7125210b06SDavid du Colombier
7225210b06SDavid du Colombier if(off == 0){ /* rewind to the beginning of the directory */
7325210b06SDavid du Colombier c->offset = 0;
7425210b06SDavid du Colombier c->devoffset = 0;
7525210b06SDavid du Colombier mountrewind(c);
7625210b06SDavid du Colombier unionrewind(c);
7725210b06SDavid du Colombier }
7825210b06SDavid du Colombier
7925210b06SDavid du Colombier if(c->qid.type & QTDIR){
8025210b06SDavid du Colombier if(mountrockread(c, p, n, &nn)){
8125210b06SDavid du Colombier /* do nothing: mountrockread filled buffer */
8225210b06SDavid du Colombier }else if(c->umh)
8325210b06SDavid du Colombier nn = unionread(c, p, n);
8425210b06SDavid du Colombier else{
8525210b06SDavid du Colombier if(off != c->offset)
8625210b06SDavid du Colombier error(Edirseek);
8725210b06SDavid du Colombier nn = devtab[c->type]->read(c, p, n, c->devoffset);
8825210b06SDavid du Colombier }
8925210b06SDavid du Colombier nnn = mountfix(c, p, nn, n);
9025210b06SDavid du Colombier }else
9125210b06SDavid du Colombier nnn = nn = devtab[c->type]->read(c, p, n, off);
9225210b06SDavid du Colombier
9325210b06SDavid du Colombier lock(c);
9425210b06SDavid du Colombier c->devoffset += nn;
9525210b06SDavid du Colombier c->offset += nnn;
9625210b06SDavid du Colombier unlock(c);
9725210b06SDavid du Colombier
9825210b06SDavid du Colombier /* nnn == 54, sizeof(Dir) == 60 */
9925210b06SDavid du Colombier return nnn;
10025210b06SDavid du Colombier }
10125210b06SDavid du Colombier
10225210b06SDavid du Colombier long
dirread(Chan * c,Dir ** d)10325210b06SDavid du Colombier dirread(Chan *c, Dir **d)
10425210b06SDavid du Colombier {
10525210b06SDavid du Colombier uchar *buf;
10625210b06SDavid du Colombier long ts;
10725210b06SDavid du Colombier
10825210b06SDavid du Colombier buf = malloc(DIRMAX);
10925210b06SDavid du Colombier if(buf == nil)
11025210b06SDavid du Colombier return -1;
11125210b06SDavid du Colombier ts = dirread0(c, buf, DIRMAX);
11225210b06SDavid du Colombier if(ts >= 0)
11325210b06SDavid du Colombier /* convert machine-independent representation to Dirs */
11425210b06SDavid du Colombier ts = dirpackage(buf, ts, d);
11525210b06SDavid du Colombier free(buf);
11625210b06SDavid du Colombier return ts;
11725210b06SDavid du Colombier }
11825210b06SDavid du Colombier
11925210b06SDavid du Colombier static int
addsdev(Dir * dirp)12025210b06SDavid du Colombier addsdev(Dir *dirp)
12125210b06SDavid du Colombier {
12225210b06SDavid du Colombier int n, f, lines, flds;
12325210b06SDavid du Colombier vlong start, end;
12425210b06SDavid du Colombier char *buf, *part;
12525210b06SDavid du Colombier char *line[64], *fld[5];
12625210b06SDavid du Colombier char ctl[64], disk[64];
12725210b06SDavid du Colombier
12825210b06SDavid du Colombier buf = smalloc(Maxfile + 1);
12925210b06SDavid du Colombier snprint(ctl, sizeof ctl, "#S/%s/ctl", dirp->name);
13025210b06SDavid du Colombier n = readfile(ctl, buf, Maxfile);
131e4575fb1SDavid du Colombier if (n < 0) {
132e4575fb1SDavid du Colombier free(buf);
13325210b06SDavid du Colombier return -1;
134e4575fb1SDavid du Colombier }
13525210b06SDavid du Colombier buf[n] = 0;
13625210b06SDavid du Colombier
13725210b06SDavid du Colombier lines = getfields(buf, line, nelem(line), 0, "\r\n");
13825210b06SDavid du Colombier part = nil;
13925210b06SDavid du Colombier for (f = 0; f < lines; f++) {
14025210b06SDavid du Colombier flds = tokenize(line[f], fld, nelem(fld));
14125210b06SDavid du Colombier if (flds < 4 || strcmp(fld[0], "part") != 0)
14225210b06SDavid du Colombier continue;
14325210b06SDavid du Colombier kstrdup(&part, fld[1]);
14425210b06SDavid du Colombier start = strtoull(fld[2], nil, 0);
14525210b06SDavid du Colombier end = strtoull(fld[3], nil, 0);
14625210b06SDavid du Colombier if (end > (vlong)100*(vlong)MB*MB) {
14725210b06SDavid du Colombier print("addsdev: implausible partition #S/%s/%s %lld %lld\n",
14825210b06SDavid du Colombier dirp->name, part, start, end);
14925210b06SDavid du Colombier continue;
15025210b06SDavid du Colombier }
15125210b06SDavid du Colombier /*
15225210b06SDavid du Colombier * We are likely to only see a "data" partition on each disk.
15325210b06SDavid du Colombier *
15425210b06SDavid du Colombier * Read the on-disk partition tables & set in-core partitions
15525210b06SDavid du Colombier * (disk, part, start, end).
15625210b06SDavid du Colombier */
15725210b06SDavid du Colombier print("found partition #S/%s/%s %,lld %,lld\n",
15825210b06SDavid du Colombier dirp->name, part, start, end);
15925210b06SDavid du Colombier snprint(disk, sizeof disk, "#S/%s", dirp->name);
16025210b06SDavid du Colombier readparts(disk);
16125210b06SDavid du Colombier }
16225210b06SDavid du Colombier free(buf);
16325210b06SDavid du Colombier return 0;
16425210b06SDavid du Colombier }
16525210b06SDavid du Colombier
16625210b06SDavid du Colombier static File file;
16725210b06SDavid du Colombier
16825210b06SDavid du Colombier /*
16925210b06SDavid du Colombier * look for kernels on a 9fat; if there's just one, return it.
17025210b06SDavid du Colombier * could treat x and x.gz as one kernel.
17125210b06SDavid du Colombier */
17225210b06SDavid du Colombier static char *
findonekernel(Bootfs * fs)17325210b06SDavid du Colombier findonekernel(Bootfs *fs)
17425210b06SDavid du Colombier {
17525210b06SDavid du Colombier int n, kerns;
17625210b06SDavid du Colombier char *bootfile, *name;
17725210b06SDavid du Colombier char **array;
17825210b06SDavid du Colombier
17925210b06SDavid du Colombier if(fswalk(fs, "", &file) <= 0) {
18025210b06SDavid du Colombier print("can't walk to ''\n");
18125210b06SDavid du Colombier return nil;
18225210b06SDavid du Colombier }
18325210b06SDavid du Colombier dosdirread(&file, &array);
18425210b06SDavid du Colombier bootfile = nil;
18525210b06SDavid du Colombier kerns = 0;
18625210b06SDavid du Colombier for (n = 0; (name = array[n]) != nil; n++)
18725210b06SDavid du Colombier if(strncmp(name, "9pc", 3) == 0 ||
188*4fa3bc58SDavid du Colombier strncmp(name, "9k8", 3) == 0 ||
189*4fa3bc58SDavid du Colombier strncmp(name, "9k10", 4) == 0){
19025210b06SDavid du Colombier bootfile = name;
19125210b06SDavid du Colombier kerns++;
19225210b06SDavid du Colombier }
19325210b06SDavid du Colombier if (kerns > 1) {
19425210b06SDavid du Colombier print("found these kernels:");
19525210b06SDavid du Colombier for (n = 0; (name = array[n]) != nil; n++)
19625210b06SDavid du Colombier print(" %s", name);
19725210b06SDavid du Colombier print("\n");
19825210b06SDavid du Colombier }
19925210b06SDavid du Colombier return kerns == 1? bootfile: nil;
20025210b06SDavid du Colombier }
20125210b06SDavid du Colombier
20225210b06SDavid du Colombier int
partboot(char * path)20325210b06SDavid du Colombier partboot(char *path)
20425210b06SDavid du Colombier {
20525210b06SDavid du Colombier long n;
20625210b06SDavid du Colombier char *buf;
20725210b06SDavid du Colombier Boot boot;
20825210b06SDavid du Colombier Boot *b;
20925210b06SDavid du Colombier Chan *ch;
21025210b06SDavid du Colombier
21125210b06SDavid du Colombier b = &boot;
21225210b06SDavid du Colombier memset(b, 0, sizeof *b);
21325210b06SDavid du Colombier b->state = INITKERNEL;
21425210b06SDavid du Colombier ch = namecopen(path, OREAD);
21525210b06SDavid du Colombier if (ch == nil) {
21625210b06SDavid du Colombier print("can't open partition %s\n", path);
21725210b06SDavid du Colombier return -1;
21825210b06SDavid du Colombier }
21925210b06SDavid du Colombier print("loading %s\n", path);
22025210b06SDavid du Colombier buf = smalloc(Bufsize);
22125210b06SDavid du Colombier while((n = devtab[ch->type]->read(ch, buf, Bufsize, ch->offset)) > 0)
22225210b06SDavid du Colombier if(bootpass(b, buf, n) != MORE)
22325210b06SDavid du Colombier break;
22425210b06SDavid du Colombier bootpass(b, nil, 0); /* attempts to boot */
22525210b06SDavid du Colombier
22625210b06SDavid du Colombier free(buf);
22725210b06SDavid du Colombier cclose(ch);
22825210b06SDavid du Colombier return -1;
22925210b06SDavid du Colombier }
23025210b06SDavid du Colombier
23125210b06SDavid du Colombier /* fsroot must be nil or a fat root directory already dosinit'ed */
23225210b06SDavid du Colombier static void
trybootfile(char * bootfile,Bootfs * fsroot)23325210b06SDavid du Colombier trybootfile(char *bootfile, Bootfs *fsroot)
23425210b06SDavid du Colombier {
23525210b06SDavid du Colombier int nf;
23625210b06SDavid du Colombier char fat[64];
23725210b06SDavid du Colombier char *disk, *part, *file, *bootcopy;
23825210b06SDavid du Colombier char *fields[4];
23925210b06SDavid du Colombier Boot boot;
24025210b06SDavid du Colombier static int didaddconf;
24125210b06SDavid du Colombier
24225210b06SDavid du Colombier bootcopy = file = nil;
24325210b06SDavid du Colombier kstrdup(&bootcopy, bootfile);
24425210b06SDavid du Colombier nf = getfields(bootcopy, fields, nelem(fields), 0, "!");
24525210b06SDavid du Colombier switch(nf){
24625210b06SDavid du Colombier case 3:
24725210b06SDavid du Colombier file = fields[2];
24825210b06SDavid du Colombier /* fall through */
24925210b06SDavid du Colombier case 2:
25025210b06SDavid du Colombier disk = fields[0];
25125210b06SDavid du Colombier part = fields[1];
25225210b06SDavid du Colombier break;
25325210b06SDavid du Colombier default:
25425210b06SDavid du Colombier print("bad bootfile syntax: %s\n", bootfile);
25525210b06SDavid du Colombier return;
25625210b06SDavid du Colombier }
25725210b06SDavid du Colombier
25825210b06SDavid du Colombier if(didaddconf == 0) {
25925210b06SDavid du Colombier didaddconf = 1;
26025210b06SDavid du Colombier sdaddallconfs(sdaddconf);
26125210b06SDavid du Colombier }
26225210b06SDavid du Colombier
26325210b06SDavid du Colombier snprint(fat, sizeof fat, "#S/%s/%s", disk, part);
26425210b06SDavid du Colombier if (file == nil) { /* if no file, try to load from partition directly */
26525210b06SDavid du Colombier partboot(fat);
26625210b06SDavid du Colombier return;
26725210b06SDavid du Colombier }
26825210b06SDavid du Colombier
26925210b06SDavid du Colombier if (fsroot == nil) {
27025210b06SDavid du Colombier fsroot = &fs;
27125210b06SDavid du Colombier memset(fsroot, 0, sizeof *fsroot);
27225210b06SDavid du Colombier if (dosinit(fsroot, fat) < 0) {
27325210b06SDavid du Colombier print("dosinit %s failed\n", fat);
27425210b06SDavid du Colombier return;
27525210b06SDavid du Colombier }
27625210b06SDavid du Colombier }
27725210b06SDavid du Colombier
27825210b06SDavid du Colombier /* load kernel and jump to it */
27925210b06SDavid du Colombier memset(&boot, 0, sizeof boot);
28025210b06SDavid du Colombier boot.state = INITKERNEL;
28125210b06SDavid du Colombier fsboot(fsroot, file, &boot);
28225210b06SDavid du Colombier
28325210b06SDavid du Colombier /* failed to boot */
28425210b06SDavid du Colombier }
28525210b06SDavid du Colombier
28625210b06SDavid du Colombier /*
28725210b06SDavid du Colombier * for a given disk's 9fat, find & load plan9.ini, parse it,
28825210b06SDavid du Colombier * extract kernel filename, load that kernel and jump to it.
28925210b06SDavid du Colombier */
29025210b06SDavid du Colombier static void
trydiskboot(char * disk)29125210b06SDavid du Colombier trydiskboot(char *disk)
29225210b06SDavid du Colombier {
29325210b06SDavid du Colombier int n;
29425210b06SDavid du Colombier char fat[80];
29525210b06SDavid du Colombier char *ini, *bootfile;
29625210b06SDavid du Colombier
29725210b06SDavid du Colombier /* mount the disk's 9fat */
29825210b06SDavid du Colombier memset(&fs, 0, sizeof fs);
29925210b06SDavid du Colombier snprint(fat, sizeof fat, "#S/%s/9fat", disk);
30025210b06SDavid du Colombier if (dosinit(&fs, fat) < 0) {
30125210b06SDavid du Colombier print("dosinit %s failed\n", fat);
30225210b06SDavid du Colombier return;
30325210b06SDavid du Colombier }
30425210b06SDavid du Colombier
30525210b06SDavid du Colombier /* open plan9.ini, read it */
30625210b06SDavid du Colombier ini = smalloc(Maxfile+1);
30725210b06SDavid du Colombier if(fswalk(&fs, "plan9.ini", &file) <= 0) {
30825210b06SDavid du Colombier print("no plan9.ini in %s\n", fat);
30925210b06SDavid du Colombier n = 0;
31025210b06SDavid du Colombier } else {
31125210b06SDavid du Colombier n = fsread(&file, ini, Maxfile);
31225210b06SDavid du Colombier if (n < 0)
31325210b06SDavid du Colombier panic("error reading %s", ini);
31425210b06SDavid du Colombier }
31525210b06SDavid du Colombier ini[n] = 0;
31625210b06SDavid du Colombier
31725210b06SDavid du Colombier /*
31825210b06SDavid du Colombier * take note of plan9.ini contents. consumes ini to make config vars,
31925210b06SDavid du Colombier * thus we can't free ini.
32025210b06SDavid du Colombier */
32125210b06SDavid du Colombier dotini(ini);
32225210b06SDavid du Colombier i8250console(); /* (re)configure serial port */
32325210b06SDavid du Colombier
32425210b06SDavid du Colombier bootfile = nil; /* for kstrdup in askbootfile */
32525210b06SDavid du Colombier if(isconf("bootfile")) {
32625210b06SDavid du Colombier kstrdup(&bootfile, getconf("bootfile"));
32725210b06SDavid du Colombier if(strcmp(bootfile, "manual") == 0)
328e4575fb1SDavid du Colombier askbootfile(fat, sizeof fat, &bootfile, 0, "");
32925210b06SDavid du Colombier
33025210b06SDavid du Colombier /* pass arguments to kernels that can use them */
33125210b06SDavid du Colombier strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, bootfile);
33225210b06SDavid du Colombier } else if ((bootfile = findonekernel(&fs)) != nil) { /* look in fat */
33325210b06SDavid du Colombier snprint(fat, sizeof fat, "%s!9fat!%s", disk, bootfile);
33425210b06SDavid du Colombier bootfile = fat;
33525210b06SDavid du Colombier print("no bootfile named in plan9.ini; found %s\n", bootfile);
33625210b06SDavid du Colombier } else {
33725210b06SDavid du Colombier /* if #S/disk/kernel partition exists, load from it. */
33825210b06SDavid du Colombier snprint(fat, sizeof fat, "#S/%s/kernel", disk);
33925210b06SDavid du Colombier partboot(fat);
34025210b06SDavid du Colombier /* last resort: ask the user */
341e4575fb1SDavid du Colombier askbootfile(fat, sizeof fat, &bootfile, Promptsecs,
342e4575fb1SDavid du Colombier "sdC0!9fat!9pccpu");
34325210b06SDavid du Colombier }
34425210b06SDavid du Colombier trybootfile(bootfile, &fs);
34525210b06SDavid du Colombier
34625210b06SDavid du Colombier /* failed; try again */
34725210b06SDavid du Colombier }
34825210b06SDavid du Colombier
34925210b06SDavid du Colombier /*
35025210b06SDavid du Colombier * find all the disks in #S, read their partition tables and set those
35125210b06SDavid du Colombier * partitions in core, mainly so that we can access 9fat file systems.
35225210b06SDavid du Colombier * for each disk's 9fat, read plan9.ini and boot the named kernel.
35325210b06SDavid du Colombier */
35425210b06SDavid du Colombier void
bootloadproc(void *)35525210b06SDavid du Colombier bootloadproc(void *)
35625210b06SDavid du Colombier {
35725210b06SDavid du Colombier int n, dirs, sdev;
35825210b06SDavid du Colombier char kern[64];
35925210b06SDavid du Colombier char *sdevs[128];
36025210b06SDavid du Colombier Chan *sdch;
36125210b06SDavid du Colombier Dir *dirp, *dp;
36225210b06SDavid du Colombier
363e4575fb1SDavid du Colombier memset(sdevs, 0, sizeof sdevs);
36425210b06SDavid du Colombier sdch = nil;
36525210b06SDavid du Colombier while(waserror()) {
36625210b06SDavid du Colombier print("error caught at top level in bootload\n");
36725210b06SDavid du Colombier if(sdch) {
36825210b06SDavid du Colombier cclose(sdch);
36925210b06SDavid du Colombier sdch = nil;
37025210b06SDavid du Colombier }
37125210b06SDavid du Colombier }
37225210b06SDavid du Colombier bind("#S", "/dev", MAFTER); /* try to force an attach */
37325210b06SDavid du Colombier sdch = namecopen("#S", OREAD);
37425210b06SDavid du Colombier if (sdch == nil)
37525210b06SDavid du Colombier panic("no disks (no #S)");
37625210b06SDavid du Colombier sdev = 0;
37725210b06SDavid du Colombier while ((dirs = dirread(sdch, &dirp)) > 0) {
37825210b06SDavid du Colombier for (dp = dirp; dirs-- > 0; dp++)
37925210b06SDavid du Colombier if (strcmp(dp->name, "sdctl") != 0) {
38025210b06SDavid du Colombier addsdev(dp);
38125210b06SDavid du Colombier if (sdev >= nelem(sdevs))
38225210b06SDavid du Colombier print("too many sdevs; ignoring %s\n",
38325210b06SDavid du Colombier dp->name);
38425210b06SDavid du Colombier else
38525210b06SDavid du Colombier kstrdup(&sdevs[sdev++], dp->name);
38625210b06SDavid du Colombier }
38725210b06SDavid du Colombier free(dirp);
38825210b06SDavid du Colombier }
38925210b06SDavid du Colombier cclose(sdch);
39025210b06SDavid du Colombier sdch = nil;
39125210b06SDavid du Colombier if (sdev == 0)
39225210b06SDavid du Colombier panic("no disks (in #S)");
39325210b06SDavid du Colombier
39425210b06SDavid du Colombier print("disks:");
39525210b06SDavid du Colombier for (n = 0; n < sdev; n++)
39625210b06SDavid du Colombier print(" %s", sdevs[n]);
39725210b06SDavid du Colombier print("\n");
39825210b06SDavid du Colombier
39925210b06SDavid du Colombier for (n = 0; n < sdev; n++) {
40025210b06SDavid du Colombier print("trying %s...", sdevs[n]);
40125210b06SDavid du Colombier trydiskboot(sdevs[n]);
40225210b06SDavid du Colombier }
40325210b06SDavid du Colombier USED(sdch);
40425210b06SDavid du Colombier for (;;) {
405e4575fb1SDavid du Colombier askbootfile(kern, sizeof kern, nil, 0, "");
40625210b06SDavid du Colombier trybootfile(kern, nil);
40725210b06SDavid du Colombier }
40825210b06SDavid du Colombier // poperror();
40925210b06SDavid du Colombier }
410