/* * 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 */ #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(); }