1implement Mangaload; 2 3# to do: 4# - set arp entry based on /lib/ndb if necessary 5 6include "sys.m"; 7 sys: Sys; 8 9include "draw.m"; 10 11include "ip.m"; 12 ip: IP; 13 IPaddr: import ip; 14 15include "timers.m"; 16 timers: Timers; 17 Timer: import timers; 18 19include "dial.m"; 20 dial: Dial; 21 22include "arg.m"; 23 24Mangaload: module 25{ 26 init: fn(nil: ref Draw->Context, nil: list of string); 27}; 28 29# manga parameters 30FlashBlocksize: con 16r10000; 31FlashSize: con 16r400000; # 4meg for now 32FlashUserArea: con 16r3C0000; 33 34# magic values 35FooterOffset: con 16rFFEC; 36FooterSig: con 16rA0FFFF9F; # ARM flash library 37FileInfosize: con 64; 38FileNamesize: con FileInfosize - 3*4; # x, y, z 39Packetdatasize: con 1500-28; # ether data less IP + ICMP header 40RequestTimeout: con 500; 41Probecount: con 10; # query unit every so many packets 42 43# manga uses extended TFTP ops in ICMP InfoRequest packets 44Tftp_Req: con 0; 45Tftp_Read: con 1; 46Tftp_Write: con 2; 47Tftp_Data: con 3; 48Tftp_Ack: con 4; 49Tftp_Error: con 5; 50Tftp_Last: con 6; 51 52Icmp: 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 68EchoReply: con 0; 69Unreachable: con 3; 70SrcQuench: con 4; 71EchoRequest: con 8; 72TimeExceed: con 11; 73Timestamp: con 13; 74TimestampReply: con 14; 75InfoRequest: con 15; 76InfoReply: con 16; 77 78Nmsg: con 32; 79Interval: con 1000; # ms 80 81debug := 0; 82flashblock := 1; # never 0, that's the boot firmware 83maxfilesize := 8*FlashBlocksize; 84flashlim := FlashSize/FlashBlocksize; 85loadinitrd := 0; 86maxlen := 512*1024; 87mypid := 0; 88Datablocksize: con 4096; 89 90init(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 243kill(pid: int) 244{ 245 if(pid) 246 sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill"); 247} 248 249senderr() 250{ 251 sys->fprint(sys->fildes(2), "mangaload: icmp write failed: %r\n"); 252 raise "disaster"; 253} 254 255send(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 269flush(input: chan of ref Icmp) 270{ 271 for(;;)alt{ 272 <-input => 273 ; 274 * => 275 return; 276 } 277} 278 279recv(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 293reader(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 317Ovihl: con 0; 318Otos: con 1; 319Olength: con 2; 320Oid: con Olength+2; 321Ofrag: con Oid+2; 322Ottl: con Ofrag+2; 323Oproto: con Ottl+1; 324Oipcksum: con Oproto+1; 325Osrc: con Oipcksum+2; 326Odst: con Osrc+4; 327Otype: con Odst+4; 328Ocode: con Otype+1; 329Ocksum: con Ocode+1; 330Oicmpid: con Ocksum+2; 331Oseq: con Oicmpid+2; 332Odata: con Oseq+2; 333 334Icmp.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