xref: /inferno-os/appl/cmd/auxi/mangaload.b (revision 9274481003af38a88988b4e9a3a2c3e0df206bee)
137da2899SCharles.Forsythimplement Mangaload;
237da2899SCharles.Forsyth
337da2899SCharles.Forsyth# to do:
437da2899SCharles.Forsyth#	- set arp entry based on /lib/ndb if necessary
537da2899SCharles.Forsyth
637da2899SCharles.Forsythinclude "sys.m";
737da2899SCharles.Forsyth	sys: Sys;
837da2899SCharles.Forsyth
937da2899SCharles.Forsythinclude "draw.m";
1037da2899SCharles.Forsyth
1137da2899SCharles.Forsythinclude "ip.m";
1237da2899SCharles.Forsyth	ip: IP;
1337da2899SCharles.Forsyth	IPaddr: import ip;
1437da2899SCharles.Forsyth
1537da2899SCharles.Forsythinclude "timers.m";
1637da2899SCharles.Forsyth	timers: Timers;
1737da2899SCharles.Forsyth	Timer: import timers;
1837da2899SCharles.Forsyth
19*92744810Sforsythinclude "dial.m";
20*92744810Sforsyth	dial: Dial;
21*92744810Sforsyth
2237da2899SCharles.Forsythinclude "arg.m";
2337da2899SCharles.Forsyth
2437da2899SCharles.ForsythMangaload: module
2537da2899SCharles.Forsyth{
2637da2899SCharles.Forsyth	init:	fn(nil: ref Draw->Context, nil: list of string);
2737da2899SCharles.Forsyth};
2837da2899SCharles.Forsyth
2937da2899SCharles.Forsyth# manga parameters
3037da2899SCharles.ForsythFlashBlocksize: con 16r10000;
3137da2899SCharles.ForsythFlashSize: con 16r400000;	# 4meg for now
3237da2899SCharles.ForsythFlashUserArea: con 16r3C0000;
3337da2899SCharles.Forsyth
3437da2899SCharles.Forsyth# magic values
3537da2899SCharles.ForsythFooterOffset: con 16rFFEC;
3637da2899SCharles.ForsythFooterSig: con 16rA0FFFF9F;	# ARM flash library
3737da2899SCharles.ForsythFileInfosize: con 64;
3837da2899SCharles.ForsythFileNamesize: con FileInfosize - 3*4;	# x, y, z
3937da2899SCharles.ForsythPacketdatasize: con 1500-28;	# ether data less IP + ICMP header
4037da2899SCharles.ForsythRequestTimeout: con 500;
4137da2899SCharles.ForsythProbecount: con 10;	# query unit every so many packets
4237da2899SCharles.Forsyth
4337da2899SCharles.Forsyth# manga uses extended TFTP ops in ICMP InfoRequest packets
4437da2899SCharles.ForsythTftp_Req: con 0;
4537da2899SCharles.ForsythTftp_Read: con 1;
4637da2899SCharles.ForsythTftp_Write: con 2;
4737da2899SCharles.ForsythTftp_Data: con 3;
4837da2899SCharles.ForsythTftp_Ack: con 4;
4937da2899SCharles.ForsythTftp_Error: con 5;
5037da2899SCharles.ForsythTftp_Last: con 6;
5137da2899SCharles.Forsyth
5237da2899SCharles.ForsythIcmp: adt
5337da2899SCharles.Forsyth{
5437da2899SCharles.Forsyth	ttl:	int;	# time to live
5537da2899SCharles.Forsyth	src:	IPaddr;
5637da2899SCharles.Forsyth	dst:	IPaddr;
5737da2899SCharles.Forsyth	ptype:	int;
5837da2899SCharles.Forsyth	code:	int;
5937da2899SCharles.Forsyth	id:	int;
6037da2899SCharles.Forsyth	seq:	int;
6137da2899SCharles.Forsyth	data:	array of byte;
6237da2899SCharles.Forsyth	munged:	int;	# packet received but corrupt
6337da2899SCharles.Forsyth
6437da2899SCharles.Forsyth	unpack:	fn(b: array of byte): ref Icmp;
6537da2899SCharles.Forsyth};
6637da2899SCharles.Forsyth
6737da2899SCharles.Forsyth# ICMP packet types
6837da2899SCharles.ForsythEchoReply: con 0;
6937da2899SCharles.ForsythUnreachable: con 3;
7037da2899SCharles.ForsythSrcQuench: con 4;
7137da2899SCharles.ForsythEchoRequest: con 8;
7237da2899SCharles.ForsythTimeExceed: con 11;
7337da2899SCharles.ForsythTimestamp: con 13;
7437da2899SCharles.ForsythTimestampReply: con 14;
7537da2899SCharles.ForsythInfoRequest: con 15;
7637da2899SCharles.ForsythInfoReply: con 16;
7737da2899SCharles.Forsyth
7837da2899SCharles.ForsythNmsg: con 32;
7937da2899SCharles.ForsythInterval: con 1000;	# ms
8037da2899SCharles.Forsyth
8137da2899SCharles.Forsythdebug := 0;
8237da2899SCharles.Forsythflashblock := 1;	# never 0, that's the boot firmware
8337da2899SCharles.Forsythmaxfilesize := 8*FlashBlocksize;
8437da2899SCharles.Forsythflashlim := FlashSize/FlashBlocksize;
8537da2899SCharles.Forsythloadinitrd := 0;
8637da2899SCharles.Forsythmaxlen := 512*1024;
8737da2899SCharles.Forsythmypid := 0;
8837da2899SCharles.ForsythDatablocksize: con 4096;
8937da2899SCharles.Forsyth
9037da2899SCharles.Forsythinit(nil: ref Draw->Context, args: list of string)
9137da2899SCharles.Forsyth{
9237da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
9337da2899SCharles.Forsyth	timers = load Timers Timers->PATH;
94*92744810Sforsyth	dial = load Dial Dial->PATH;
9537da2899SCharles.Forsyth	ip = load IP IP->PATH;
9637da2899SCharles.Forsyth	ip->init();
9737da2899SCharles.Forsyth
9837da2899SCharles.Forsyth
9937da2899SCharles.Forsyth	arg := load Arg Arg->PATH;
10037da2899SCharles.Forsyth	arg->init(args);
10137da2899SCharles.Forsyth	arg->setusage("mangaload [-48dr] destination file");
10237da2899SCharles.Forsyth	while((o := arg->opt()) != 0)
10337da2899SCharles.Forsyth		case o {
10437da2899SCharles.Forsyth		'4' =>
10537da2899SCharles.Forsyth			flashlim = 4*1024*1024/FlashBlocksize;
10637da2899SCharles.Forsyth		'8' =>
10737da2899SCharles.Forsyth			flashlim = 8*1024*1024/FlashBlocksize;
10837da2899SCharles.Forsyth		'r' =>
10937da2899SCharles.Forsyth			loadinitrd = 1;
11037da2899SCharles.Forsyth			flashblock = 9;
11137da2899SCharles.Forsyth			if(flashlim > 4*1024*1024/FlashBlocksize)
11237da2899SCharles.Forsyth				maxfilesize = 113*FlashBlocksize;
11337da2899SCharles.Forsyth			else
11437da2899SCharles.Forsyth				maxfilesize = 50*FlashBlocksize;
11537da2899SCharles.Forsyth		'd' =>
11637da2899SCharles.Forsyth			debug++;
11737da2899SCharles.Forsyth		}
11837da2899SCharles.Forsyth	args = arg->argv();
11937da2899SCharles.Forsyth	if(len args != 2)
12037da2899SCharles.Forsyth		arg->usage();
12137da2899SCharles.Forsyth	arg = nil;
12237da2899SCharles.Forsyth
12337da2899SCharles.Forsyth	sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil);
12437da2899SCharles.Forsyth
12537da2899SCharles.Forsyth	filename := hd tl args;
12637da2899SCharles.Forsyth	fd := sys->open(filename, Sys->OREAD);
12737da2899SCharles.Forsyth	if(fd == nil){
12837da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "mangaload: can't open %s: %r\n", filename);
12937da2899SCharles.Forsyth		raise "fail:open";
13037da2899SCharles.Forsyth	}
13137da2899SCharles.Forsyth	(ok, d) := sys->fstat(fd);
13237da2899SCharles.Forsyth	if(ok < 0){
13337da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "mangaload: can't stat %s: %r\n", filename);
13437da2899SCharles.Forsyth		raise "fail:stat";
13537da2899SCharles.Forsyth	}
13637da2899SCharles.Forsyth	if(d.length > big maxfilesize){
13737da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "mangaload: file %s too long (must not exceed %d bytes)\n",
13837da2899SCharles.Forsyth			filename, maxfilesize);
13937da2899SCharles.Forsyth		raise "fail:size";
14037da2899SCharles.Forsyth	}
14137da2899SCharles.Forsyth	filesize := int d.length;
14237da2899SCharles.Forsyth
14337da2899SCharles.Forsyth	port := sys->sprint("%d", 16r8695);
144*92744810Sforsyth	addr := dial->netmkaddr(hd args, "icmp", port);
145*92744810Sforsyth	c := dial->dial(addr, port);
146*92744810Sforsyth	if(c == nil){
14737da2899SCharles.Forsyth		sys->fprint(sys->fildes(2), "mangaload: can't dial %s: %r\n", addr);
14837da2899SCharles.Forsyth		raise "fail:dial";
14937da2899SCharles.Forsyth	}
15037da2899SCharles.Forsyth
15137da2899SCharles.Forsyth	tpid := timers->init(20);
15237da2899SCharles.Forsyth
15337da2899SCharles.Forsyth	pids := chan of int;
15437da2899SCharles.Forsyth	replies := chan [2] of ref Icmp;
15537da2899SCharles.Forsyth	spawn reader(c.dfd, replies, pids);
15637da2899SCharles.Forsyth	rpid := <-pids;
15737da2899SCharles.Forsyth
15837da2899SCharles.Forsyth	flashoffset := flashblock * FlashBlocksize;
15937da2899SCharles.Forsyth
16037da2899SCharles.Forsyth	# file name first
16137da2899SCharles.Forsyth	bname := array of byte filename;
16237da2899SCharles.Forsyth	l := len bname;
16337da2899SCharles.Forsyth	buf := array[Packetdatasize] of byte;
16437da2899SCharles.Forsyth	ip->put4(buf, 0, filesize);
16537da2899SCharles.Forsyth	ip->put4(buf, 4, l);
16637da2899SCharles.Forsyth	buf[8:] = bname;
16737da2899SCharles.Forsyth	l += 2*4;
16837da2899SCharles.Forsyth	buf[l++] = byte 0;
16937da2899SCharles.Forsyth	ip->put4(buf, l, flashoffset);
17037da2899SCharles.Forsyth	l += 4;
17137da2899SCharles.Forsyth	{
17237da2899SCharles.Forsyth		if(send(c.dfd, buf[0:l], Tftp_Write, 0) < 0)
17337da2899SCharles.Forsyth			senderr();
17437da2899SCharles.Forsyth		(op, iseq, data) := recv(replies, 400);
17537da2899SCharles.Forsyth		sys->print("initial reply: %d %d\n", op, iseq);
17637da2899SCharles.Forsyth		if(op != Tftp_Ack){
17737da2899SCharles.Forsyth			why := "no response";
17837da2899SCharles.Forsyth			if(op == Tftp_Error)
17937da2899SCharles.Forsyth				why = "manga cannot receive file";
18037da2899SCharles.Forsyth			sys->fprint(sys->fildes(2), "mangaload: %s\n", why);
18137da2899SCharles.Forsyth			raise "fail:error";
18237da2899SCharles.Forsyth		}
18337da2899SCharles.Forsyth		sys->print("sending %s size %d at address %d (0x%x)\n", filename, filesize, flashoffset, flashoffset);
18437da2899SCharles.Forsyth		seq := 1;
18537da2899SCharles.Forsyth		nsent := 0;
18637da2899SCharles.Forsyth		last := 0;
18737da2899SCharles.Forsyth		while((n := sys->read(fd, buf, len buf)) >= 0 && !last){
18837da2899SCharles.Forsyth			last = n != len buf;
18937da2899SCharles.Forsyth		  Retry:
19037da2899SCharles.Forsyth			for(;;){
19137da2899SCharles.Forsyth				if(++nsent%10 == 0){	# probe
19237da2899SCharles.Forsyth					o = Tftp_Req;
19337da2899SCharles.Forsyth					send(c.dfd, array[0] of byte, Tftp_Req, seq);
19437da2899SCharles.Forsyth					(op, iseq, data) = recv(replies, 500);
19537da2899SCharles.Forsyth					if(debug || op != Tftp_Ack)
19637da2899SCharles.Forsyth						sys->print("ack reply: %d %d\n", op, iseq);
19737da2899SCharles.Forsyth					if(op == Tftp_Last || op == Tftp_Error){
19837da2899SCharles.Forsyth						if(op == Tftp_Last)
19937da2899SCharles.Forsyth							sys->print("timed out\n");
20037da2899SCharles.Forsyth						else
20137da2899SCharles.Forsyth							sys->print("error reply\n");
20237da2899SCharles.Forsyth						raise "disaster";
20337da2899SCharles.Forsyth					}
20437da2899SCharles.Forsyth					if(debug)
20537da2899SCharles.Forsyth						sys->print("ok\n");
20637da2899SCharles.Forsyth					continue Retry;
20737da2899SCharles.Forsyth				}
20837da2899SCharles.Forsyth				send(c.dfd, buf[0:n], Tftp_Data, seq);
20937da2899SCharles.Forsyth				(op, iseq, data) = recv(replies, 40);
21037da2899SCharles.Forsyth				case op {
21137da2899SCharles.Forsyth				Tftp_Error =>
21237da2899SCharles.Forsyth					sys->fprint(sys->fildes(2), "mangaload: manga refused data\n");
21337da2899SCharles.Forsyth					raise "disaster";
21437da2899SCharles.Forsyth				Tftp_Ack =>
21537da2899SCharles.Forsyth					if(seq == iseq){
21637da2899SCharles.Forsyth						seq++;
21737da2899SCharles.Forsyth						break Retry;
21837da2899SCharles.Forsyth					}
21937da2899SCharles.Forsyth					sys->print("sequence error: rcvd %d expected %d\n", iseq, seq);
22037da2899SCharles.Forsyth					if(iseq > seq){
22137da2899SCharles.Forsyth						sys->print("unrecoverable sequence error\n");
22237da2899SCharles.Forsyth						send(c.dfd, array[0] of byte, Tftp_Data, ++seq);	# stop manga
22337da2899SCharles.Forsyth						raise "disaster";
22437da2899SCharles.Forsyth					}
22537da2899SCharles.Forsyth					# resend
22637da2899SCharles.Forsyth					sys->seek(fd, -big ((seq-iseq)*len buf), 1);
22737da2899SCharles.Forsyth					seq = iseq;
22837da2899SCharles.Forsyth				Tftp_Last =>
22937da2899SCharles.Forsyth					seq++;
23037da2899SCharles.Forsyth					break Retry;	# timeout ok: manga doesn't usually reply unless packet lost
23137da2899SCharles.Forsyth				}
23237da2899SCharles.Forsyth			}
23337da2899SCharles.Forsyth		}
23437da2899SCharles.Forsyth	}exception{
23537da2899SCharles.Forsyth	* =>
23637da2899SCharles.Forsyth		;
23737da2899SCharles.Forsyth	}
23837da2899SCharles.Forsyth	kill(rpid);
23937da2899SCharles.Forsyth	kill(tpid);
24037da2899SCharles.Forsyth	sys->print("ok?\n");
24137da2899SCharles.Forsyth}
24237da2899SCharles.Forsyth
24337da2899SCharles.Forsythkill(pid: int)
24437da2899SCharles.Forsyth{
24537da2899SCharles.Forsyth	if(pid)
24637da2899SCharles.Forsyth		sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
24737da2899SCharles.Forsyth}
24837da2899SCharles.Forsyth
24937da2899SCharles.Forsythsenderr()
25037da2899SCharles.Forsyth{
25137da2899SCharles.Forsyth	sys->fprint(sys->fildes(2), "mangaload: icmp write failed: %r\n");
25237da2899SCharles.Forsyth	raise "disaster";
25337da2899SCharles.Forsyth}
25437da2899SCharles.Forsyth
25537da2899SCharles.Forsythsend(fd: ref Sys->FD, data: array of byte, op: int, seq: int): int
25637da2899SCharles.Forsyth{
25737da2899SCharles.Forsyth	buf := array[64*1024+512] of {* => byte 0};
25837da2899SCharles.Forsyth	buf[Odata:] = data;
25937da2899SCharles.Forsyth	ip->put2(buf, Oseq, seq);
26037da2899SCharles.Forsyth	buf[Otype] = byte InfoRequest;
26137da2899SCharles.Forsyth	buf[Ocode] = byte op;
26237da2899SCharles.Forsyth	if(sys->write(fd, buf, Odata+len data) < Odata+len data)
26337da2899SCharles.Forsyth		return -1;
26437da2899SCharles.Forsyth	if(debug)
26537da2899SCharles.Forsyth		sys->print("sent op=%d seq=%d ld=%d\n", op, seq, len data);
26637da2899SCharles.Forsyth	return 0;
26737da2899SCharles.Forsyth}
26837da2899SCharles.Forsyth
26937da2899SCharles.Forsythflush(input: chan of ref Icmp)
27037da2899SCharles.Forsyth{
27137da2899SCharles.Forsyth	for(;;)alt{
27237da2899SCharles.Forsyth	<-input =>
27337da2899SCharles.Forsyth		;
27437da2899SCharles.Forsyth	* =>
27537da2899SCharles.Forsyth		return;
27637da2899SCharles.Forsyth	}
27737da2899SCharles.Forsyth}
27837da2899SCharles.Forsyth
27937da2899SCharles.Forsythrecv(input: chan of ref Icmp, msec: int): (int, int, array of byte)
28037da2899SCharles.Forsyth{
28137da2899SCharles.Forsyth	t := Timer.start(msec);
28237da2899SCharles.Forsyth	alt{
28337da2899SCharles.Forsyth	<-t.timeout =>
28437da2899SCharles.Forsyth		return (Tftp_Last, 0, nil);
28537da2899SCharles.Forsyth	ic := <-input =>
28637da2899SCharles.Forsyth		t.stop();
28737da2899SCharles.Forsyth		if(ic.ptype == InfoReply)
28837da2899SCharles.Forsyth			return (ic.code, ic.seq, ic.data);
28937da2899SCharles.Forsyth		return (Tftp_Last, 0, nil);
29037da2899SCharles.Forsyth	}
29137da2899SCharles.Forsyth}
29237da2899SCharles.Forsyth
29337da2899SCharles.Forsythreader(fd: ref Sys->FD, out: chan of ref Icmp, pid: chan of int)
29437da2899SCharles.Forsyth{
29537da2899SCharles.Forsyth	pid <-= sys->pctl(0, nil);
29637da2899SCharles.Forsyth	for(;;){
29737da2899SCharles.Forsyth		buf := array[64*1024+512] of byte;
29837da2899SCharles.Forsyth		n := sys->read(fd, buf, len buf);
29937da2899SCharles.Forsyth		if(n <= 0){
30037da2899SCharles.Forsyth			if(n == 0)
30137da2899SCharles.Forsyth				sys->werrstr("unexpected eof");
30237da2899SCharles.Forsyth			break;
30337da2899SCharles.Forsyth		}
30437da2899SCharles.Forsyth		ic := Icmp.unpack(buf[0:n]);
30537da2899SCharles.Forsyth		if(ic != nil){
30637da2899SCharles.Forsyth			if(debug)
30737da2899SCharles.Forsyth				sys->print("recv type=%d op=%d seq=%d id=%d\n", ic.ptype, ic.code, ic.seq, ic.id);
30837da2899SCharles.Forsyth			out <-= ic;
30937da2899SCharles.Forsyth		}else
31037da2899SCharles.Forsyth			sys->fprint(sys->fildes(2), "mangaload: corrupt icmp packet rcvd\n");
31137da2899SCharles.Forsyth	}
31237da2899SCharles.Forsyth	sys->print("read: %r\n");
31337da2899SCharles.Forsyth	out <-= nil;
31437da2899SCharles.Forsyth}
31537da2899SCharles.Forsyth
31637da2899SCharles.Forsyth# IP and ICMP packet header
31737da2899SCharles.ForsythOvihl: con 0;
31837da2899SCharles.ForsythOtos: con 1;
31937da2899SCharles.ForsythOlength: con 2;
32037da2899SCharles.ForsythOid: con Olength+2;
32137da2899SCharles.ForsythOfrag: con Oid+2;
32237da2899SCharles.ForsythOttl: con Ofrag+2;
32337da2899SCharles.ForsythOproto: con Ottl+1;
32437da2899SCharles.ForsythOipcksum: con Oproto+1;
32537da2899SCharles.ForsythOsrc: con Oipcksum+2;
32637da2899SCharles.ForsythOdst: con Osrc+4;
32737da2899SCharles.ForsythOtype: con Odst+4;
32837da2899SCharles.ForsythOcode: con Otype+1;
32937da2899SCharles.ForsythOcksum: con Ocode+1;
33037da2899SCharles.ForsythOicmpid: con Ocksum+2;
33137da2899SCharles.ForsythOseq: con Oicmpid+2;
33237da2899SCharles.ForsythOdata: con Oseq+2;
33337da2899SCharles.Forsyth
33437da2899SCharles.ForsythIcmp.unpack(b: array of byte): ref Icmp
33537da2899SCharles.Forsyth{
33637da2899SCharles.Forsyth	if(len b < Odata)
33737da2899SCharles.Forsyth		return nil;
33837da2899SCharles.Forsyth	ic := ref Icmp;
33937da2899SCharles.Forsyth	ic.ttl = int b[Ottl];
34037da2899SCharles.Forsyth	ic.src = IPaddr.newv4(b[Osrc:]);
34137da2899SCharles.Forsyth	ic.dst = IPaddr.newv4(b[Odst:]);
34237da2899SCharles.Forsyth	ic.ptype = int b[Otype];
34337da2899SCharles.Forsyth	ic.code = int b[Ocode];
34437da2899SCharles.Forsyth	ic.seq = ip->get2(b, Oseq);
34537da2899SCharles.Forsyth	ic.id = ip->get2(b, Oicmpid);
34637da2899SCharles.Forsyth	ic.munged = 0;
34737da2899SCharles.Forsyth	if(len b > Odata)
34837da2899SCharles.Forsyth		ic.data = b[Odata:];
34937da2899SCharles.Forsyth	return ic;
35037da2899SCharles.Forsyth}
351