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