xref: /inferno-os/os/init/bootinit.b (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1#
2# Generalized boot Inferno
3#
4
5implement Init;
6
7include "sys.m";
8	sys:	Sys;
9
10include "draw.m";
11
12include "keyring.m";
13	kr: Keyring;
14
15include "security.m";
16	auth: Auth;
17	random: Random;
18
19include "tftp.m";
20
21Bootpreadlen: con 128;
22
23Init: module
24{
25	init:	fn();
26};
27
28ip: string;
29mask: string;
30fsip: string;
31bootprotocol: string;
32bootserver: string;
33bootfile: string;
34
35debug: con 0;
36
37init()
38{
39	ipavailable: int;
40	sys = load Sys Sys->PATH;
41
42	kexecfd := sys->open("#B/kexec", Sys->OWRITE);
43	if (kexecfd == nil)
44		fatal(sys->sprint("opening #B/kexec: %r"));
45
46	ipavailable = 0;
47	if (dobind("#l", "/net", sys->MREPL) && dobind("#I", "/net", sys->MAFTER))
48		ipavailable = 1;
49
50	dobind("#c", "/dev", sys->MAFTER); 	# console device
51
52	if (!ipavailable)
53		fatal("no IP stack available");
54	cfd := sys->open("/net/ipifc/clone", sys->ORDWR);
55	if(cfd == nil)
56		fatal(sys->sprint("open /net/ipifc/clone: %r"));
57
58	if (sys->fprint(cfd, "bind ether ether0") < 0)
59		fatal(sys->sprint("binding ether0: %r"));
60
61	fsready := 0;
62
63	fsip = ipconfig(cfd);
64
65	bootstring := getenvdefault("bootpath", "tftp");
66
67	(bootprotocol, bootserver, bootfile) = parsebootstring(bootstring);
68
69	if (bootprotocol == nil)
70		fatal(bootstring + ": unrecognised syntax");
71
72	# Run dhcp if necessary
73	if (bootprotocol == "tftp" && (bootserver == nil || bootfile == nil))
74		dhcp();
75
76	# determine server
77	if (bootprotocol == "net" && bootserver == nil)
78		bootserver = fsip;
79
80	if (bootserver == nil)
81		fatal("couldn't determine boot server");
82
83	if (bootfile == nil)
84		fatal("couldn't determine boot file");
85
86	if (bootprotocol == nil)
87		fatal("couldn't determine boot protocol");
88
89	sys->print("loading %s!%s!%s\n", bootprotocol, bootserver, bootfile);
90
91	if (bootprotocol == "net") {
92		sys->print("Attempting remote mount\n");
93		if (netfs(bootserver) == 0)
94			sys->print("Remote mount successful\n");
95		else
96			fatal(sys->sprint("Remote mount failed: %r"));
97		fd := sys->open("/n/remote" + bootfile, Sys->OREAD);
98		if (fd == nil)
99			fatal(sys->sprint("%s:/n/remote%s: %r", bootserver, bootfile));
100		if (sys->stream(fd, kexecfd, 4096) < 0)
101			fatal(sys->sprint("copying %s: %r", bootfile));
102	}
103	else if (bootprotocol == "tftp") {
104		tftp := load Tftp Tftp->PATH;
105		if (tftp == nil)
106			fatal("can't load tftp module");
107		tftp->init(1);
108		errstr := tftp->receive(bootserver, bootfile, kexecfd);
109		if (errstr != nil)
110			fatal("tftp: " + errstr);
111	}
112	else
113		fatal("protocol " + bootprotocol + " not supported");
114	sys->print("Launching new kernel\n");
115	kexecfd = nil;
116}
117
118parsebootstring(s: string): (string, string, string)
119{
120	proto, server, file: string;
121	(n, l) := sys->tokenize(s, "!");
122	if (n > 3)
123		return (nil, nil, nil);
124	proto = hd l;
125	l = tl l;
126	if (l != nil) {
127		server = hd l;
128		l = tl l;
129	}
130	if (l != nil)
131		file = hd l;
132	case proto {
133	"tftp" =>
134		;
135	"net" =>
136		# can't have a default file, so n must be 3
137		if (n != 3)
138			return (nil, nil, nil);
139	* =>
140		return (nil, nil, nil);
141	}
142	return (proto, server, file);
143}
144
145dobind(f, t: string, flags: int): int
146{
147	if(sys->bind(f, t, flags) < 0) {
148		err(sys->sprint("can't bind %s on %s: %r", f, t));
149		return 0;
150	}
151	return 1;
152}
153
154err(s: string)
155{
156	sys->fprint(sys->fildes(2), "bootinit: %s\n", s);
157}
158
159hang()
160{
161	<-(chan of int);
162}
163
164fatal(s: string)
165{
166	err(s);
167	hang();
168}
169
170envlist: list of string;
171
172getenv(name: string): string
173{
174	if (envlist == nil) {
175		fd := sys->open("/dev/sysenv", Sys->OREAD);
176		if (fd != nil) {
177			ntok: int;
178			buf := array[1024] of byte;
179			nr := sys->read(fd, buf, len buf);
180			if(nr > 0)
181				(ntok, envlist) = sys->tokenize(string buf, "\n");
182		}
183	}
184	ls := envlist;
185	while(ls != nil) {
186		(ntok2, ls2) := sys->tokenize(hd ls, "=");
187		if(hd ls2 == name)
188			return hd tl ls2;
189		ls = tl ls;
190	}
191	return nil;
192}
193
194getenvdefault(name: string, default: string): string
195{
196	rv := getenv(name);
197	if (rv == nil)
198		return default;
199	return rv;
200}
201
202ipconfig(cfd: ref sys->FD): string
203{
204	ip = getenv("wireip");
205	if (ip == nil)
206		ip = getenv("ip");
207	mask = getenv("ipmask");
208	fsip = getenv("fsip");
209	if (ip != nil && mask != nil) {
210		sys->print("ip %s %s\n", ip, mask);
211		sys->fprint(cfd, "add %s %s", ip, mask);
212		gwip := getenv("gwip");
213		if (gwip != nil) {
214			sys->print("gwip %s\n", gwip);
215			rfd := sys->open("/net/iproute", Sys->ORDWR);
216			if (rfd == nil || sys->fprint(rfd, "add 0.0.0.0 0.0.0.0 %s", gwip) < 0)
217				err(sys->sprint("failed to add default route: %r"));
218		}
219	}
220	if (ip == nil || mask == nil)
221		return bootp(cfd);
222	return fsip;
223}
224
225bootpdone: int;
226
227bootp(cfd: ref sys->FD): string
228{
229	if (bootpdone == 1)
230		return fsip;
231
232	bootpdone = 1;
233
234	sys->print("bootp ...");
235
236	if (sys->fprint(cfd, "bootp") < 0) {
237		sys->print("init: bootp: %r");
238		return nil;
239	}
240
241	fd := sys->open("/net/bootp", sys->OREAD);
242	if(fd == nil) {
243		err(sys->sprint("open /net/bootp: %r"));
244		return nil;
245	}
246
247	buf := array[Bootpreadlen] of byte;
248	nr := sys->read(fd, buf, len buf);
249	fd = nil;
250	if(nr <= 0) {
251		err(sys->sprint("read /net/bootp: %r"));
252		return nil;
253	}
254	(ntok, ls) := sys->tokenize(string buf, " \t\n");
255	while(ls != nil) {
256		name := hd ls;
257		ls = tl ls;
258		if (ls == nil)
259			break;
260		value := hd ls;
261		ls = tl ls;
262		if (name == "fsip")
263			fsip = value;
264		else if (name == "ipaddr")
265			ip = value;
266		else if (name == "ipmask")
267			mask = value;
268	}
269	return fsip;
270}
271
272netfs(server: string): int
273{
274	auth = load Auth  Auth->PATH;
275	if (auth != nil)
276		auth->init();
277
278	kr = load Keyring Keyring->PATH;
279	sys->print("dial...");
280	(ok, c) := sys->dial("tcp!" + server + "!6666", nil);
281	if(ok < 0)
282		return -1;
283
284	if(kr != nil && auth != nil){
285		err: string;
286		sys->print("Authenticate ...");
287		ai := kr->readauthinfo("/nvfs/default");
288		if(ai == nil){
289			sys->print("readauthinfo /nvfs/default failed: %r\n");
290			sys->print("trying mount as `nobody'\n");
291		}
292		(c.dfd, err) = auth->client("none", ai, c.dfd);
293		if(c.dfd == nil){
294			sys->print("authentication failed: %s\n", err);
295			return -1;
296		}
297	}
298
299	sys->print("mount ...");
300
301	c.cfd = nil;
302	n := sys->mount(c.dfd, nil, "/n/remote", sys->MREPL, "");
303	if(n > 0)
304		return 0;
305	return -1;
306}
307
308#
309#
310# DHCP
311#
312#
313
314Dhcp: adt {
315	op: int;
316	htype: int;
317	hops: int;
318	xid: int;
319	secs: int;
320	flags: int;
321	ciaddr: int;
322	yiaddr: int;
323	siaddr: int;
324	giaddr: int;
325	chaddr: array of byte;
326	sname: string;
327	file: string;
328};
329
330nboputl(buf: array of byte, val: int)
331{
332	buf[0] = byte (val >> 24);
333	buf[1] = byte (val >> 16);
334	buf[2] = byte (val >> 8);
335	buf[3] = byte val;
336}
337
338nboputs(buf: array of byte, val: int)
339{
340	buf[0] = byte (val >> 8);
341	buf[1] = byte val;
342}
343
344nbogets(buf: array of byte): int
345{
346	return (int buf[0] << 8) | int buf[1];
347}
348
349nbogetl(buf: array of byte): int
350{
351	return (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | int buf[3];
352}
353
354stringget(buf: array of byte): string
355{
356	for (x := 0; x < len buf; x++)
357		if (buf[x] == byte 0)
358			break;
359	if (x == 0)
360		return nil;
361	return string buf[0 : x];
362}
363
364memcmp(b1: array of byte, b2: array of byte): int
365{
366	l := len b1;
367	if (l < len b2)
368		return int -b2[l];
369	if (l > len b2)
370		return int b1[l];
371	for (i := 0; i < l; i++) {
372		d := int b1[i] - int b2[i];
373		if (d != 0)
374			return d;
375	}
376	return 0;
377}
378
379memncpy(out: array of byte, in: array of byte)
380{
381	if (in == nil)
382		return;
383	l := len in;
384	if (l > len out)
385		l = len out;
386	out[0 :] = in[0 : l];
387}
388
389memset(out: array of byte, val: byte)
390{
391	for (l := 0; l < len out; l++)
392		out[l] = val;
393}
394
395dhcpsend(dfd: ref Sys->FD, dhcp: ref Dhcp)
396{
397	buf := array[576] of byte;
398	buf[0] = byte dhcp.op;
399	buf[1] = byte dhcp.htype;
400	buf[2] = byte len dhcp.chaddr;
401	buf[3] = byte dhcp.hops;
402	nboputl(buf[4 : 8], dhcp.xid);
403	nboputs(buf[8 : 10], dhcp.secs);
404	nboputs(buf[10 : 12], dhcp.flags);
405	nboputl(buf[12 : 16], dhcp.ciaddr);
406	nboputl(buf[16 : 20], dhcp.yiaddr);
407	nboputl(buf[20 : 24], dhcp.siaddr);
408	nboputl(buf[24 : 28], dhcp.giaddr);
409	memset(buf[28 :], byte 0);
410	memncpy(buf[28 : 44], dhcp.chaddr);
411	memncpy(buf[44 : 108], array of byte dhcp.sname);
412	memncpy(buf[108 : 236], array of byte dhcp.file);
413	sys->write(dfd, buf, len buf);
414}
415
416kill(pid: int)
417{
418	fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
419	if (fd == nil)
420		return;
421
422	msg := array of byte "kill";
423        sys->write(fd, msg, len msg);
424}
425
426ipfmt(ipaddr: int): string
427{
428	return sys->sprint("%ud.%ud.%ud.%ud",
429		(ipaddr >> 24) & 16rff,
430		(ipaddr >> 16) & 16rff,
431		(ipaddr >> 8) & 16rff,
432		ipaddr & 16rff);
433}
434
435dumpdhcp(dhcp: ref Dhcp)
436{
437	sys->print("op %d htype %d hops %d xid %ud\n", dhcp.op, dhcp.htype, dhcp.hops, dhcp.xid);
438	sys->print("secs %d flags 0x%.4ux\n", dhcp.secs, dhcp.flags);
439	sys->print("ciaddr %s\n", ipfmt(dhcp.ciaddr));
440	sys->print("yiaddr %s\n", ipfmt(dhcp.yiaddr));
441	sys->print("siaddr %s\n", ipfmt(dhcp.siaddr));
442	sys->print("giaddr %s\n", ipfmt(dhcp.giaddr));
443	sys->print("chaddr ");
444	for (x := 0; x < len dhcp.chaddr; x++)
445		sys->print("%.2ux", int dhcp.chaddr[x]);
446	sys->print("\n");
447	if (dhcp.sname != nil)
448		sys->print("sname %s\n", dhcp.sname);
449	if (dhcp.file != nil)
450		sys->print("file %s\n", dhcp.file);
451}
452
453dhcplisten(pidc: chan of int, fd: ref Sys->FD, dc: chan of ref Dhcp)
454{
455	pid := sys->pctl(0, nil);
456	pidc <-= pid;
457	buf := array [576] of byte;
458	while (1) {
459		n := sys->read(fd, buf, len buf);
460		dhcp := ref Dhcp;
461		dhcp.op = int buf[0];
462		dhcp.htype = int buf[1];
463		hlen := int buf[2];
464		dhcp.hops = int buf[3];
465		dhcp.xid = nbogetl(buf[4 : 8]);
466		dhcp.secs = nbogets(buf[8 : 10]);
467		dhcp.flags = nbogets(buf[10 : 12]);
468		dhcp.ciaddr = nbogetl(buf[12 : 16]);
469		dhcp.yiaddr = nbogetl(buf[16 : 20]);
470		dhcp.siaddr = nbogetl(buf[20 : 24]);
471		dhcp.giaddr = nbogetl(buf[24: 28]);
472		dhcp.chaddr = buf[28 : 28 + hlen];
473		dhcp.sname = stringget(buf[44 : 108]);
474		dhcp.file = stringget(buf[108 : 236]);
475		dc <-= dhcp;
476	}
477}
478
479timeoutproc(pid: chan of int, howlong: int, c: chan of string)
480{
481	pid <-= sys->pctl(0, nil);
482
483	sys->sleep(howlong);
484
485	# send timeout
486	c <-= "timed out";
487}
488
489tpid := -1;
490tc: chan of string;
491
492timeoutcancel()
493{
494	if (tpid >= 0) {
495		kill(tpid);
496		tpid = -1;
497	}
498}
499
500timeoutstart(howlong: int): (chan of string)
501{
502	timeoutcancel();
503	pidc := chan of int;
504	tc = chan of string;
505	spawn timeoutproc(pidc, howlong, tc);
506	tpid = <- pidc;
507	return tc;
508}
509
510atohn(b: byte): int
511{
512	if (b >= byte '0' && b <= byte '9')
513		return int (b - byte '0');
514	if (b >= byte 'A' && b <= byte 'F')
515		return int b - 'A' + 10;
516	if (b >= byte 'a' && b <= byte 'f')
517		return int b - 'a' + 10;
518	return -1;
519}
520
521atohb(buf: array of byte): int
522{
523	tn := atohn(buf[0]);
524	bn := atohn(buf[1]);
525	if (tn < 0 || bn < 0)
526		return -1;
527	return tn * 16 + bn;
528}
529
530gethaddr(dhcp: ref Dhcp): int
531{
532	fd := sys->open("#l/ether0/addr", Sys->OREAD);
533	if (fd == nil)
534		return 0;
535	buf := array [100] of byte;
536	n := sys->read(fd, buf, len buf);
537	if (n < 0)
538		return 0;
539	dhcp.htype = 1;
540	hlen := n / 2;
541	dhcp.chaddr = array [hlen] of byte;
542	for (i := 0; i < hlen; i++)
543		dhcp.chaddr[i] = byte atohb(buf[i * 2 : i * 2 + 2]);
544	return 1;
545}
546
547parsedq(dq: string): (int, int)
548{
549	(c, l) := sys->tokenize(dq, ".");
550	if (c != 4)
551		return (0, 0);
552	a := hd l;
553	l = tl l;
554	b := hd l;
555	l = tl l;
556	d := hd l;
557	l = tl l;
558	addr := (int a << 24) | (int b << 16) | (int d << 8) | int hd l;
559	return (1, addr);
560}
561
562dhcp()
563{
564	ok: int;
565	conn: Sys->Connection;
566	rdhcp: ref Dhcp;
567
568	if (random == nil)
569		random = load Random Random->PATH;
570
571	(ok, conn) = sys->dial("udp!255.255.255.255!67", "68");
572	if (!ok)
573		fatal(sys->sprint("failed to dial udp broadcast: %r"));
574
575	pidc := chan of int;
576	dc := chan of ref Dhcp;
577	spawn dhcplisten(pidc, conn.dfd, dc);
578	dhcppid := <- pidc;
579	dhcp := ref Dhcp;
580	dhcp.op = 1;
581	dhcp.htype  = 1;
582	gethaddr(dhcp);
583	dhcp.hops = 0;
584	dhcp.xid = random->randomint(Random->NotQuiteRandom);
585	dhcp.secs = 0;
586	dhcp.flags = 0;
587	(ok, dhcp.ciaddr) = parsedq(ip);
588	dhcp.yiaddr = 0;
589	dhcp.siaddr = 0;
590	dhcp.giaddr = 0;
591	if (bootfile != "bootp")
592		dhcp.file = bootfile;
593	else
594		dhcp.file = nil;
595	ok = 0;
596	for (count := 0; !ok && count < 5; count++) {
597		mtc := timeoutstart(3000);
598		dhcpsend(conn.dfd, dhcp);
599		timedout := 0;
600		do {
601			alt {
602				<- mtc =>
603					timedout = 1;
604				rdhcp = <- dc =>
605					if (debug)
606						dumpdhcp(rdhcp);
607					if (rdhcp.ciaddr != dhcp.ciaddr || rdhcp.xid != dhcp.xid
608						|| memcmp(rdhcp.chaddr, dhcp.chaddr) != 0) {
609						break;
610					}
611					if (rdhcp.file != nil) {
612						ok = 1;
613						timeoutcancel();
614					}
615			}
616		} while (!timedout && !ok);
617		dhcp.xid++;
618	}
619	if (ok) {
620		if (bootfile == nil)
621			bootfile = rdhcp.file;
622		if (bootserver == nil)
623			bootserver = ipfmt(rdhcp.siaddr);
624	}
625	else
626		err("bootp timed out");
627	kill(dhcppid);
628}
629