xref: /inferno-os/appl/cmd/auxi/mangaload.b (revision cd17ce433410c01516d7ba8f052a6c5b67b0e2d5)
1 implement Mangaload;
2 
3 # to do:
4 #	- set arp entry based on /lib/ndb if necessary
5 
6 include "sys.m";
7 	sys: Sys;
8 
9 include "draw.m";
10 
11 include "ip.m";
12 	ip: IP;
13 	IPaddr: import ip;
14 
15 include "timers.m";
16 	timers: Timers;
17 	Timer: import timers;
18 
19 include "dial.m";
20 	dial: Dial;
21 
22 include "arg.m";
23 
24 Mangaload: module
25 {
26 	init:	fn(nil: ref Draw->Context, nil: list of string);
27 };
28 
29 # manga parameters
30 FlashBlocksize: con 16r10000;
31 FlashSize: con 16r400000;	# 4meg for now
32 FlashUserArea: con 16r3C0000;
33 
34 # magic values
35 FooterOffset: con 16rFFEC;
36 FooterSig: con 16rA0FFFF9F;	# ARM flash library
37 FileInfosize: con 64;
38 FileNamesize: con FileInfosize - 3*4;	# x, y, z
39 Packetdatasize: con 1500-28;	# ether data less IP + ICMP header
40 RequestTimeout: con 500;
41 Probecount: con 10;	# query unit every so many packets
42 
43 # manga uses extended TFTP ops in ICMP InfoRequest packets
44 Tftp_Req: con 0;
45 Tftp_Read: con 1;
46 Tftp_Write: con 2;
47 Tftp_Data: con 3;
48 Tftp_Ack: con 4;
49 Tftp_Error: con 5;
50 Tftp_Last: con 6;
51 
52 Icmp: adt
53 {
54 	ttl:	int;	# time to live
55 	src:	IPaddr;
56 	dst:	IPaddr;
57 	ptype:	int;
58 	code:	int;
59 	id:	int;
60 	seq:	int;
61 	data:	array of byte;
62 	munged:	int;	# packet received but corrupt
63 
64 	unpack:	fn(b: array of byte): ref Icmp;
65 };
66 
67 # ICMP packet types
68 EchoReply: con 0;
69 Unreachable: con 3;
70 SrcQuench: con 4;
71 EchoRequest: con 8;
72 TimeExceed: con 11;
73 Timestamp: con 13;
74 TimestampReply: con 14;
75 InfoRequest: con 15;
76 InfoReply: con 16;
77 
78 Nmsg: con 32;
79 Interval: con 1000;	# ms
80 
81 debug := 0;
82 flashblock := 1;	# never 0, that's the boot firmware
83 maxfilesize := 8*FlashBlocksize;
84 flashlim := FlashSize/FlashBlocksize;
85 loadinitrd := 0;
86 maxlen := 512*1024;
87 mypid := 0;
88 Datablocksize: con 4096;
89 
90 init(nil: ref Draw->Context, args: list of string)
91 {
92 	sys = load Sys Sys->PATH;
93 	timers = load Timers Timers->PATH;
94 	dial = load Dial Dial->PATH;
95 	ip = load IP IP->PATH;
96 	ip->init();
97 
98 
99 	arg := load Arg Arg->PATH;
100 	arg->init(args);
101 	arg->setusage("mangaload [-48dr] destination file");
102 	while((o := arg->opt()) != 0)
103 		case o {
104 		'4' =>
105 			flashlim = 4*1024*1024/FlashBlocksize;
106 		'8' =>
107 			flashlim = 8*1024*1024/FlashBlocksize;
108 		'r' =>
109 			loadinitrd = 1;
110 			flashblock = 9;
111 			if(flashlim > 4*1024*1024/FlashBlocksize)
112 				maxfilesize = 113*FlashBlocksize;
113 			else
114 				maxfilesize = 50*FlashBlocksize;
115 		'd' =>
116 			debug++;
117 		}
118 	args = arg->argv();
119 	if(len args != 2)
120 		arg->usage();
121 	arg = nil;
122 
123 	sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil);
124 
125 	filename := hd tl args;
126 	fd := sys->open(filename, Sys->OREAD);
127 	if(fd == nil){
128 		sys->fprint(sys->fildes(2), "mangaload: can't open %s: %r\n", filename);
129 		raise "fail:open";
130 	}
131 	(ok, d) := sys->fstat(fd);
132 	if(ok < 0){
133 		sys->fprint(sys->fildes(2), "mangaload: can't stat %s: %r\n", filename);
134 		raise "fail:stat";
135 	}
136 	if(d.length > big maxfilesize){
137 		sys->fprint(sys->fildes(2), "mangaload: file %s too long (must not exceed %d bytes)\n",
138 			filename, maxfilesize);
139 		raise "fail:size";
140 	}
141 	filesize := int d.length;
142 
143 	port := sys->sprint("%d", 16r8695);
144 	addr := dial->netmkaddr(hd args, "icmp", port);
145 	c := dial->dial(addr, port);
146 	if(c == nil){
147 		sys->fprint(sys->fildes(2), "mangaload: can't dial %s: %r\n", addr);
148 		raise "fail:dial";
149 	}
150 
151 	tpid := timers->init(20);
152 
153 	pids := chan of int;
154 	replies := chan [2] of ref Icmp;
155 	spawn reader(c.dfd, replies, pids);
156 	rpid := <-pids;
157 
158 	flashoffset := flashblock * FlashBlocksize;
159 
160 	# file name first
161 	bname := array of byte filename;
162 	l := len bname;
163 	buf := array[Packetdatasize] of byte;
164 	ip->put4(buf, 0, filesize);
165 	ip->put4(buf, 4, l);
166 	buf[8:] = bname;
167 	l += 2*4;
168 	buf[l++] = byte 0;
169 	ip->put4(buf, l, flashoffset);
170 	l += 4;
171 	{
172 		if(send(c.dfd, buf[0:l], Tftp_Write, 0) < 0)
173 			senderr();
174 		(op, iseq, data) := recv(replies, 400);
175 		sys->print("initial reply: %d %d\n", op, iseq);
176 		if(op != Tftp_Ack){
177 			why := "no response";
178 			if(op == Tftp_Error)
179 				why = "manga cannot receive file";
180 			sys->fprint(sys->fildes(2), "mangaload: %s\n", why);
181 			raise "fail:error";
182 		}
183 		sys->print("sending %s size %d at address %d (0x%x)\n", filename, filesize, flashoffset, flashoffset);
184 		seq := 1;
185 		nsent := 0;
186 		last := 0;
187 		while((n := sys->read(fd, buf, len buf)) >= 0 && !last){
188 			last = n != len buf;
189 		  Retry:
190 			for(;;){
191 				if(++nsent%10 == 0){	# probe
192 					o = Tftp_Req;
193 					send(c.dfd, array[0] of byte, Tftp_Req, seq);
194 					(op, iseq, data) = recv(replies, 500);
195 					if(debug || op != Tftp_Ack)
196 						sys->print("ack reply: %d %d\n", op, iseq);
197 					if(op == Tftp_Last || op == Tftp_Error){
198 						if(op == Tftp_Last)
199 							sys->print("timed out\n");
200 						else
201 							sys->print("error reply\n");
202 						raise "disaster";
203 					}
204 					if(debug)
205 						sys->print("ok\n");
206 					continue Retry;
207 				}
208 				send(c.dfd, buf[0:n], Tftp_Data, seq);
209 				(op, iseq, data) = recv(replies, 40);
210 				case op {
211 				Tftp_Error =>
212 					sys->fprint(sys->fildes(2), "mangaload: manga refused data\n");
213 					raise "disaster";
214 				Tftp_Ack =>
215 					if(seq == iseq){
216 						seq++;
217 						break Retry;
218 					}
219 					sys->print("sequence error: rcvd %d expected %d\n", iseq, seq);
220 					if(iseq > seq){
221 						sys->print("unrecoverable sequence error\n");
222 						send(c.dfd, array[0] of byte, Tftp_Data, ++seq);	# stop manga
223 						raise "disaster";
224 					}
225 					# resend
226 					sys->seek(fd, -big ((seq-iseq)*len buf), 1);
227 					seq = iseq;
228 				Tftp_Last =>
229 					seq++;
230 					break Retry;	# timeout ok: manga doesn't usually reply unless packet lost
231 				}
232 			}
233 		}
234 	}exception{
235 	* =>
236 		;
237 	}
238 	kill(rpid);
239 	kill(tpid);
240 	sys->print("ok?\n");
241 }
242 
243 kill(pid: int)
244 {
245 	if(pid)
246 		sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
247 }
248 
249 senderr()
250 {
251 	sys->fprint(sys->fildes(2), "mangaload: icmp write failed: %r\n");
252 	raise "disaster";
253 }
254 
255 send(fd: ref Sys->FD, data: array of byte, op: int, seq: int): int
256 {
257 	buf := array[64*1024+512] of {* => byte 0};
258 	buf[Odata:] = data;
259 	ip->put2(buf, Oseq, seq);
260 	buf[Otype] = byte InfoRequest;
261 	buf[Ocode] = byte op;
262 	if(sys->write(fd, buf, Odata+len data) < Odata+len data)
263 		return -1;
264 	if(debug)
265 		sys->print("sent op=%d seq=%d ld=%d\n", op, seq, len data);
266 	return 0;
267 }
268 
269 flush(input: chan of ref Icmp)
270 {
271 	for(;;)alt{
272 	<-input =>
273 		;
274 	* =>
275 		return;
276 	}
277 }
278 
279 recv(input: chan of ref Icmp, msec: int): (int, int, array of byte)
280 {
281 	t := Timer.start(msec);
282 	alt{
283 	<-t.timeout =>
284 		return (Tftp_Last, 0, nil);
285 	ic := <-input =>
286 		t.stop();
287 		if(ic.ptype == InfoReply)
288 			return (ic.code, ic.seq, ic.data);
289 		return (Tftp_Last, 0, nil);
290 	}
291 }
292 
293 reader(fd: ref Sys->FD, out: chan of ref Icmp, pid: chan of int)
294 {
295 	pid <-= sys->pctl(0, nil);
296 	for(;;){
297 		buf := array[64*1024+512] of byte;
298 		n := sys->read(fd, buf, len buf);
299 		if(n <= 0){
300 			if(n == 0)
301 				sys->werrstr("unexpected eof");
302 			break;
303 		}
304 		ic := Icmp.unpack(buf[0:n]);
305 		if(ic != nil){
306 			if(debug)
307 				sys->print("recv type=%d op=%d seq=%d id=%d\n", ic.ptype, ic.code, ic.seq, ic.id);
308 			out <-= ic;
309 		}else
310 			sys->fprint(sys->fildes(2), "mangaload: corrupt icmp packet rcvd\n");
311 	}
312 	sys->print("read: %r\n");
313 	out <-= nil;
314 }
315 
316 # IP and ICMP packet header
317 Ovihl: con 0;
318 Otos: con 1;
319 Olength: con 2;
320 Oid: con Olength+2;
321 Ofrag: con Oid+2;
322 Ottl: con Ofrag+2;
323 Oproto: con Ottl+1;
324 Oipcksum: con Oproto+1;
325 Osrc: con Oipcksum+2;
326 Odst: con Osrc+4;
327 Otype: con Odst+4;
328 Ocode: con Otype+1;
329 Ocksum: con Ocode+1;
330 Oicmpid: con Ocksum+2;
331 Oseq: con Oicmpid+2;
332 Odata: con Oseq+2;
333 
334 Icmp.unpack(b: array of byte): ref Icmp
335 {
336 	if(len b < Odata)
337 		return nil;
338 	ic := ref Icmp;
339 	ic.ttl = int b[Ottl];
340 	ic.src = IPaddr.newv4(b[Osrc:]);
341 	ic.dst = IPaddr.newv4(b[Odst:]);
342 	ic.ptype = int b[Otype];
343 	ic.code = int b[Ocode];
344 	ic.seq = ip->get2(b, Oseq);
345 	ic.id = ip->get2(b, Oicmpid);
346 	ic.munged = 0;
347 	if(len b > Odata)
348 		ic.data = b[Odata:];
349 	return ic;
350 }
351