]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/alphapc/bootp.c
fltfmt: %.0g should print with one significant figure
[plan9front.git] / sys / src / boot / alphapc / bootp.c
1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6
7 #include "ip.h"
8
9 uchar broadcast[Eaddrlen] = {
10         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
11 };
12
13 static ushort tftpport = 5000;
14 static int Id = 1;
15 static Netaddr myaddr;
16 static Netaddr server;
17
18 typedef struct {
19         uchar   header[4];
20         uchar   data[Segsize];
21 } Tftp;
22 static Tftp tftpb;
23
24 int
25 etherrxpkt(int ctlrno, Etherpkt *pkt, int timo)
26 {
27         int n;
28
29         for (;;) {
30                 n = devread(ctlrno, (uchar*)pkt, sizeof(*pkt), 0);
31                 if (n >= 0)
32                         return n;
33                 if (timo-- < 0)
34                         return -1;
35         }
36 }
37
38 int
39 ethertxpkt(int ctlrno, Etherpkt *pkt, int len, int timo)
40 {
41         USED(timo);
42         return devwrite(ctlrno, (uchar*)pkt, len, 0);
43 }
44
45 static void
46 hnputs(uchar *ptr, ushort val)
47 {
48         ptr[0] = val>>8;
49         ptr[1] = val;
50 }
51
52 static void
53 hnputl(uchar *ptr, ulong val)
54 {
55         ptr[0] = val>>24;
56         ptr[1] = val>>16;
57         ptr[2] = val>>8;
58         ptr[3] = val;
59 }
60
61 static ulong
62 nhgetl(uchar *ptr)
63 {
64         return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
65 }
66
67 static ushort
68 nhgets(uchar *ptr)
69 {
70         return ((ptr[0]<<8) | ptr[1]);
71 }
72
73 static  short   endian  = 1;
74 static  char*   aendian = (char*)&endian;
75 #define LITTLE  *aendian
76
77 static ushort
78 ptcl_csum(void *a, int len)
79 {
80         uchar *addr;
81         ulong t1, t2;
82         ulong losum, hisum, mdsum, x;
83
84         addr = a;
85         losum = 0;
86         hisum = 0;
87         mdsum = 0;
88
89         x = 0;
90         if((ulong)addr & 1) {
91                 if(len) {
92                         hisum += addr[0];
93                         len--;
94                         addr++;
95                 }
96                 x = 1;
97         }
98         while(len >= 16) {
99                 t1 = *(ushort*)(addr+0);
100                 t2 = *(ushort*)(addr+2);        mdsum += t1;
101                 t1 = *(ushort*)(addr+4);        mdsum += t2;
102                 t2 = *(ushort*)(addr+6);        mdsum += t1;
103                 t1 = *(ushort*)(addr+8);        mdsum += t2;
104                 t2 = *(ushort*)(addr+10);       mdsum += t1;
105                 t1 = *(ushort*)(addr+12);       mdsum += t2;
106                 t2 = *(ushort*)(addr+14);       mdsum += t1;
107                 mdsum += t2;
108                 len -= 16;
109                 addr += 16;
110         }
111         while(len >= 2) {
112                 mdsum += *(ushort*)addr;
113                 len -= 2;
114                 addr += 2;
115         }
116         if(x) {
117                 if(len)
118                         losum += addr[0];
119                 if(LITTLE)
120                         losum += mdsum;
121                 else
122                         hisum += mdsum;
123         } else {
124                 if(len)
125                         hisum += addr[0];
126                 if(LITTLE)
127                         hisum += mdsum;
128                 else
129                         losum += mdsum;
130         }
131
132         losum += hisum >> 8;
133         losum += (hisum & 0xff) << 8;
134         while(hisum = losum>>16)
135                 losum = hisum + (losum & 0xffff);
136
137         return ~losum;
138 }
139
140 static ushort
141 ip_csum(uchar *addr)
142 {
143         int len;
144         ulong sum = 0;
145
146         len = (addr[0]&0xf)<<2;
147
148         while(len > 0) {
149                 sum += addr[0]<<8 | addr[1] ;
150                 len -= 2;
151                 addr += 2;
152         }
153
154         sum = (sum & 0xffff) + (sum >> 16);
155         sum = (sum & 0xffff) + (sum >> 16);
156         return (sum^0xffff);
157 }
158
159 static void
160 udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
161 {
162         Udphdr *uh;
163         Etherhdr *ip;
164         Etherpkt pkt;
165         int len, ptcllen;
166
167
168         uh = (Udphdr*)&pkt;
169
170         memset(uh, 0, sizeof(Etherpkt));
171         memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
172
173         /*
174          * UDP portion
175          */
176         ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
177         uh->ttl = 0;
178         uh->udpproto = IP_UDPPROTO;
179         uh->frag[0] = 0;
180         uh->frag[1] = 0;
181         hnputs(uh->udpplen, ptcllen);
182         hnputl(uh->udpsrc, myaddr.ip);
183         hnputs(uh->udpsport, myaddr.port);
184         hnputl(uh->udpdst, a->ip);
185         hnputs(uh->udpdport, a->port);
186         hnputs(uh->udplen, ptcllen);
187         uh->udpcksum[0] = 0;
188         uh->udpcksum[1] = 0;
189         dlen = (dlen+1)&~1;
190         hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
191
192         /*
193          * IP portion
194          */
195         ip = (Etherhdr*)&pkt;
196         len = UDP_EHSIZE+UDP_HDRSIZE+dlen;              /* non-descriptive names */
197         ip->vihl = IP_VER|IP_HLEN;
198         ip->tos = 0;
199         ip->ttl = 255;
200         hnputs(ip->length, len-ETHER_HDR);
201         hnputs(ip->id, Id++);
202         ip->frag[0] = 0;
203         ip->frag[1] = 0;
204         ip->cksum[0] = 0;
205         ip->cksum[1] = 0;
206         hnputs(ip->cksum, ip_csum(&ip->vihl));
207
208         /*
209          * Ethernet MAC portion
210          */
211         hnputs(ip->type, ET_IP);
212         memmove(ip->d, a->ea, sizeof(ip->d));
213
214         ethertxpkt(ctlrno, &pkt, len, Timeout);
215 }
216
217 static void
218 nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
219 {
220         int n;
221         char buf[128];
222
223         buf[0] = 0;
224         buf[1] = Tftp_ERROR;
225         buf[2] = 0;
226         buf[3] = code;
227         strcpy(buf+4, msg);
228         n = strlen(msg) + 4 + 1;
229         udpsend(ctlrno, a, buf, n);
230         if(report)
231                 print("\ntftp: error(%d): %s\n", code, msg);
232 }
233
234 static int
235 udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
236 {
237         int n, len;
238         ushort csm;
239         Udphdr *h;
240         ulong addr, timo;
241         Etherpkt pkt;
242         static int rxactive;
243
244         if(rxactive == 0)
245                 timo = 1000;
246         else
247                 timo = Timeout;
248         timo += msec();
249         while(timo > msec()){
250                 n = etherrxpkt(ctlrno, &pkt, timo-msec());
251                 if(n <= 0)
252                         continue;
253
254                 h = (Udphdr*)&pkt;
255                 if(nhgets(h->type) != ET_IP)
256                         continue;
257
258                 if(ip_csum(&h->vihl)) {
259                         print("ip chksum error\n");
260                         continue;
261                 }
262                 if(h->vihl != (IP_VER|IP_HLEN)) {
263                         print("ip bad vers/hlen\n");
264                         continue;
265                 }
266
267                 if(h->udpproto != IP_UDPPROTO)
268                         continue;
269
270                 h->ttl = 0;
271                 len = nhgets(h->udplen);
272                 hnputs(h->udpplen, len);
273
274                 if(nhgets(h->udpcksum)) {
275                         csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
276                         if(csm != 0) {
277                                 print("udp chksum error csum #%4lux len %d\n", csm, n);
278                                 break;
279                         }
280                 }
281
282                 if(a->port != 0 && nhgets(h->udpsport) != a->port)
283                         continue;
284                 if(myaddr.port != 0 && nhgets(h->udpdport) != myaddr.port)
285                         continue;
286
287                 addr = nhgetl(h->udpsrc);
288                 if(a->ip != Bcastip && addr != a->ip)
289                         continue;
290
291                 len -= UDP_HDRSIZE-UDP_PHDRSIZE;
292                 if(len > dlen) {
293                         print("udp: packet too big\n");
294                         continue;
295                 }
296
297                 memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
298                 a->ip = addr;
299                 a->port = nhgets(h->udpsport);
300                 memmove(a->ea, pkt.s, sizeof(a->ea));
301
302                 rxactive = 1;
303                 return len;
304         }
305
306         return 0;
307 }
308
309 static int tftpblockno;
310
311 static int
312 tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
313 {
314         int i, len, rlen;
315         char buf[Segsize+2];
316
317         buf[0] = 0;
318         buf[1] = Tftp_READ;
319         len = sprint(buf+2, "%s", name) + 2;
320         len += sprint(buf+len+1, "octet") + 2;
321
322         for(i = 0; i < 5; i++){
323                 udpsend(ctlrno, a, buf, len);
324                 a->port = 0;
325                 if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
326                         continue;
327
328                 switch((tftp->header[0]<<8)|tftp->header[1]){
329
330                 case Tftp_ERROR:
331                         print("tftpopen: error (%d): %s\n",
332                                 (tftp->header[2]<<8)|tftp->header[3], tftp->data);
333                         return -1;
334
335                 case Tftp_DATA:
336                         tftpblockno = 1;
337                         len = (tftp->header[2]<<8)|tftp->header[3];
338                         if(len != tftpblockno){
339                                 print("tftpopen: block error: %d\n", len);
340                                 nak(ctlrno, a, 1, "block error", 0);
341                                 return -1;
342                         }
343                         return rlen-sizeof(tftp->header);
344                 }
345         }
346
347         print("tftpopen: failed to connect to server\n");
348         return -1;
349 }
350
351 static int
352 tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
353 {
354         int blockno, len;
355         uchar buf[4];
356
357         buf[0] = 0;
358         buf[1] = Tftp_ACK;
359         buf[2] = tftpblockno>>8;
360         buf[3] = tftpblockno;
361         tftpblockno++;
362
363         dlen += sizeof(tftp->header);
364
365 buggery:
366         udpsend(ctlrno, a, buf, sizeof(buf));
367
368         if((len = udprecv(ctlrno, a, tftp, dlen)) != dlen){
369                 print("tftpread: %d != %d\n", len, dlen);
370                 nak(ctlrno, a, 2, "short read", 0);
371         }
372
373         blockno = (tftp->header[2]<<8)|tftp->header[3];
374         if(blockno != tftpblockno){
375                 print("tftpread: block error: %d, expected %d\n", blockno, tftpblockno);
376
377                 if(blockno == tftpblockno-1)
378                         goto buggery;
379                 nak(ctlrno, a, 1, "block error", 0);
380
381                 return -1;
382         }
383
384         return len-sizeof(tftp->header);
385 }
386
387 // #define      BOOT_MAGIC      L_MAGIC
388 #define BOOT_MAGIC      0x0700e0c3
389
390 void
391 getether(char *dev, uchar *ea)
392 {
393         int i;
394         char *p;
395
396         p = dev;
397         for (i = 0; i < 8; i++) {
398                 p = strchr(p, ' ');
399                 if (p == 0)
400                         panic("no ether addr");
401                 p++;
402         }
403         for (i = 0; i < 6; i++) {
404                 ea[i] = strtoul(p, &p, 16);
405                 if (*p != (i == 5 ? ' ' : '-'))
406                         panic("bad ether addr");
407                 p++;
408         }
409 }
410
411 static char inibuf[BOOTARGSLEN];
412
413 int
414 bootp(char *dev)
415 {
416         Bootp req, rep;
417         int i, fd, dlen, segsize, text, data, bss, total;
418         uchar *addr, *p, ea[6];
419         char *cp;
420         ulong entry;
421         Exec *exec;
422         char *filename, confname[32];
423
424         getether(dev, ea);
425         fd = devopen(dev);
426         if (fd < 0)
427                 panic("bootp devopen");
428
429         memset(&req, 0, sizeof(req));
430         req.op = Bootrequest;
431         req.htype = 1;                  /* ethernet */
432         req.hlen = Eaddrlen;            /* ethernet */
433         memmove(req.chaddr, ea, Eaddrlen);
434
435         myaddr.ip = 0;
436         myaddr.port = BPportsrc;
437         memmove(myaddr.ea, ea, Eaddrlen);
438
439         for(i = 0; i < 10; i++) {
440                 server.ip = Bcastip;
441                 server.port = BPportdst;
442                 memmove(server.ea, broadcast, sizeof(server.ea));
443                 udpsend(fd, &server, &req, sizeof(req));
444                 if(udprecv(fd, &server, &rep, sizeof(rep)) <= 0)
445                         continue;
446                 if(memcmp(req.chaddr, rep.chaddr, Eaddrlen))
447                         continue;
448                 if(rep.htype != 1 || rep.hlen != Eaddrlen)
449                         continue;
450                 break;
451         }
452         if(i >= 10) {
453                 print("bootp timed out\n");
454                 return -1;
455         }
456
457         sprint(confname, "/alpha/conf/%d.%d.%d.%d",
458                 rep.yiaddr[0],
459                 rep.yiaddr[1],
460                 rep.yiaddr[2],
461                 rep.yiaddr[3]);
462
463         if(rep.sname[0] != '\0')
464                  print("%s ", rep.sname);
465         print("(%d.%d.%d.%d!%d): %s...",
466                 rep.siaddr[0],
467                 rep.siaddr[1],
468                 rep.siaddr[2],
469                 rep.siaddr[3],
470                 server.port,
471                 confname);
472
473         myaddr.ip = nhgetl(rep.yiaddr);
474         myaddr.port = tftpport++;
475         server.ip = nhgetl(rep.siaddr);
476         server.port = TFTPport;
477
478         if((dlen = tftpopen(fd, &server, confname, &tftpb)) < 0)
479                 return -1;
480         cp = inibuf;
481         while(dlen > 0) {
482                 if(cp-inibuf+dlen > BOOTARGSLEN)
483                         panic("conf too large");
484                 memmove(cp, tftpb.data, dlen);
485                 cp += dlen;
486                 if(dlen != Segsize)
487                         break;
488                 if((dlen = tftpread(fd, &server, &tftpb, sizeof(tftpb.data))) < 0)
489                         return -1;
490         }
491         *cp = 0;
492         setconf(inibuf);
493
494         filename = "/alpha/9apc";
495         cp = getconf("bootfile");
496         if(cp != nil)
497                 filename = cp;
498
499         print("%s\n", filename);
500         myaddr.port = tftpport++;
501         server.port = TFTPport;
502         if((dlen = tftpopen(fd, &server, filename, &tftpb)) < 0)
503                 return -1;
504
505         exec = (Exec*)(tftpb.data);
506         if(dlen < sizeof(Exec) || GLLONG(exec->magic) != BOOT_MAGIC){
507                 nak(fd, &server, 0, "bad magic number", 1);
508                 return -1;
509         }
510         text = GLLONG(exec->text);
511         data = GLLONG(exec->data);
512         bss = GLLONG(exec->bss);
513         total = text+data+bss;
514         entry = GLLONG(exec->entry);
515         if (!validrgn(entry, entry+total))
516                 panic("memory range not available: %lux-%lux\n", entry, entry+total);
517         print("%d", text);
518
519         addr = (uchar*)entry;
520         p = tftpb.data+sizeof(Exec);
521         dlen -= sizeof(Exec);
522         segsize = text;
523         for(;;){
524                 if(dlen == 0){
525                         if((dlen = tftpread(fd, &server, &tftpb, sizeof(tftpb.data))) < 0)
526                                 return -1;
527                         p = tftpb.data;
528                 }
529                 if(segsize <= dlen)
530                         i = segsize;
531                 else
532                         i = dlen;
533                 memmove(addr, p, i);
534
535                 addr += i;
536                 p += i;
537                 segsize -= i;
538                 dlen -= i;
539
540                 if(segsize <= 0){
541                         if(data == 0)
542                                 break;
543                         print("+%d", data);
544                         segsize = data;
545                         data = 0;
546 //                      addr = (uchar*)pground((uvlong)addr);
547                 }
548         }
549         nak(fd, &server, 3, "ok", 0);           /* tftpclose */
550         print("+%d=%d\n", bss, total);
551         print("entry: 0x%lux\n", entry);
552
553         kexec(entry);
554
555         return 0;
556 }