xref: /plan9-contrib/sys/src/nboot/pc/pxe.c (revision 529c1f209803c78c4f2cda11b13818a57f01c872)
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*
unfar(ulong seg,ulong off)64 unfar(ulong seg, ulong off)
65 {
66 	return (void*)((off & 0xFFFF) + (seg & 0xFFFF)*16);
67 }
68 
69 static void
puts(void * x,ushort v)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
gets(void * x)79 gets(void *x)
80 {
81 	uchar *p = x;
82 
83 	return p[1]<<8 | p[0];
84 }
85 
86 static void
hnputs(void * x,ushort v)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
nhgets(void * x)96 nhgets(void *x)
97 {
98 	uchar *p = x;
99 
100 	return p[0]<<8 | p[1];
101 }
102 
103 static void
moveip(IP4 d,IP4 s)104 moveip(IP4 d, IP4 s)
105 {
106 	memmove(d, s, sizeof(d));
107 }
108 
109 void
unload(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
getip(IP4 yip,IP4 sip,IP4 gip,char mac[16])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
udpopen(IP4 sip)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
udpclose(void)172 udpclose(void)
173 {
174 	uchar status[2];
175 	puts(status, 0);
176 	return pxecall(0x31, status);
177 }
178 
179 static int
udpread(IP4 sip,IP4 dip,int * sport,int dport,int * len,void * data)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
udpwrite(IP4 ip,IP4 gw,int sport,int dport,int len,void * data)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
read(void * f,void * data,int len)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 			if(seq < t->seq){
260 				putc('@');
261 				continue;
262 			}
263 			t->seq = seq+1;
264 			n -= 4;
265 			t->rp = t->pkt + 4;
266 			t->ep = t->rp + n;
267 			t->eof = n < Segsize;
268 			break;
269 		case Tftp_ERROR:
270 			print(t->pkt+4);
271 			print("\n");
272 		default:
273 			t->eof = 1;
274 			return -1;
275 		}
276 		break;
277 	}
278 	n = t->ep - t->rp;
279 	if(len > n)
280 		len = n;
281 	memmove(data, t->rp, len);
282 	t->rp += len;
283 	return len;
284 }
285 
286 void
close(void * f)287 close(void *f)
288 {
289 	Tftp *t = f;
290 	t->eof = 1;
291 	udpclose();
292 }
293 
294 
295 static int
tftpopen(Tftp * t,char * path,IP4 sip,IP4 dip,IP4 gip)296 tftpopen(Tftp *t, char *path, IP4 sip, IP4 dip, IP4 gip)
297 {
298 	static ushort xport = 6666;
299 	int r, n;
300 	char *p;
301 
302 	moveip(t->sip, sip);
303 	moveip(t->gip, gip);
304 	memset(t->dip, 0, sizeof(t->dip));
305 	t->sport = xport++;
306 	t->dport = 0;
307 	t->rp = t->ep = 0;
308 	t->seq = 1;
309 	t->eof = 0;
310 	t->nul = 0;
311 	if(r = udpopen(t->sip))
312 		return r;
313 	p = t->pkt;
314 	hnputs(p, Tftp_READ); p += 2;
315 	n = strlen(path)+1;
316 	memmove(p, path, n); p += n;
317 	memmove(p, "octet", 6); p += 6;
318 	n = p - t->pkt;
319 	for(;;){
320 		if(r = udpwrite(dip, t->gip, t->sport, TftpPort, n, t->pkt))
321 			break;
322 		if(r = read(t, 0, 0))
323 			break;
324 		return 0;
325 	}
326 	close(t);
327 	return r;
328 }
329 
330 void
start(void *)331 start(void *)
332 {
333 	char mac[16], path[Maxpath], *kern;
334 	IP4 yip, sip, gip;
335 	void *f;
336 	Tftp t;
337 
338 	if(pxeinit()){
339 		print("pxe init\n");
340 		halt();
341 	}
342 	if(getip(yip, sip, gip, mac)){
343 		print("bad dhcp\n");
344 		halt();
345 	}
346 	memmove(path, "/cfg/pxe/", 9);
347 	memmove(path+9, mac, 13);
348 	if(tftpopen(f = &t, path, yip, sip, gip))
349 		if(tftpopen(f, "/cfg/pxe/default", yip, sip, gip)){
350 			print("no config\n");
351 			f = 0;
352 		}
353 	for(;;){
354 		kern = configure(f, path); f = 0;
355 		if(tftpopen(&t, kern, yip, sip, gip)){
356 			print("not found\n");
357 			continue;
358 		}
359 		print(bootkern(&t));
360 		print("\n");
361 	}
362 }
363