]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/pxe.c
merge
[plan9front.git] / sys / src / boot / pc / pxe.c
1 #include <u.h>
2 #include "fns.h"
3
4 enum {
5         Tftp_READ       = 1,
6         Tftp_WRITE      = 2,
7         Tftp_DATA       = 3,
8         Tftp_ACK        = 4,
9         Tftp_ERROR      = 5,
10         Tftp_OACK       = 6,
11
12         TftpPort        = 69,
13
14         Segsize         = 512,
15         Maxpath         = 64,
16 };
17
18 typedef uchar IP4[4];
19
20 typedef struct Tftp Tftp;
21 typedef struct Dhcp Dhcp;
22
23 struct Tftp
24 {
25         IP4 sip;
26         IP4 dip;
27         IP4 gip;
28
29         int sport;
30         int dport;
31
32         char *rp;
33         char *ep;
34
35         int seq;
36         int eof;
37         
38         char pkt[2+2+Segsize];
39         char nul;
40 };
41
42 struct Dhcp
43 {
44         uchar opcode;
45         uchar hardware;
46         uchar hardlen;
47         uchar gatehops;
48         uchar ident[4];
49         uchar seconds[2];
50         uchar flags[2];
51         uchar cip[4];
52         uchar yip[4];
53         uchar sip[4];
54         uchar gip[4];
55         uchar mac[16];
56         char sname[64];
57         char bootfile[128];
58 };
59
60 int pxeinit(void);
61 int pxecall(int op, void *buf);
62
63 static void*
64 unfar(ulong seg, ulong off)
65 {
66         return (void*)((off & 0xFFFF) + (seg & 0xFFFF)*16);
67 }
68
69 static void
70 puts(void *x, ushort v)
71 {
72         uchar *p = x;
73
74         p[1] = (v>>8) & 0xFF;
75         p[0] = v & 0xFF;
76 }
77
78 static ushort
79 gets(void *x)
80 {
81         uchar *p = x;
82
83         return p[1]<<8 | p[0];
84 }
85
86 static void
87 hnputs(void *x, ushort v)
88 {
89         uchar *p = x;
90
91         p[0] = (v>>8) & 0xFF;
92         p[1] = v & 0xFF;
93 }
94
95 static ushort
96 nhgets(void *x)
97 {
98         uchar *p = x;
99
100         return p[0]<<8 | p[1];
101 }
102
103 static void
104 moveip(IP4 d, IP4 s)
105 {
106         memmove(d, s, sizeof(d));
107 }
108
109 void
110 unload(void)
111 {
112         struct {
113                 uchar status[2];
114                 uchar junk[10];
115         } buf;
116         static uchar shutdown[] = { 0x05, 0x070, 0x02, 0 };
117         uchar *o;
118
119         for(o = shutdown; *o; o++){ 
120                 memset(&buf, 0, sizeof(buf));
121                 if(pxecall(*o, &buf))
122                         break;
123         }
124 }
125
126 static int
127 getip(IP4 yip, IP4 sip, IP4 gip, char mac[16])
128 {
129         struct {
130                 uchar status[2];
131                 uchar pkttype[2];
132                 uchar bufsize[2];
133                 uchar off[2];
134                 uchar seg[2];
135                 uchar lim[2];
136         } buf;
137         int i, r;
138         Dhcp *p;
139
140         memset(&buf, 0, sizeof(buf));
141         puts(buf.pkttype, 3);
142
143         if(r = pxecall(0x71, &buf))
144                 return -r;
145         if((p = unfar(gets(buf.seg), gets(buf.off))) == 0)
146                 return -1;
147         moveip(yip, p->yip);
148         moveip(sip, p->sip);
149         moveip(gip, p->gip);
150         mac[12] = 0;
151         for(i=0; i<6; i++){
152                 mac[i*2] = hex[p->mac[i]>>4];
153                 mac[i*2+1] = hex[p->mac[i]&15];
154         }
155         return 0;
156 }
157
158 static int
159 udpopen(IP4 sip)
160 {
161         struct {
162                 uchar status[2];
163                 uchar sip[4];
164         } buf;
165
166         puts(buf.status, 0);
167         moveip(buf.sip, sip);
168         return pxecall(0x30, &buf);
169 }
170
171 static int
172 udpclose(void)
173 {
174         uchar status[2];
175         puts(status, 0);
176         return pxecall(0x31, status);
177 }
178
179 static int
180 udpread(IP4 sip, IP4 dip, int *sport, int dport, int *len, void *data)
181 {
182         struct {
183                 uchar status[2];
184                 uchar sip[4];
185                 uchar dip[4];
186                 uchar sport[2];
187                 uchar dport[2];
188                 uchar len[2];
189                 uchar off[2];
190                 uchar seg[2];
191         } buf;
192         int r;
193
194         puts(buf.status, 0);
195         moveip(buf.sip, sip);
196         moveip(buf.dip, dip);
197         hnputs(buf.sport, *sport);
198         hnputs(buf.dport, dport);
199         puts(buf.len, *len);
200         puts(buf.off, (long)data);
201         puts(buf.seg, 0);
202         if(r = pxecall(0x32, &buf))
203                 return r;
204         moveip(sip, buf.sip);
205         *sport = nhgets(buf.sport);
206         *len = gets(buf.len);
207         return 0;
208 }
209
210 static int
211 udpwrite(IP4 ip, IP4 gw, int sport, int dport, int len, void *data)
212 {
213         struct {
214                 uchar status[2];
215                 uchar ip[4];
216                 uchar gw[4];
217                 uchar sport[2];
218                 uchar dport[2];
219                 uchar len[2];
220                 uchar off[2];
221                 uchar seg[2];
222         } buf;
223
224         puts(buf.status, 0);
225         moveip(buf.ip, ip);
226         moveip(buf.gw, gw);
227         hnputs(buf.sport, sport);
228         hnputs(buf.dport, dport);
229         puts(buf.len, len);
230         puts(buf.off, (long)data);
231         puts(buf.seg, 0);
232         return pxecall(0x33, &buf);
233 }
234
235 int
236 read(void *f, void *data, int len)
237 {
238         Tftp *t = f;
239         int seq, n;
240
241         while(!t->eof && t->rp >= t->ep){
242                 for(;;){
243                         n = sizeof(t->pkt);
244                         if(udpread(t->dip, t->sip, &t->dport, t->sport, &n, t->pkt))
245                                 continue;
246                         if(n >= 4)
247                                 break;
248                 }
249                 switch(nhgets(t->pkt)){
250                 case Tftp_DATA:
251                         seq = nhgets(t->pkt+2);
252                         if(seq <= t->seq){
253                                 putc('@');
254                                 continue;
255                         }
256                         hnputs(t->pkt, Tftp_ACK);
257                         while(udpwrite(t->dip, t->gip, t->sport, t->dport, 4, t->pkt))
258                                 putc('!');
259                         t->seq = seq;
260                         t->rp = t->pkt + 4;
261                         t->ep = t->pkt + n;
262                         t->eof = n < Segsize;
263                         break;
264                 case Tftp_ERROR:
265                         print(t->pkt+4);
266                         print(crnl);
267                 default:
268                         t->eof = 1;
269                         return -1;
270                 }
271                 break;
272         }
273         n = t->ep - t->rp;
274         if(len > n)
275                 len = n;
276         memmove(data, t->rp, len);
277         t->rp += len;
278         return len;
279 }
280
281 void
282 close(void *f)
283 {
284         Tftp *t = f;
285         t->eof = 1;
286         udpclose();
287 }
288
289
290 static int
291 tftpopen(Tftp *t, char *path, IP4 sip, IP4 dip, IP4 gip)
292 {
293         static ushort xport = 6666;
294         int r, n;
295         char *p;
296
297         moveip(t->sip, sip);
298         moveip(t->gip, gip);
299         memset(t->dip, 0, sizeof(t->dip));
300         t->sport = xport++;
301         t->dport = 0;
302         t->rp = t->ep = 0;
303         t->seq = -1;
304         t->eof = 0;
305         t->nul = 0;
306         if(r = udpopen(t->sip))
307                 return r;
308         p = t->pkt;
309         hnputs(p, Tftp_READ); p += 2;
310         n = strlen(path)+1;
311         memmove(p, path, n); p += n;
312         memmove(p, "octet", 6); p += 6;
313         n = p - t->pkt;
314         for(;;){
315                 if(r = udpwrite(dip, t->gip, t->sport, TftpPort, n, t->pkt))
316                         break;
317                 if(r = read(t, 0, 0))
318                         break;
319                 return 0;
320         }
321         close(t);
322         return r;
323 }
324
325 void
326 start(void *)
327 {
328         char mac[16], path[Maxpath], *kern;
329         IP4 yip, sip, gip;
330         void *f;
331         Tftp t;
332
333         if(pxeinit()){
334                 print("pxe init\r\n");
335                 halt();
336         }
337         if(getip(yip, sip, gip, mac)){
338                 print("bad dhcp\r\n");
339                 halt();
340         }
341         memmove(path, "/cfg/pxe/", 9);
342         memmove(path+9, mac, 13);
343         if(tftpopen(f = &t, path, yip, sip, gip)){
344                 print("no config\r\n");
345                 f = 0;
346         }
347         for(;;){
348                 kern = configure(f, path); f = 0;
349                 if(tftpopen(&t, kern, yip, sip, gip)){
350                         print("not found\r\n");
351                         continue;
352                 }
353                 print(bootkern(&t));
354                 print(crnl);
355         }
356 }