diff options
Diffstat (limited to 'os/init/bootinit.b')
| -rw-r--r-- | os/init/bootinit.b | 628 |
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); +} |
