summaryrefslogtreecommitdiff
path: root/os/init/bootinit.b
diff options
context:
space:
mode:
Diffstat (limited to 'os/init/bootinit.b')
-rw-r--r--os/init/bootinit.b628
1 files changed, 628 insertions, 0 deletions
diff --git a/os/init/bootinit.b b/os/init/bootinit.b
new file mode 100644
index 00000000..0fcd58ef
--- /dev/null
+++ b/os/init/bootinit.b
@@ -0,0 +1,628 @@
+#
+# Generalized boot Inferno
+#
+
+implement Init;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "keyring.m";
+ kr: Keyring;
+
+include "security.m";
+ auth: Auth;
+ random: Random;
+
+include "tftp.m";
+
+Bootpreadlen: con 128;
+
+Init: module
+{
+ init: fn();
+};
+
+ip: string;
+mask: string;
+fsip: string;
+bootprotocol: string;
+bootserver: string;
+bootfile: string;
+
+debug: con 0;
+
+init()
+{
+ ipavailable: int;
+ sys = load Sys Sys->PATH;
+
+ kexecfd := sys->open("#B/kexec", Sys->OWRITE);
+ if (kexecfd == nil)
+ fatal(sys->sprint("opening #B/kexec: %r"));
+
+ ipavailable = 0;
+ if (dobind("#l", "/net", sys->MREPL) && dobind("#I", "/net", sys->MAFTER))
+ ipavailable = 1;
+
+ dobind("#c", "/dev", sys->MAFTER); # console device
+
+ if (!ipavailable)
+ fatal("no IP stack available");
+ cfd := sys->open("/net/ipifc/clone", sys->ORDWR);
+ if(cfd == nil)
+ fatal(sys->sprint("open /net/ipifc/clone: %r"));
+
+ if (sys->fprint(cfd, "bind ether ether0") < 0)
+ fatal(sys->sprint("binding ether0: %r"));
+
+ fsready := 0;
+
+ fsip = ipconfig(cfd);
+
+ bootstring := getenvdefault("bootpath", "tftp");
+
+ (bootprotocol, bootserver, bootfile) = parsebootstring(bootstring);
+
+ if (bootprotocol == nil)
+ fatal(bootstring + ": unrecognised syntax");
+
+ # Run dhcp if necessary
+ if (bootprotocol == "tftp" && (bootserver == nil || bootfile == nil))
+ dhcp();
+
+ # determine server
+ if (bootprotocol == "net" && bootserver == nil)
+ bootserver = fsip;
+
+ if (bootserver == nil)
+ fatal("couldn't determine boot server");
+
+ if (bootfile == nil)
+ fatal("couldn't determine boot file");
+
+ if (bootprotocol == nil)
+ fatal("couldn't determine boot protocol");
+
+ sys->print("loading %s!%s!%s\n", bootprotocol, bootserver, bootfile);
+
+ if (bootprotocol == "net") {
+ sys->print("Attempting remote mount\n");
+ if (netfs(bootserver) == 0)
+ sys->print("Remote mount successful\n");
+ else
+ fatal(sys->sprint("Remote mount failed: %r"));
+ fd := sys->open("/n/remote" + bootfile, Sys->OREAD);
+ if (fd == nil)
+ fatal(sys->sprint("%s:/n/remote%s: %r", bootserver, bootfile));
+ if (sys->stream(fd, kexecfd, 4096) < 0)
+ fatal(sys->sprint("copying %s: %r", bootfile));
+ }
+ else if (bootprotocol == "tftp") {
+ tftp := load Tftp Tftp->PATH;
+ if (tftp == nil)
+ fatal("can't load tftp module");
+ tftp->init(1);
+ errstr := tftp->receive(bootserver, bootfile, kexecfd);
+ if (errstr != nil)
+ fatal("tftp: " + errstr);
+ }
+ else
+ fatal("protocol " + bootprotocol + " not supported");
+ sys->print("Launching new kernel\n");
+ kexecfd = nil;
+}
+
+parsebootstring(s: string): (string, string, string)
+{
+ proto, server, file: string;
+ (n, l) := sys->tokenize(s, "!");
+ if (n > 3)
+ return (nil, nil, nil);
+ proto = hd l;
+ l = tl l;
+ if (l != nil) {
+ server = hd l;
+ l = tl l;
+ }
+ if (l != nil)
+ file = hd l;
+ case proto {
+ "tftp" =>
+ ;
+ "net" =>
+ # can't have a default file, so n must be 3
+ if (n != 3)
+ return (nil, nil, nil);
+ * =>
+ return (nil, nil, nil);
+ }
+ return (proto, server, file);
+}
+
+dobind(f, t: string, flags: int): int
+{
+ if(sys->bind(f, t, flags) < 0) {
+ err(sys->sprint("can't bind %s on %s: %r", f, t));
+ return 0;
+ }
+ return 1;
+}
+
+err(s: string)
+{
+ sys->fprint(sys->fildes(2), "bootinit: %s\n", s);
+}
+
+hang()
+{
+ <-(chan of int);
+}
+
+fatal(s: string)
+{
+ err(s);
+ hang();
+}
+
+envlist: list of string;
+
+getenv(name: string): string
+{
+ if (envlist == nil) {
+ fd := sys->open("/dev/sysenv", Sys->OREAD);
+ if (fd != nil) {
+ ntok: int;
+ buf := array[1024] of byte;
+ nr := sys->read(fd, buf, len buf);
+ if(nr > 0)
+ (ntok, envlist) = sys->tokenize(string buf, "\n");
+ }
+ }
+ ls := envlist;
+ while(ls != nil) {
+ (ntok2, ls2) := sys->tokenize(hd ls, "=");
+ if(hd ls2 == name)
+ return hd tl ls2;
+ ls = tl ls;
+ }
+ return nil;
+}
+
+getenvdefault(name: string, default: string): string
+{
+ rv := getenv(name);
+ if (rv == nil)
+ return default;
+ return rv;
+}
+
+ipconfig(cfd: ref sys->FD): string
+{
+ ip = getenv("wireip");
+ if (ip == nil)
+ ip = getenv("ip");
+ mask = getenv("ipmask");
+ fsip = getenv("fsip");
+ if (ip != nil && mask != nil) {
+ sys->print("ip %s %s\n", ip, mask);
+ sys->fprint(cfd, "add %s %s", ip, mask);
+ gwip := getenv("gwip");
+ if (gwip != nil) {
+ sys->print("gwip %s\n", gwip);
+ rfd := sys->open("/net/iproute", Sys->ORDWR);
+ if (rfd == nil || sys->fprint(rfd, "add 0.0.0.0 0.0.0.0 %s", gwip) < 0)
+ err(sys->sprint("failed to add default route: %r"));
+ }
+ }
+ if (ip == nil || mask == nil)
+ return bootp(cfd);
+ return fsip;
+}
+
+bootpdone: int;
+
+bootp(cfd: ref sys->FD): string
+{
+ if (bootpdone == 1)
+ return fsip;
+
+ bootpdone = 1;
+
+ sys->print("bootp ...");
+
+ if (sys->fprint(cfd, "bootp") < 0) {
+ sys->print("init: bootp: %r");
+ return nil;
+ }
+
+ fd := sys->open("/net/bootp", sys->OREAD);
+ if(fd == nil) {
+ err(sys->sprint("open /net/bootp: %r"));
+ return nil;
+ }
+
+ buf := array[Bootpreadlen] of byte;
+ nr := sys->read(fd, buf, len buf);
+ fd = nil;
+ if(nr <= 0) {
+ err(sys->sprint("read /net/bootp: %r"));
+ return nil;
+ }
+ (ntok, ls) := sys->tokenize(string buf, " \t\n");
+ while(ls != nil) {
+ name := hd ls;
+ ls = tl ls;
+ if (ls == nil)
+ break;
+ value := hd ls;
+ ls = tl ls;
+ if (name == "fsip")
+ fsip = value;
+ else if (name == "ipaddr")
+ ip = value;
+ else if (name == "ipmask")
+ mask = value;
+ }
+ return fsip;
+}
+
+netfs(server: string): int
+{
+ auth = load Auth Auth->PATH;
+ if (auth != nil)
+ auth->init();
+
+ kr = load Keyring Keyring->PATH;
+ sys->print("dial...");
+ (ok, c) := sys->dial("tcp!" + server + "!6666", nil);
+ if(ok < 0)
+ return -1;
+
+ if(kr != nil && auth != nil){
+ err: string;
+ sys->print("Authenticate ...");
+ ai := kr->readauthinfo("/nvfs/default");
+ if(ai == nil){
+ sys->print("readauthinfo /nvfs/default failed: %r\n");
+ sys->print("trying mount as `nobody'\n");
+ }
+ (c.dfd, err) = auth->client("none", ai, c.dfd);
+ if(c.dfd == nil){
+ sys->print("authentication failed: %s\n", err);
+ return -1;
+ }
+ }
+
+ sys->print("mount ...");
+
+ c.cfd = nil;
+ n := sys->mount(c.dfd, nil, "/n/remote", sys->MREPL, "");
+ if(n > 0)
+ return 0;
+ return -1;
+}
+
+#
+#
+# DHCP
+#
+#
+
+Dhcp: adt {
+ op: int;
+ htype: int;
+ hops: int;
+ xid: int;
+ secs: int;
+ flags: int;
+ ciaddr: int;
+ yiaddr: int;
+ siaddr: int;
+ giaddr: int;
+ chaddr: array of byte;
+ sname: string;
+ file: string;
+};
+
+nboputl(buf: array of byte, val: int)
+{
+ buf[0] = byte (val >> 24);
+ buf[1] = byte (val >> 16);
+ buf[2] = byte (val >> 8);
+ buf[3] = byte val;
+}
+
+nboputs(buf: array of byte, val: int)
+{
+ buf[0] = byte (val >> 8);
+ buf[1] = byte val;
+}
+
+nbogets(buf: array of byte): int
+{
+ return (int buf[0] << 8) | int buf[1];
+}
+
+nbogetl(buf: array of byte): int
+{
+ return (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | int buf[3];
+}
+
+stringget(buf: array of byte): string
+{
+ for (x := 0; x < len buf; x++)
+ if (buf[x] == byte 0)
+ break;
+ if (x == 0)
+ return nil;
+ return string buf[0 : x];
+}
+
+memcmp(b1: array of byte, b2: array of byte): int
+{
+ l := len b1;
+ if (l < len b2)
+ return int -b2[l];
+ if (l > len b2)
+ return int b1[l];
+ for (i := 0; i < l; i++) {
+ d := int b1[i] - int b2[i];
+ if (d != 0)
+ return d;
+ }
+ return 0;
+}
+
+memncpy(out: array of byte, in: array of byte)
+{
+ if (in == nil)
+ return;
+ l := len in;
+ if (l > len out)
+ l = len out;
+ out[0 :] = in[0 : l];
+}
+
+memset(out: array of byte, val: byte)
+{
+ for (l := 0; l < len out; l++)
+ out[l] = val;
+}
+
+dhcpsend(dfd: ref Sys->FD, dhcp: ref Dhcp)
+{
+ buf := array[576] of byte;
+ buf[0] = byte dhcp.op;
+ buf[1] = byte dhcp.htype;
+ buf[2] = byte len dhcp.chaddr;
+ buf[3] = byte dhcp.hops;
+ nboputl(buf[4 : 8], dhcp.xid);
+ nboputs(buf[8 : 10], dhcp.secs);
+ nboputs(buf[10 : 12], dhcp.flags);
+ nboputl(buf[12 : 16], dhcp.ciaddr);
+ nboputl(buf[16 : 20], dhcp.yiaddr);
+ nboputl(buf[20 : 24], dhcp.siaddr);
+ nboputl(buf[24 : 28], dhcp.giaddr);
+ memset(buf[28 :], byte 0);
+ memncpy(buf[28 : 44], dhcp.chaddr);
+ memncpy(buf[44 : 108], array of byte dhcp.sname);
+ memncpy(buf[108 : 236], array of byte dhcp.file);
+ sys->write(dfd, buf, len buf);
+}
+
+kill(pid: int)
+{
+ fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
+ if (fd == nil)
+ return;
+
+ msg := array of byte "kill";
+ sys->write(fd, msg, len msg);
+}
+
+ipfmt(ipaddr: int): string
+{
+ return sys->sprint("%ud.%ud.%ud.%ud",
+ (ipaddr >> 24) & 16rff,
+ (ipaddr >> 16) & 16rff,
+ (ipaddr >> 8) & 16rff,
+ ipaddr & 16rff);
+}
+
+dumpdhcp(dhcp: ref Dhcp)
+{
+ sys->print("op %d htype %d hops %d xid %ud\n", dhcp.op, dhcp.htype, dhcp.hops, dhcp.xid);
+ sys->print("secs %d flags 0x%.4ux\n", dhcp.secs, dhcp.flags);
+ sys->print("ciaddr %s\n", ipfmt(dhcp.ciaddr));
+ sys->print("yiaddr %s\n", ipfmt(dhcp.yiaddr));
+ sys->print("siaddr %s\n", ipfmt(dhcp.siaddr));
+ sys->print("giaddr %s\n", ipfmt(dhcp.giaddr));
+ sys->print("chaddr ");
+ for (x := 0; x < len dhcp.chaddr; x++)
+ sys->print("%.2ux", int dhcp.chaddr[x]);
+ sys->print("\n");
+ if (dhcp.sname != nil)
+ sys->print("sname %s\n", dhcp.sname);
+ if (dhcp.file != nil)
+ sys->print("file %s\n", dhcp.file);
+}
+
+dhcplisten(pidc: chan of int, fd: ref Sys->FD, dc: chan of ref Dhcp)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [576] of byte;
+ while (1) {
+ n := sys->read(fd, buf, len buf);
+ dhcp := ref Dhcp;
+ dhcp.op = int buf[0];
+ dhcp.htype = int buf[1];
+ hlen := int buf[2];
+ dhcp.hops = int buf[3];
+ dhcp.xid = nbogetl(buf[4 : 8]);
+ dhcp.secs = nbogets(buf[8 : 10]);
+ dhcp.flags = nbogets(buf[10 : 12]);
+ dhcp.ciaddr = nbogetl(buf[12 : 16]);
+ dhcp.yiaddr = nbogetl(buf[16 : 20]);
+ dhcp.siaddr = nbogetl(buf[20 : 24]);
+ dhcp.giaddr = nbogetl(buf[24: 28]);
+ dhcp.chaddr = buf[28 : 28 + hlen];
+ dhcp.sname = stringget(buf[44 : 108]);
+ dhcp.file = stringget(buf[108 : 236]);
+ dc <-= dhcp;
+ }
+}
+
+timeoutproc(pid: chan of int, howlong: int, c: chan of string)
+{
+ pid <-= sys->pctl(0, nil);
+
+ sys->sleep(howlong);
+
+ # send timeout
+ c <-= "timed out";
+}
+
+tpid := -1;
+tc: chan of string;
+
+timeoutcancel()
+{
+ if (tpid >= 0) {
+ kill(tpid);
+ tpid = -1;
+ }
+}
+
+timeoutstart(howlong: int): (chan of string)
+{
+ timeoutcancel();
+ pidc := chan of int;
+ tc = chan of string;
+ spawn timeoutproc(pidc, howlong, tc);
+ tpid = <- pidc;
+ return tc;
+}
+
+atohn(b: byte): int
+{
+ if (b >= byte '0' && b <= byte '9')
+ return int (b - byte '0');
+ if (b >= byte 'A' && b <= byte 'F')
+ return int b - 'A' + 10;
+ if (b >= byte 'a' && b <= byte 'f')
+ return int b - 'a' + 10;
+ return -1;
+}
+
+atohb(buf: array of byte): int
+{
+ tn := atohn(buf[0]);
+ bn := atohn(buf[1]);
+ if (tn < 0 || bn < 0)
+ return -1;
+ return tn * 16 + bn;
+}
+
+gethaddr(dhcp: ref Dhcp): int
+{
+ fd := sys->open("#l/ether0/addr", Sys->OREAD);
+ if (fd == nil)
+ return 0;
+ buf := array [100] of byte;
+ n := sys->read(fd, buf, len buf);
+ if (n < 0)
+ return 0;
+ dhcp.htype = 1;
+ hlen := n / 2;
+ dhcp.chaddr = array [hlen] of byte;
+ for (i := 0; i < hlen; i++)
+ dhcp.chaddr[i] = byte atohb(buf[i * 2 : i * 2 + 2]);
+ return 1;
+}
+
+parsedq(dq: string): (int, int)
+{
+ (c, l) := sys->tokenize(dq, ".");
+ if (c != 4)
+ return (0, 0);
+ a := hd l;
+ l = tl l;
+ b := hd l;
+ l = tl l;
+ d := hd l;
+ l = tl l;
+ addr := (int a << 24) | (int b << 16) | (int d << 8) | int hd l;
+ return (1, addr);
+}
+
+dhcp()
+{
+ ok: int;
+ conn: Sys->Connection;
+ rdhcp: ref Dhcp;
+
+ if (random == nil)
+ random = load Random Random->PATH;
+
+ (ok, conn) = sys->dial("udp!255.255.255.255!67", "68");
+ if (!ok)
+ fatal(sys->sprint("failed to dial udp broadcast: %r"));
+
+ pidc := chan of int;
+ dc := chan of ref Dhcp;
+ spawn dhcplisten(pidc, conn.dfd, dc);
+ dhcppid := <- pidc;
+ dhcp := ref Dhcp;
+ dhcp.op = 1;
+ dhcp.htype = 1;
+ gethaddr(dhcp);
+ dhcp.hops = 0;
+ dhcp.xid = random->randomint(Random->NotQuiteRandom);
+ dhcp.secs = 0;
+ dhcp.flags = 0;
+ (ok, dhcp.ciaddr) = parsedq(ip);
+ dhcp.yiaddr = 0;
+ dhcp.siaddr = 0;
+ dhcp.giaddr = 0;
+ if (bootfile != "bootp")
+ dhcp.file = bootfile;
+ else
+ dhcp.file = nil;
+ ok = 0;
+ for (count := 0; !ok && count < 5; count++) {
+ mtc := timeoutstart(3000);
+ dhcpsend(conn.dfd, dhcp);
+ timedout := 0;
+ do {
+ alt {
+ <- mtc =>
+ timedout = 1;
+ rdhcp = <- dc =>
+ if (debug)
+ dumpdhcp(rdhcp);
+ if (rdhcp.ciaddr != dhcp.ciaddr || rdhcp.xid != dhcp.xid
+ || memcmp(rdhcp.chaddr, dhcp.chaddr) != 0) {
+ break;
+ }
+ if (rdhcp.file != nil) {
+ ok = 1;
+ timeoutcancel();
+ }
+ }
+ } while (!timedout && !ok);
+ dhcp.xid++;
+ }
+ if (ok) {
+ if (bootfile == nil)
+ bootfile = rdhcp.file;
+ if (bootserver == nil)
+ bootserver = ipfmt(rdhcp.siaddr);
+ }
+ else
+ err("bootp timed out");
+ kill(dhcppid);
+}