--- /sys/src/9k/boot/aux.c +++ /sys/src/9k/boot/aux.c @@ -40,7 +40,6 @@ plumb(char *dir, char *dest, int *efd, char *here) } return efd[1]; } - */ int sendmsg(int fd, char *msg) @@ -52,6 +51,7 @@ sendmsg(int fd, char *msg) return -1; return 0; } + */ void warning(char *s) @@ -66,12 +66,14 @@ warning(char *s) void fatal(char *s) { + char *msg; char buf[ERRMAX]; buf[0] = '\0'; errstr(buf, sizeof buf); - fprint(2, "boot: %s: %s\n", s, buf); - exits(0); + msg = smprint("%s: %s", s, buf); + fprint(2, "boot: %s\n", msg); + exits(msg); /* this will trigger a panic */ } int --- /sys/src/9k/boot/boot.c +++ /sys/src/9k/boot/boot.c @@ -4,6 +4,13 @@ #include #include "../boot/boot.h" +#define PARTSRV "partfs.sdXX" + +enum { + Dontpost, + Post, +}; + char cputype[64]; char sys[2*64]; char reply[256]; @@ -11,90 +18,115 @@ int printcol; int mflag; int fflag; int kflag; +int debugboot; +int nousbboot; char *bargv[Nbarg]; int bargc; static void swapproc(void); static Method *rootserver(char*); -static void usbinit(void); static void kbmap(void); -void -boot(int argc, char *argv[]) +/* + * we should inherit the standard fds all referring to /dev/cons, + * but we're being paranoid. + */ +static void +opencons(void) { - int fd, afd; - Method *mp; - char *cmd, cmdbuf[64], *iargv[16]; - char rootbuf[64]; - int islocal, ishybrid; - char *rp, *rsp; - int iargc, n; - char buf[32]; - AuthInfo *ai; - - fmtinstall('r', errfmt); - + close(0); + close(1); + close(2); bind("#c", "/dev", MBEFORE); open("/dev/cons", OREAD); open("/dev/cons", OWRITE); open("/dev/cons", OWRITE); - /* - * init will reinitialize its namespace. - * #ec gets us plan9.ini settings (*var variables). - */ +} + +/* + * init will reinitialize its namespace. + * #ec gets us plan9.ini settings (*var variables). + */ +static void +bindenvsrv(void) +{ bind("#ec", "/env", MREPL); bind("#e", "/env", MBEFORE|MCREATE); - bind("#s", "/srv", MREPL|MCREATE); + bind("#s", "/srv/", MREPL|MCREATE); +} + +static void +debuginit(int argc, char **argv) +{ + int fd; + + if(getenv("debugboot")) + debugboot = 1; + if(getenv("nousbboot")) + nousbboot = 1; #ifdef DEBUG print("argc=%d\n", argc); for(fd = 0; fd < argc; fd++) print("%#p %s ", argv[fd], argv[fd]); print("\n"); -#endif DEBUG +#endif /* DEBUG */ + SET(fd); + USED(argc, argv, fd); +} - ARGBEGIN{ - case 'k': - kflag = 1; - break; - case 'm': - mflag = 1; - break; - case 'f': - fflag = 1; - break; - }ARGEND +/* + * read disk partition tables here so that readnvram via factotum + * can see them. ideally we would have this information in + * environment variables before attaching #S, which would then + * parse them and create partitions. + */ +static void +partinit(void) +{ + char *rdparts; - readfile("#e/cputype", cputype, sizeof(cputype)); + rdparts = getenv("readparts"); + if(rdparts) + readparts(); + free(rdparts); +} - /* - * set up usb keyboard, mouse and disk, if any. - */ - usbinit(); +/* + * pick a method and initialize it + */ +static Method * +pickmethod(int argc, char **argv) +{ + Method *mp; - /* - * pick a method and initialize it - */ if(method[0].name == nil) fatal("no boot methods"); mp = rootserver(argc ? *argv : 0); (*mp->config)(mp); - islocal = strcmp(mp->name, "local") == 0; - ishybrid = strcmp(mp->name, "hybrid") == 0; - - /* - * load keymap if it is there. - */ - kbmap(); + return mp; +} - /* - * authentication agent - */ +/* + * authentication agent + * sets hostowner, creating an auth discontinuity + */ +static void +doauth(int cpuflag) +{ + dprint("auth..."); authentication(cpuflag); +} + +/* + * connect to the root file system + */ +static int +connectroot(Method *mp, int islocal, int ishybrid) +{ + int fd, n; + char buf[32]; - /* - * connect to the root file system - */ fd = (*mp->connect)(); if(fd < 0) fatal("can't connect to file server"); @@ -110,10 +142,20 @@ boot(int argc, char *argv[]) if(n < 0) fatal("can't init 9P"); srvcreate("boot", fd); + return fd; +} + +/* + * create the name space, mount the root fs + */ +static int +nsinit(int fd, char **rspp) +{ + int afd; + char *rp, *rsp; + AuthInfo *ai; + static char rootbuf[64]; - /* - * create the name space, mount the root fs - */ if(bind("/", "/", MREPL) < 0) fatal("bind /"); rp = getenv("rootspec"); @@ -141,23 +183,28 @@ boot(int argc, char *argv[]) rp = rootbuf; if(bind(rp, "/", MAFTER|MCREATE) < 0){ fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp); - if(strcmp(rootbuf, "/root//plan9") == 0){ - fprint(2, "**** warning: remove rootdir=/plan9 entry from plan9.ini\n"); - rp = "/root"; - if(bind(rp, "/", MAFTER|MCREATE) < 0) - fatal("second bind /"); - }else + if(strcmp(rootbuf, "/root//plan9") != 0) + fatal("second bind /"); + /* undo installer's work */ + fprint(2, "**** warning: remove rootdir=/plan9 " + "entry from plan9.ini\n"); + rp = "/root"; + if(bind(rp, "/", MAFTER|MCREATE) < 0) fatal("second bind /"); } } - close(fd); setenv("rootdir", rp); + *rspp = rsp; + return afd; +} - settime(islocal, afd, rsp); - if(afd > 0) - close(afd); - swapproc(); +static void +execinit(void) +{ + int iargc; + char *cmd, cmdbuf[64], *iargv[16]; + /* exec init */ cmd = getenv("init"); if(cmd == nil){ sprint(cmdbuf, "/%s/init -%s%s", cputype, @@ -175,10 +222,75 @@ boot(int argc, char *argv[]) iargv[iargc] = nil; + chmod("/srv/" PARTSRV, 0600); exec(cmd, iargv); fatal(cmd); } +void +boot(int argc, char *argv[]) +{ + int fd, afd, islocal, ishybrid; + char *rsp; + Method *mp; + + fmtinstall('r', errfmt); + opencons(); + bindenvsrv(); + debuginit(argc, argv); + + ARGBEGIN{ + case 'k': + kflag = 1; + break; + case 'm': + mflag = 1; + break; + case 'f': + fflag = 1; + break; + }ARGEND + + readfile("#e/cputype", cputype, sizeof(cputype)); + + /* + * set up usb keyboard & mouse, if any. + * starts partfs on first disk, if any, to permit nvram on usb. + */ + if (!nousbboot) + usbinit(Dontpost); + + dprint("pickmethod..."); + mp = pickmethod(argc, argv); + islocal = strcmp(mp->name, "local") == 0; + ishybrid = strcmp(mp->name, "hybrid") == 0; + + kbmap(); /* load keymap if it's there. */ + + /* don't trigger aoe until the network has been configured */ + dprint("bind #æ..."); + bind("#æ", "/dev", MAFTER); /* nvram could be here */ + dprint("bind #S..."); + bind("#S", "/dev", MAFTER); /* nvram could be here */ + dprint("partinit..."); + partinit(); + + doauth(cpuflag); /* authentication usually changes hostowner */ + rfork(RFNAMEG); /* leave existing subprocs in own namespace */ + if (!nousbboot) + usbinit(Post); /* restart partfs under the new hostowner id */ + fd = connectroot(mp, islocal, ishybrid); + afd = nsinit(fd, &rsp); + close(fd); + + settime(islocal, afd, rsp); + if(afd > 0) + close(afd); + swapproc(); + execinit(); + exits("failed to exec init"); +} + static Method* findmethod(char *a) { @@ -215,6 +327,7 @@ rootserver(char *arg) int n; /* look for required reply */ + dprint("read #e/nobootprompt..."); readfile("#e/nobootprompt", reply, sizeof(reply)); if(reply[0]){ mp = findmethod(reply); @@ -232,6 +345,7 @@ rootserver(char *arg) sprint(prompt+n, ")"); /* create default reply */ + dprint("read #e/bootargs..."); readfile("#e/bootargs", reply, sizeof(reply)); if(reply[0] == 0 && arg != 0) strcpy(reply, arg); @@ -245,6 +359,7 @@ rootserver(char *arg) /* parse replies */ do{ + dprint("outin..."); outin(prompt, reply, sizeof(reply)); mp = findmethod(reply); }while(mp == nil); @@ -255,6 +370,7 @@ HaveMethod: cp = strchr(reply, '!'); if(cp) strcpy(sys, cp+1); + dprint("pickmethod done\n"); return mp; } @@ -301,16 +417,6 @@ old9p(int fd) } static void -usbinit(void) -{ - static char usbd[] = "/boot/usbd"; - - if(access("#u/usb/ctl", 0) >= 0 && bind("#u", "/dev", MAFTER) >= 0 && - access(usbd, AEXIST) >= 0) - run(usbd, nil); -} - -static void kbmap(void) { char *f; --- /sys/src/9k/boot/boot.h +++ /sys/src/9k/boot/boot.h @@ -12,15 +12,17 @@ enum Nbarg= 16, }; -extern void authentication(int); -extern char* bootdisk; +#define dprint(...) if(debugboot) fprint(2, __VA_ARGS__); else USED(debugboot) + +extern char* bootdisk; /* defined in ../$arch/boot$CONF.c */ extern char* rootdir; extern int (*cfs)(int); extern int cpuflag; extern char cputype[]; +extern int debugboot; extern int fflag; extern int kflag; -extern Method method[]; +extern Method method[]; /* defined in ../$arch/boot$CONF.c */ extern void (*pword)(int, Method*); extern char sys[]; extern uchar hostkey[]; @@ -29,20 +31,26 @@ extern int bargc; extern char *bargv[Nbarg]; /* libc equivalent */ +extern void authentication(int); extern int cache(int); extern char* checkkey(Method*, char*, char*); +extern int chmod(char *file, int mode); extern void fatal(char*); extern void getpasswd(char*, int); extern void key(int, Method*); +extern int mountusbparts(void); extern int outin(char*, char*, int); extern int plumb(char*, char*, int*, char*); extern int readfile(char*, char*, int); +extern int readparts(void); extern long readn(int, void*, long); extern void run(char *file, ...); +extern void runv(char **argv); extern int sendmsg(int, char*); extern void setenv(char*, char*); extern void settime(int, int, char*); extern void srvcreate(char*, int); +extern void usbinit(int post); extern void warning(char*); extern int writefile(char*, char*, int); extern void boot(int, char **); --- /sys/src/9k/boot/bootauth.c +++ /sys/src/9k/boot/bootauth.c @@ -10,6 +10,7 @@ static void glenda(void); void authentication(int cpuflag) { + char *s; char *argv[16], **av; int ac; @@ -24,6 +25,9 @@ authentication(int cpuflag) av[ac++] = "factotum"; if(getenv("debugfactotum")) av[ac++] = "-p"; + s = getenv("factotumopts"); + if(s != nil && *s != '\0') + av[ac++] = s; // av[ac++] = "-d"; /* debug traces */ // av[ac++] = "-D"; /* 9p messages */ if(cpuflag) @@ -42,16 +46,11 @@ authentication(int cpuflag) case 0: exec("/boot/factotum", av); fatal("execing /boot/factotum"); - default: - break; } /* wait for agent to really be there */ while(access("/mnt/factotum", 0) < 0) sleep(250); - - if(cpuflag) - return; } static void --- /sys/src/9k/boot/bootip.c +++ /sys/src/9k/boot/bootip.c @@ -5,20 +5,18 @@ #include "boot.h" static uchar fsip[IPaddrlen]; - uchar auip[IPaddrlen]; +static uchar auip[IPaddrlen]; static char mpoint[32]; static int isvalidip(uchar*); -static void netndb(char*, uchar*); -static void netenv(char*, uchar*); - +static void getndbvar(char *name, uchar *var, char *prompt); void configip(int bargc, char **bargv, int needfs) { Waitmsg *w; int argc, pid; - char **arg, **argv, buf[32], *p; + char **arg, **argv, *p; fmtinstall('I', eipfmt); fmtinstall('M', eipfmt); @@ -48,31 +46,38 @@ configip(int bargc, char **bargv, int needfs) break; } ARGEND; - /* bind in an ip interface */ + /* bind in an ip interface or two */ + dprint("bind #I..."); if(bind("#I", mpoint, MAFTER) < 0) - fatal("bind #I\n"); + fatal("bind #I"); + dprint("bind #l0..."); if(access("#l0", 0) == 0 && bind("#l0", mpoint, MAFTER) < 0) - print("bind #l0: %r\n"); + warning("bind #l0"); + dprint("bind #l1..."); if(access("#l1", 0) == 0 && bind("#l1", mpoint, MAFTER) < 0) - print("bind #l1: %r\n"); + warning("bind #l1"); + dprint("bind #l2..."); if(access("#l2", 0) == 0 && bind("#l2", mpoint, MAFTER) < 0) - print("bind #l2: %r\n"); + warning("bind #l2"); + dprint("bind #l3..."); if(access("#l3", 0) == 0 && bind("#l3", mpoint, MAFTER) < 0) - print("bind #l3: %r\n"); + warning("bind #l3"); werrstr(""); - /* let ipconfig configure the ip interface */ + /* let ipconfig configure the first ip interface */ switch(pid = fork()){ case -1: - fatal("fork configuring ip"); + fatal("fork configuring ip: %r"); case 0: + dprint("starting ipconfig..."); exec("/boot/ipconfig", arg); - fatal("execing /ipconfig"); + fatal("execing /boot/ipconfig: %r"); default: break; } /* wait for ipconfig to finish */ + dprint("waiting for dhcp..."); for(;;){ w = wait(); if(w != nil && w->pid == pid){ @@ -84,29 +89,11 @@ configip(int bargc, char **bargv, int needfs) fatal("configuring ip"); free(w); } + dprint("\n"); - if(!needfs) - return; - - /* if we didn't get a file and auth server, query user */ - netndb("fs", fsip); - if(!isvalidip(fsip)) - netenv("fs", fsip); - while(!isvalidip(fsip)){ - buf[0] = 0; - outin("filesystem IP address", buf, sizeof(buf)); - if (parseip(fsip, buf) == -1) - fprint(2, "configip: can't parse fs ip %s\n", buf); - } - - netndb("auth", auip); - if(!isvalidip(auip)) - netenv("auth", auip); - while(!isvalidip(auip)){ - buf[0] = 0; - outin("authentication server IP address", buf, sizeof(buf)); - if (parseip(auip, buf) == -1) - fprint(2, "configip: can't parse auth ip %s\n", buf); + if(needfs) { /* if we didn't get a file and auth server, query user */ + getndbvar("fs", fsip, "filesystem IP address"); + getndbvar("auth", auip, "authentication server IP address"); } } @@ -122,7 +109,9 @@ setauthaddr(char *proto, int port) void configtcp(Method*) { + dprint("configip..."); configip(bargc, bargv, 1); + dprint("setauthaddr..."); setauthaddr("tcp", 567); } @@ -133,6 +122,7 @@ connecttcp(void) char buf[64]; snprint(buf, sizeof buf, "tcp!%I!564", fsip); + dprint("dial %s...", buf); fd = dial(buf, 0, 0, 0); if (fd < 0) werrstr("dial %s: %r", buf); @@ -162,6 +152,7 @@ netenv(char *attr, uchar *ip) return; n = read(fd, buf, sizeof(buf)-1); + close(fd); if(n <= 0) return; buf[n] = 0; @@ -199,5 +190,20 @@ netndb(char *attr, uchar *ip) return; } } - return; +} + +static void +getndbvar(char *name, uchar *var, char *prompt) +{ + char buf[64]; + + netndb(name, var); + if(!isvalidip(var)) + netenv(name, var); + while(!isvalidip(var)){ + buf[0] = 0; + outin(prompt, buf, sizeof buf); + if (parseip(var, buf) == -1) + fprint(2, "configip: can't parse %s ip %s\n", name, buf); + } } --- /sys/src/9k/boot/local.c +++ /sys/src/9k/boot/local.c @@ -9,9 +9,10 @@ static char **args; void configlocal(Method *mp) { - char *p; + char *p, *inibootdisk; int n; + inibootdisk = getenv("bootdisk"); if(*sys == '/' || *sys == '#'){ /* * if the user specifies the disk in the boot cmd or @@ -29,10 +30,13 @@ configlocal(Method *mp) disk = diskname; } else if(mp->arg){ /* - * a default is supplied when the kernel is made + * a default is optionally supplied when the kernel is made */ disk = mp->arg; - } else if(*bootdisk){ + } else if(inibootdisk != nil && *inibootdisk) + /* plan9.ini overrides default from config file */ + disk = inibootdisk; + else if(bootdisk != nil && *bootdisk){ /* * an environment variable from a pc's plan9.ini or * from the mips nvram or generated by the kernel @@ -42,10 +46,10 @@ configlocal(Method *mp) } /* if we've decided on one, pass it on to all programs */ - if(disk) - setenv("bootdisk", disk); - - USED(mp); + if(disk) { + bootdisk = disk; + setenv("bootdisk", bootdisk); + } } int @@ -130,7 +134,7 @@ connectlocalkfs(void) } void -run(char *file, ...) +runv(char **argv) { int i, pid; @@ -138,16 +142,22 @@ run(char *file, ...) case -1: fatal("fork"); case 0: - exec(file, &file); - fatal(smprint("can't exec %s: %r", file)); + exec(argv[0], argv); + fatal(smprint("can't exec %s: %r", argv[0])); default: while ((i = waitpid()) != pid && i != -1) ; if(i == -1) - fatal(smprint("wait failed running %s", file)); + fatal(smprint("wait failed running %s", argv[0])); } } +void +run(char *file, ...) +{ + runv(&file); +} + static int print1(int fd, char *s) { @@ -202,7 +212,7 @@ connectlocalfossil(void) settime(1, -1, nil); - /* make venti available */ + /* make venti available. give it 20% of free memory. */ if((venti = getenv("venti")) && (nf = tokenize(venti, f, nelem(f)))){ if((fd = open(f[0], OREAD)) >= 0){ print("venti..."); @@ -224,7 +234,8 @@ connectlocalfossil(void) f[2] = "tcp!127.1!8000"; } configloopback(); - run("/boot/venti", "-c", f[0], "-a", f[1], "-h", f[2], 0); + run("/boot/venti", "-m", "20", "-c", f[0], + "-a", f[1], "-h", f[2], nil); /* * If the announce address is tcp!*!foo, then set * $venti to tcp!127.1!foo instead, which is actually dialable. @@ -243,15 +254,16 @@ connectlocalfossil(void) } } - /* start fossil */ + /* start fossil. give it 20% of free memory. */ print("fossil(%s)...", partition); - run("/boot/fossil", "-f", partition, "-c", "srv -A fboot", "-c", "srv -p fscons", 0); + run("/boot/fossil", "-m", "20", "-f", partition, + "-c", "srv -A fboot", "-c", "srv -p fscons", nil); fd = open("#s/fboot", ORDWR); if(fd < 0){ - print("open #s/fboot: %r\n"); + warning("open #s/fboot"); return -1; } - remove("#s/fboot"); /* we'll repost as #s/boot */ + remove("#s/fboot"); /* we'll repost fd as #s/boot after fversion(fd) */ return fd; } @@ -266,10 +278,11 @@ connectlocal(void) fatal("bind #p"); bind("#S", "/dev", MAFTER); bind("#k", "/dev", MAFTER); + bind("#u", "/dev", MAFTER); bind("#æ", "/dev", MAFTER); + mountusbparts(); /* make partfs partitions visible again */ if((fd = connectlocalfossil()) < 0) - if((fd = connectlocalkfs()) < 0) - return -1; + fd = connectlocalkfs(); return fd; } --- /dev/null +++ /sys/src/9k/boot/parts.c @@ -0,0 +1,607 @@ +/* + * read disk partition tables, intended for early use on systems + * that don't use 9load. borrowed from 9load. + */ + +#include +#include +#include +#include +#include +#include "../boot/boot.h" + +typedef struct Fs Fs; +#include "/sys/src/boot/pc/dosfs.h" + +#define GSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GLONG(p) ((GSHORT((p)+2)<<16)|GSHORT(p)) + +#define trace 0 + +enum { + parttrace = 0, + + Npart = 64, + SDnpart = Npart, + + Maxsec = 2048, + Cdsec = 2048, + Normsec = 512, /* disks */ + + NAMELEN = 256, /* hack */ +}; + +typedef struct SDpart SDpart; +typedef struct SDunit SDunit; + +typedef struct SDpart { + uvlong start; + uvlong end; + char name[NAMELEN]; + int valid; +} SDpart; + +typedef struct SDunit { + int ctl; /* fds */ + int data; + + char name[NAMELEN]; + + uvlong sectors; + ulong secsize; + SDpart* part; + int npart; /* of valid partitions */ +} SDunit; + +static uchar *mbrbuf, *partbuf; + +static void +sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end) +{ + SDpart *pp; + int i, partno; + + if(parttrace) + print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end); + /* + * Check name not already used + * and look for a free slot. + */ + if(unit->part != nil){ + partno = -1; + for(i = 0; i < SDnpart; i++){ + pp = &unit->part[i]; + if(!pp->valid){ + if(partno == -1) + partno = i; + break; + } + if(strcmp(name, pp->name) == 0){ + if(pp->start == start && pp->end == end){ + if(parttrace) + print("already present\n"); + return; + } + } + } + }else{ + if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){ + if(parttrace) + print("malloc failed\n"); + return; + } + partno = 0; + } + + /* + * Check there is a free slot and size and extent are valid. + */ + if(partno == -1 || start > end || end > unit->sectors){ + print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n", + unit->name, name, start, end, unit->sectors, + partno==-1 ? "no free partitions" : "partition boundaries out of range"); + return; + } + pp = &unit->part[partno]; + pp->start = start; + pp->end = end; + strncpy(pp->name, name, NAMELEN); + pp->valid = 1; + unit->npart++; + + /* update devsd's in-memory partition table */ + if (fprint(unit->ctl, "part %s %lld %lld\n", name, start, end) < 0) + fprint(2, "can't update %s's devsd partition table for %s: %r\n", + unit->name, name); + dprint("part %s %lld %lld\n", name, start, end); +} + +static long +sdread(SDunit *unit, SDpart *pp, void* va, long len, vlong off) +{ + long l, secsize; + uvlong bno, nb; + + /* + * Check the request is within partition bounds. + */ + secsize = unit->secsize; + if (secsize == 0) + sysfatal("sdread: zero sector size"); + bno = off/secsize + pp->start; + nb = (off+len+secsize-1)/secsize + pp->start - bno; + if(bno+nb > pp->end) + nb = pp->end - bno; + if(bno >= pp->end || nb == 0) + return 0; + + seek(unit->data, bno * secsize, 0); + assert(va); /* "sdread" */ + l = read(unit->data, va, len); + if (l < 0) + return 0; + return l; +} + +static int +sdreadblk(SDunit *unit, SDpart *part, void *a, vlong off, int mbr) +{ + uchar *b; + + assert(a); /* sdreadblk */ + if(sdread(unit, part, a, unit->secsize, off) != unit->secsize){ + if(trace) + print("%s: read %lud at %lld failed\n", unit->name, + unit->secsize, (vlong)part->start*unit->secsize+off); + return -1; + } + b = a; + if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){ + if(trace) + print("%s: bad magic %.2ux %.2ux at %lld\n", + unit->name, b[0x1FE], b[0x1FF], + (vlong)part->start*unit->secsize+off); + return -1; + } + return 0; +} + +/* + * read partition table. The partition table is just ascii strings. + */ +#define MAGIC "plan9 partitions" +static void +oldp9part(SDunit *unit) +{ + SDpart *pp; + char *field[3], *line[Npart+1]; + ulong n; + uvlong start, end; + int i; + + /* + * We have some partitions already. + */ + pp = &unit->part[unit->npart]; + + /* + * We prefer partition tables on the second to last sector, + * but some old disks use the last sector instead. + */ + strcpy(pp->name, "partition"); + pp->start = unit->sectors - 2; + pp->end = unit->sectors - 1; + + dprint("oldp9part %s\n", unit->name); + if(sdreadblk(unit, pp, partbuf, 0, 0) < 0) + return; + + if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) { + /* not found on 2nd last sector; look on last sector */ + pp->start++; + pp->end++; + if(sdreadblk(unit, pp, partbuf, 0, 0) < 0) + return; + if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) + return; + print("%s: using old plan9 partition table on last sector\n", unit->name); + }else + print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name); + + /* we found a partition table, so add a partition partition */ + unit->npart++; + partbuf[unit->secsize-1] = '\0'; + + /* + * parse partition table + */ + n = gettokens((char*)partbuf, line, Npart+1, "\n"); + if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){ + for(i = 1; i < n && unit->npart < SDnpart; i++){ + if(gettokens(line[i], field, 3, " ") != 3) + break; + start = strtoull(field[1], 0, 0); + end = strtoull(field[2], 0, 0); + if(start >= end || end > unit->sectors) + break; + sdaddpart(unit, field[0], start, end); + } + } +} + +static SDpart* +sdfindpart(SDunit *unit, char *name) +{ + int i; + + if(parttrace) + print("findpart %d %s %s: ", unit->npart, unit->name, name); + for(i=0; inpart; i++) { + if(parttrace) + print("%s...", unit->part[i].name); + if(strcmp(unit->part[i].name, name) == 0){ + if(parttrace) + print("\n"); + return &unit->part[i]; + } + } + if(parttrace) + print("not found\n"); + return nil; +} + +/* + * look for a plan 9 partition table on drive `unit' in the second + * sector (sector 1) of partition `name'. + * if found, add the partitions defined in the table. + */ +static void +p9part(SDunit *unit, char *name) +{ + SDpart *p; + char *field[4], *line[Npart+1]; + uvlong start, end; + int i, n; + + dprint("p9part %s %s\n", unit->name, name); + p = sdfindpart(unit, name); + if(p == nil) + return; + + if(sdreadblk(unit, p, partbuf, unit->secsize, 0) < 0) + return; + partbuf[unit->secsize-1] = '\0'; + + if(strncmp((char*)partbuf, "part ", 5) != 0) + return; + + n = gettokens((char*)partbuf, line, Npart+1, "\n"); + if(n == 0) + return; + for(i = 0; i < n && unit->npart < SDnpart; i++){ + if(strncmp(line[i], "part ", 5) != 0) + break; + if(gettokens(line[i], field, 4, " ") != 4) + break; + start = strtoull(field[2], 0, 0); + end = strtoull(field[3], 0, 0); + if(start >= end || end > unit->sectors) + break; + sdaddpart(unit, field[1], p->start+start, p->start+end); + } +} + +static int +isdos(int t) +{ + return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X; +} + +static int +isextend(int t) +{ + return t==EXTEND || t==EXTHUGE || t==LEXTEND; +} + +/* + * Fetch the first dos and all plan9 partitions out of the MBR partition table. + * We return -1 if we did not find a plan9 partition. + */ +static int +mbrpart(SDunit *unit) +{ + Dospart *dp; + uvlong taboffset, start, end; + uvlong firstxpart, nxtxpart; + int havedos, i, nplan9; + char name[10]; + + taboffset = 0; + dp = (Dospart*)&mbrbuf[0x1BE]; + { + /* get the MBR (allowing for DMDDO) */ + if(sdreadblk(unit, &unit->part[0], mbrbuf, + (vlong)taboffset * unit->secsize, 1) < 0) + return -1; + for(i=0; i<4; i++) + if(dp[i].type == DMDDO) { + if(trace) + print("DMDDO partition found\n"); + taboffset = 63; + if(sdreadblk(unit, &unit->part[0], mbrbuf, + (vlong)taboffset * unit->secsize, 1) < 0) + return -1; + i = -1; /* start over */ + } + } + + /* + * Read the partitions, first from the MBR and then + * from successive extended partition tables. + */ + nplan9 = 0; + havedos = 0; + firstxpart = 0; + for(;;) { + if(sdreadblk(unit, &unit->part[0], mbrbuf, + (vlong)taboffset * unit->secsize, 1) < 0) + return -1; + if(trace) { + if(firstxpart) + print("%s ext %llud ", unit->name, taboffset); + else + print("%s mbr ", unit->name); + } + nxtxpart = 0; + for(i=0; i<4; i++) { + if(trace) + print("dp %d...", dp[i].type); + start = taboffset+GLONG(dp[i].start); + end = start+GLONG(dp[i].len); + + if(dp[i].type == PLAN9) { + if(nplan9 == 0) + strcpy(name, "plan9"); + else + sprint(name, "plan9.%d", nplan9); + sdaddpart(unit, name, start, end); + p9part(unit, name); + nplan9++; + } + + /* + * We used to take the active partition (and then the first + * when none are active). We have to take the first here, + * so that the partition we call ``dos'' agrees with the + * partition disk/fdisk calls ``dos''. + */ + if(havedos==0 && isdos(dp[i].type)){ + havedos = 1; + sdaddpart(unit, "dos", start, end); + } + + /* nxtxpart is relative to firstxpart (or 0), not taboffset */ + if(isextend(dp[i].type)){ + nxtxpart = start-taboffset+firstxpart; + if(trace) + print("link %llud...", nxtxpart); + } + } + if(trace) + print("\n"); + + if(!nxtxpart) + break; + if(!firstxpart) + firstxpart = nxtxpart; + taboffset = nxtxpart; + } + return nplan9 ? 0 : -1; +} + +/* + * To facilitate booting from CDs, we create a partition for + * the boot floppy image embedded in a bootable CD. + */ +static int +part9660(SDunit *unit) +{ + uchar buf[Maxsec]; + ulong a, n; + uchar *p; + + if(unit->secsize != Cdsec) + return -1; + + if(sdread(unit, &unit->part[0], buf, Cdsec, 17*Cdsec) < 0) + return -1; + + if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0) + return -1; + + + p = buf+0x47; + a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + + if(sdread(unit, &unit->part[0], buf, Cdsec, a*Cdsec) < 0) + return -1; + + if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0 + || memcmp(buf+30, "\x55\xAA", 2) != 0 + || buf[0x20] != 0x88) + return -1; + + p = buf+0x28; + a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + + switch(buf[0x21]){ + case 0x01: + n = 1200*1024; + break; + case 0x02: + n = 1440*1024; + break; + case 0x03: + n = 2880*1024; + break; + default: + return -1; + } + n /= Cdsec; + + print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n); + sdaddpart(unit, "cdboot", a, a+n); + return 0; +} + +enum { + NEW = 1<<0, + OLD = 1<<1 +}; + +/* + * read unit->data to look for partition tables. + * if found, stash partitions in environment and write them to ctl too. + */ +static void +partition(SDunit *unit) +{ + int type; + char *p; + + if(unit->part == 0) + return; + + if(part9660(unit) == 0) + return; + + p = getenv("partition"); + if(p != nil && strncmp(p, "new", 3) == 0) + type = NEW; + else if(p != nil && strncmp(p, "old", 3) == 0) + type = OLD; + else + type = NEW|OLD; + + if(mbrbuf == nil) { + mbrbuf = malloc(Maxsec); + partbuf = malloc(Maxsec); + if(mbrbuf==nil || partbuf==nil) { + free(mbrbuf); + free(partbuf); + partbuf = mbrbuf = nil; + return; + } + } + + /* + * there might be no mbr (e.g. on a very large device), so look for + * a bare plan 9 partition table if mbrpart fails. + */ + if((type & NEW) && mbrpart(unit) >= 0){ + /* nothing to do */ + } + else if (type & NEW) + p9part(unit, "data"); + else if(type & OLD) + oldp9part(unit); +} + +static void +rdgeom(SDunit *unit) +{ + char *line; + char *flds[5]; + Biobuf bb; + Biobuf *bp; + static char geom[] = "geometry "; + + bp = &bb; + seek(unit->ctl, 0, 0); + Binit(bp, unit->ctl, OREAD); + while((line = Brdline(bp, '\n')) != nil){ + line[Blinelen(bp) - 1] = '\0'; + if (strncmp(line, geom, sizeof geom - 1) == 0) + break; + } + if (line != nil && tokenize(line, flds, nelem(flds)) >= 3) { + unit->sectors = atoll(flds[1]); + unit->secsize = atoll(flds[2]); + } + Bterm(bp); + seek(unit->ctl, 0, 0); +} + +static void +setpartitions(char *name, int ctl, int data) +{ + SDunit sdunit; + SDunit *unit; + SDpart *part0; + + unit = &sdunit; + memset(unit, 0, sizeof *unit); + unit->ctl = ctl; + unit->data = data; + + unit->secsize = Normsec; /* default: won't work for CDs */ + unit->sectors = ~0ull; + rdgeom(unit); + strncpy(unit->name, name, sizeof unit->name); + unit->part = mallocz(sizeof(SDpart) * SDnpart, 1); + + part0 = &unit->part[0]; + part0->end = unit->sectors - 1; + strcpy(part0->name, "data"); + part0->valid = 1; + unit->npart++; + + mbrbuf = malloc(Maxsec); + partbuf = malloc(Maxsec); + partition(unit); + free(unit->part); +} + +/* + * read disk partition tables so that readnvram via factotum + * can see them. + */ +int +readparts(void) +{ + int i, n, ctl, data, fd; + char *name, *ctlname, *dataname; + Dir *dir; + + fd = open("/dev", OREAD); + if(fd < 0) + return -1; + n = dirreadall(fd, &dir); + close(fd); + + for(i = 0; i < n; i++) { + name = dir[i].name; + if (strncmp(name, "sd", 2) != 0) + continue; + + ctlname = smprint("/dev/%s/ctl", name); + dataname = smprint("/dev/%s/data", name); + if (ctlname == nil || dataname == nil) { + free(ctlname); + free(dataname); + continue; + } + + ctl = open(ctlname, ORDWR); + data = open(dataname, OREAD); + free(ctlname); + free(dataname); + + if (ctl >= 0 && data >= 0) + setpartitions(dataname, ctl, data); + close(ctl); + close(data); + } + free(dir); + return 0; +} --- /sys/src/9k/boot/settime.c +++ /sys/src/9k/boot/settime.c @@ -12,12 +12,13 @@ void settime(int islocal, int afd, char *rp) { int n, f; - int timeset; Dir dir[2]; char timebuf[64]; + static int timeset; + if(timeset) + return; print("time..."); - timeset = 0; if(islocal){ /* * set the time from the real time clock --- /dev/null +++ /sys/src/9k/boot/usb.c @@ -0,0 +1,147 @@ +/* usb support */ + +#include +#include +#include +#include +#include "../boot/boot.h" + +#define PARTSRV "partfs.sdXX" + +enum { + Dontpost, + Post, +}; + +static char usbdisk0[] = "/dev/sdU0.0"; +static char sdxxctl[] = "/dev/sdXX/ctl"; + +/* + * run argv[0] (short name is name) and wait awhile for file to appear. + * file must be generated by running argv[0]; if it already exists, we're done. + */ +static int +start(char *name, char **argv, char *file) +{ + int cnt; + + if(access(file, AEXIST) >= 0) + return 0; + if(access(argv[0], AEXIST) < 0) { + fprint(2, "no %s...", argv[0]); + return -1; + } + + dprint("%s...", name); + runv(argv); + for(cnt = 10; cnt > 0 && access(file, AEXIST) < 0; cnt--) + sleep(100); + if (cnt <= 0) { + dprint("no %s...", file); + return -1; + } + return 0; +} + +int +chmod(char *file, int mode) +{ + Dir *dir; + + dir = dirstat(file); + if (dir == nil) { + dprint("can't stat %s: %r\n", file); + return -1; + } + dir->mode &= ~0777; + dir->mode |= mode & 0777; + dirwstat("/srv/" PARTSRV, dir); + free(dir); + return 0; +} + +/* start partfs on first usb disk, if any. optionally post partfs in /srv. */ +static int +startpartfs(int post) +{ + int r, i; + char *parts; + char *partfsv[32]; + + if(access(usbdisk0, AEXIST) < 0) + return -1; /* can't run partfs until usbd is mounted */ + + if (post) + remove("/srv/" PARTSRV); + + i = 0; + partfsv[i++] = "/boot/partfs"; + /* + * hack for booting from usb: if /env/sdB0part (from 9load) exists, + * pass it to partfs for sdXX. + */ + parts = getenv("sdB0part"); + if (parts != nil) { + partfsv[i++] = "-p"; + partfsv[i++] = parts; + } + if (post) { + partfsv[i++] = "-s"; + partfsv[i++] = PARTSRV; + } + partfsv[i++] = usbdisk0; + partfsv[i] = nil; + r = start("partfs", partfsv, sdxxctl); + + if (post) + chmod("/srv/" PARTSRV, 0666); + return r; +} + +static int +mountusb(void) +{ + int fd; + + dprint("mount usbd..."); + fd = open("/srv/usb", ORDWR); + if(fd < 0) + warning("can't open /srv/usb"); + else if(mount(fd, -1, "/dev", MBEFORE, "") < 0) { + warning("mount -a /srv/usb /dev"); + close(fd); + } else + return 0; /* mount closed fd */ + return -1; +} + +int +mountusbparts(void) +{ + mountusb(); + return startpartfs(Post); +} + +/* + * start usbd, which mounts itself on /dev. + * start partfs on first disk, if any, to permit nvram on usb. + */ +void +usbinit(int post) +{ + int cnt; + static char *usbdv[] = { "/boot/usbd", nil }; + + if(access("#u/usb/ctl", AEXIST) < 0 || bind("#u", "/dev", MAFTER) < 0) + return; + dprint("usbinit..."); + start("usbd", usbdv, "/srv/usb"); + + /* allow a little time for usbd's device discovery */ + for(cnt = 20; cnt > 0 && access(usbdisk0, AEXIST) < 0; cnt--) + sleep(100); + if(cnt > 0) + startpartfs(post); + else + dprint("no usb disk..."); +} --- /sys/src/9k/mk/bootmkfile +++ /sys/src/9k/mk/bootmkfile @@ -10,8 +10,10 @@ BOOTFILES=\ local.$O\ embed.$O\ settime.$O\ + parts.$O\ paq.$O\ printstub.$O\ + usb.$O\ $BOOTLIB(%.$O):N: %.$O