Commit | Line | Data |
---|---|---|
db4d0741 MR |
1 | /************************************************************************** |
2 | NETBOOT - BOOTP/TFTP Bootstrap Program | |
3 | ||
4 | Author: Martin Renters | |
5 | Date: Dec/93 | |
6 | ||
7 | **************************************************************************/ | |
8 | ||
9 | #include "netboot.h" | |
10 | ||
11 | #define ESC 0x1b /* ESC Key */ | |
12 | ||
13 | int jmp_bootmenu[10]; | |
14 | struct exec head; | |
15 | int txtoff; | |
16 | void (*kernelentry)(); | |
17 | char *loadpoint; | |
18 | char *bootfile; | |
19 | char bootname[128]; | |
20 | char cfg[32]; | |
21 | char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
22 | struct nfs_diskless nfsdiskless; | |
23 | extern char packet[]; | |
24 | extern int packetlen; | |
25 | char *putdec(); | |
26 | ||
27 | /************************************************************************** | |
28 | MAIN - Kick off routine | |
29 | **************************************************************************/ | |
30 | main() | |
31 | { | |
32 | int c; | |
33 | char *p; | |
34 | extern char edata[], end[]; | |
35 | for (p=edata; p<end; p++) *p = 0; /* Zero BSS */ | |
36 | #ifdef ASK_BOOT | |
37 | while (1) { | |
38 | printf("\n\rBoot from Network (Y/N) ? "); | |
39 | c = getchar(); | |
40 | if ((c >= 'a') && (c <= 'z')) c &= 0x5F; | |
41 | if (c == '\r') break; | |
42 | putchar(c); | |
43 | if (c == 'N') | |
44 | exit(0); | |
45 | if (c == 'Y') | |
46 | break; | |
47 | printf(" - bad response\n\r"); | |
48 | } | |
49 | #endif | |
50 | gateA20(); | |
51 | printf("\r\nBOOTP/TFTP bootstrap loader ESC for menu\n\r"); | |
52 | printf("\r\nSearching for adapter..."); | |
53 | if (!eth_probe()) { | |
54 | printf("No adapter found.\r\n"); | |
55 | exit(0); | |
56 | } | |
57 | bootfile = DEFAULT_BOOTFILE; | |
58 | while (1) { | |
59 | if (setjmp(jmp_bootmenu)) | |
60 | bootmenu(); | |
61 | else | |
62 | load(); | |
63 | } | |
64 | } | |
65 | ||
66 | /************************************************************************** | |
67 | LOAD - Try to get booted | |
68 | **************************************************************************/ | |
69 | load() | |
70 | { | |
71 | char *p; | |
72 | if (!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr) { | |
73 | printf("\r\nSearching for server...\r\n"); | |
74 | if (!bootp()) { | |
75 | printf("No Server found.\r\n"); | |
76 | longjmp(jmp_bootmenu,1); | |
77 | } | |
78 | } | |
db4d0741 MR |
79 | printf("station IP %I, server IP %I\r\n", |
80 | arptable[ARP_CLIENT].ipaddr, | |
81 | arptable[ARP_SERVER].ipaddr); | |
82 | p = cfg; | |
83 | *(p++) = 'c'; | |
84 | *(p++) = 'f'; | |
85 | *(p++) = 'g'; | |
86 | *(p++) = '.'; | |
87 | p = putdec(p, arptable[ARP_CLIENT].ipaddr>>24); | |
88 | *(p++) = '.'; | |
89 | p = putdec(p, arptable[ARP_CLIENT].ipaddr>>16); | |
90 | *(p++) = '.'; | |
91 | p = putdec(p, arptable[ARP_CLIENT].ipaddr>>8); | |
92 | *(p++) = '.'; | |
93 | p = putdec(p, arptable[ARP_CLIENT].ipaddr); | |
94 | *p = 0; | |
95 | printf("Loading %s...\r\n",cfg); | |
96 | if (!tftp(cfg, TFTP_CODE_CFG)) { | |
97 | printf("Unable to load config file.\r\n"); | |
98 | longjmp(jmp_bootmenu,1); | |
99 | } | |
100 | printf("Loading %s...\r\n",bootfile); | |
101 | if (!tftp(bootfile, TFTP_CODE_BOOT)) { | |
102 | printf("Unable to load boot file.\r\n"); | |
103 | longjmp(jmp_bootmenu,1); | |
104 | } | |
105 | if (!(head.a_entry & 0x00F00000)) { /* < 1MB kernel? */ | |
106 | printf("<1MB kernel. Relocating\r\n"); | |
107 | bcopy(0x100000, 0, 0x400); /* Relocate */ | |
108 | bcopy(0x100500, 0x500, ((int)loadpoint) - 0x100500); | |
109 | } | |
110 | kernelentry = (void *)(head.a_entry & 0x00FFFFFF); | |
111 | (*kernelentry)(0,0,0,0,&nfsdiskless,0,0,0); | |
112 | } | |
113 | ||
114 | /************************************************************************** | |
115 | POLLKBD - Check for Interrupt from keyboard | |
116 | **************************************************************************/ | |
117 | pollkbd() | |
118 | { | |
119 | if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1); | |
120 | } | |
121 | ||
122 | /************************************************************************** | |
123 | UDP_TRANSMIT - Send a UDP datagram | |
124 | **************************************************************************/ | |
125 | udp_transmit(destip, srcsock, destsock, len, buf) | |
126 | unsigned long destip; | |
127 | unsigned short srcsock, destsock; | |
128 | int len; | |
129 | char *buf; | |
130 | { | |
131 | struct iphdr *ip; | |
132 | struct udphdr *udp; | |
133 | struct arprequest arpreq, *arpreply; | |
134 | int arpentry, i; | |
135 | unsigned long time; | |
136 | int retry = MAX_ARP_RETRIES; | |
137 | ip = (struct iphdr *)buf; | |
138 | udp = (struct udphdr *)(buf + sizeof(struct iphdr)); | |
139 | ip->verhdrlen = 0x45; | |
140 | ip->service = 0; | |
141 | ip->len = htons(len); | |
142 | ip->ident = 0; | |
143 | ip->frags = 0; | |
144 | ip->ttl = 60; | |
145 | ip->protocol = IP_UDP; | |
146 | ip->chksum = 0; | |
147 | convert_ipaddr(ip->src, &arptable[ARP_CLIENT].ipaddr); | |
148 | convert_ipaddr(ip->dest, &destip); | |
149 | ip->chksum = ipchksum(buf, sizeof(struct iphdr)); | |
150 | udp->src = htons(srcsock); | |
151 | udp->dest = htons(destsock); | |
152 | udp->len = htons(len - sizeof(struct iphdr)); | |
153 | udp->chksum = 0; | |
154 | if (destip == IP_BROADCAST) { | |
155 | eth_transmit(broadcast, IP, len, buf); | |
156 | } else { | |
157 | for(arpentry = 0; arpentry<MAX_ARP; arpentry++) | |
158 | if (arptable[arpentry].ipaddr == destip) break; | |
159 | if (arpentry == MAX_ARP) { | |
160 | printf("%I is not in my arp table!\n"); | |
161 | return(0); | |
162 | } | |
163 | for (i = 0; i<ETHER_ADDR_SIZE; i++) | |
164 | if (arptable[arpentry].node[i]) break; | |
165 | if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */ | |
166 | arpreq.hwtype = htons(1); | |
167 | arpreq.protocol = htons(IP); | |
168 | arpreq.hwlen = ETHER_ADDR_SIZE; | |
169 | arpreq.protolen = 4; | |
170 | arpreq.opcode = htons(ARP_REQUEST); | |
171 | bcopy(arptable[ARP_CLIENT].node, arpreq.shwaddr, ETHER_ADDR_SIZE); | |
172 | convert_ipaddr(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr); | |
173 | bzero(arpreq.thwaddr, ETHER_ADDR_SIZE); | |
174 | convert_ipaddr(arpreq.tipaddr, &destip); | |
175 | while (retry--) { | |
176 | eth_transmit(broadcast, ARP, sizeof(arpreq), &arpreq); | |
177 | time = currticks() + TIMEOUT; | |
178 | while (time > currticks()) { | |
179 | pollkbd(); | |
180 | if (eth_poll() && (packetlen >= ETHER_HDR_SIZE + sizeof(arpreq)) && | |
181 | (((packet[12] << 8) | packet[13]) == ARP)) { | |
182 | arpreply = (struct arprequest *)&packet[ETHER_HDR_SIZE]; | |
183 | if ((arpreply->opcode == ntohs(ARP_REPLY)) && | |
184 | bcompare(arpreply->sipaddr,arpreq.tipaddr, 4)) { | |
185 | bcopy(arpreply->shwaddr, arptable[arpentry].node,ETHER_ADDR_SIZE); | |
186 | goto xmit; | |
187 | } | |
188 | } | |
189 | } | |
190 | } | |
191 | return(0); | |
192 | } | |
193 | xmit: eth_transmit(arptable[arpentry].node, IP, len, buf); | |
194 | } | |
195 | return(1); | |
196 | } | |
197 | ||
198 | /************************************************************************** | |
199 | TFTP - Try to load something | |
200 | **************************************************************************/ | |
201 | tftp(name, type) | |
202 | char *name; | |
203 | int type; | |
204 | { | |
205 | int retry = MAX_TFTP_RETRIES; | |
206 | static unsigned short isocket = 2000; | |
207 | unsigned short osocket = TFTP; | |
208 | unsigned short len, block=1; | |
209 | struct tftp_t tp; | |
210 | unsigned long time; | |
211 | int code; | |
212 | char *p = tp.u.rrq; | |
213 | isocket++; | |
214 | tp.opcode = htons(TFTP_RRQ); | |
215 | while (*name) *(p++) = *(name++); | |
216 | *(p++) = 0; | |
217 | *(p++) = 'o'; | |
218 | *(p++) = 'c'; | |
219 | *(p++) = 't'; | |
220 | *(p++) = 'e'; | |
221 | *(p++) = 't'; | |
222 | *(p++) = 0; | |
223 | len = p - (char *)&tp; | |
224 | while(retry--) { | |
225 | if (!udp_transmit(arptable[ARP_SERVER].ipaddr, isocket, osocket, | |
226 | len, &tp)) return(0); | |
227 | next: time = currticks() + TIMEOUT; | |
228 | while(time > currticks()) { | |
229 | pollkbd(); | |
230 | if (eth_poll()) { | |
231 | code = tftp_data(&tp, &block, isocket, &osocket, | |
232 | type); | |
233 | if (!code) continue; | |
234 | if (code == TFTP_CODE_EOF) return(1); | |
235 | if (code == TFTP_CODE_ERROR) return(0); | |
236 | len = TFTP_MIN_PACKET_SIZE; | |
237 | retry = MAX_TFTP_RETRIES; | |
238 | goto next; | |
239 | } | |
240 | } | |
241 | } | |
242 | return(0); | |
243 | } | |
244 | ||
245 | /************************************************************************** | |
246 | TFTP_DATA - Check and handle incoming TFTP packets | |
247 | **************************************************************************/ | |
248 | tftp_data(req, block, isocket, osocket, type) | |
249 | struct tftp_t *req; | |
250 | unsigned short *block, isocket, *osocket; | |
251 | int type; | |
252 | { | |
253 | struct tftp_t *tp; | |
254 | char *p; | |
255 | int len; | |
256 | if (!chkpacket(TFTP_MIN_PACKET_SIZE, isocket)) return(0); | |
257 | tp = (struct tftp_t *)&packet[ETHER_HDR_SIZE]; | |
258 | if (tp->opcode == ntohs(TFTP_ERROR)) { | |
259 | printf("TFTP error %d (%s)\r\n", ntohs(tp->u.err.errcode), | |
260 | tp->u.err.errmsg); | |
261 | longjmp(jmp_bootmenu, 1); | |
262 | } | |
263 | if (tp->opcode != htons(TFTP_DATA)) return(0); | |
264 | len = ntohs(tp->udp.len) - sizeof(struct udphdr) - | |
265 | (2*sizeof(unsigned short)); | |
266 | req->opcode = htons(TFTP_ACK); /* Send ack */ | |
267 | req->u.ack.block = tp->u.data.block; | |
268 | udp_transmit(arptable[ARP_SERVER].ipaddr, isocket, *osocket, | |
269 | TFTP_MIN_PACKET_SIZE, req); | |
270 | if (*block != ntohs(tp->u.data.block)) return(TFTP_CODE_MORE); | |
271 | if (*block == 1) { | |
272 | *osocket = htons(tp->udp.src); | |
273 | if ((type == TFTP_CODE_CFG) && | |
274 | (len == sizeof(struct nfs_diskless))) { | |
275 | bcopy(tp->u.data.download, &nfsdiskless, sizeof(struct nfs_diskless)); | |
276 | return(TFTP_CODE_EOF); | |
277 | } | |
278 | bcopy(tp->u.data.download, &head, sizeof(struct exec)); | |
279 | if ((type == TFTP_CODE_BOOT) && | |
280 | ((len < sizeof(struct exec)) || (N_BADMAG(head)))) { | |
281 | printf("Not an executable.\r\n"); | |
282 | return(TFTP_CODE_ERROR); | |
283 | } else { | |
284 | if (((head.a_entry & 0x00FFFFFF) == 0) && | |
285 | (head.a_text + head.a_data >= RELOC)) { | |
286 | printf("Executable too large.\r\n"); | |
287 | return(TFTP_CODE_ERROR); | |
288 | } | |
289 | /* We load above 1 mb so we don't clobber DOS */ | |
290 | loadpoint = (char *)0x100000; | |
291 | printf("text=0x%X", head.a_text); | |
292 | } | |
293 | txtoff = N_TXTOFF(head); | |
294 | } | |
295 | p = tp->u.data.download; | |
296 | *block +=1; | |
297 | while (len--) { | |
298 | if (txtoff) { | |
299 | txtoff--; | |
300 | p++; | |
301 | continue; | |
302 | } | |
303 | if (head.a_text) { | |
304 | *(loadpoint++) = *(p++); | |
305 | head.a_text--; | |
306 | if (!head.a_text) { | |
307 | printf(", data=0x%X",head.a_data); | |
308 | while (((int)loadpoint) & CLOFSET) | |
309 | *(loadpoint++) = 0; | |
310 | } | |
311 | continue; | |
312 | } | |
313 | if (head.a_data) { | |
314 | *(loadpoint++) = *(p++); | |
315 | head.a_data--; | |
316 | if (!head.a_data) { | |
317 | printf(", bss=0x%X\r\n",head.a_bss); | |
318 | return(TFTP_CODE_EOF); | |
319 | } | |
320 | ||
321 | } | |
322 | } | |
323 | return((head.a_text || head.a_data) ? TFTP_CODE_MORE : TFTP_CODE_EOF); | |
324 | } | |
325 | ||
326 | /************************************************************************** | |
327 | BOOTP - Get my IP address and load information | |
328 | **************************************************************************/ | |
329 | bootp() | |
330 | { | |
331 | int retry = MAX_BOOTP_RETRIES; | |
332 | struct bootp_t bp; | |
333 | struct bootp_t *reply; | |
334 | unsigned long time, starttime; | |
335 | bzero(&bp, sizeof(struct bootp_t)); | |
336 | bp.bp_op = BOOTP_REQUEST; | |
337 | bp.bp_htype = 1; | |
338 | bp.bp_hlen = ETHER_ADDR_SIZE; | |
339 | bp.bp_xid = starttime = currticks(); | |
340 | bcopy(arptable[ARP_CLIENT].node, bp.bp_hwaddr, ETHER_ADDR_SIZE); | |
341 | while(retry--) { | |
342 | udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER, | |
343 | sizeof(struct bootp_t), &bp); | |
344 | time = currticks() + TIMEOUT; | |
345 | while(time > currticks()) { | |
346 | pollkbd(); | |
347 | if (eth_poll()) { /* We have something! */ | |
348 | reply = (struct bootp_t *)&packet[ETHER_HDR_SIZE]; | |
349 | if (((!chkpacket(sizeof(struct bootp_t), | |
350 | BOOTP_CLIENT))) || (reply->bp_op != BOOTP_REPLY)) | |
351 | continue; | |
352 | convert_ipaddr(&arptable[ARP_CLIENT].ipaddr, | |
353 | reply->bp_yiaddr); | |
354 | convert_ipaddr(&arptable[ARP_SERVER].ipaddr, | |
355 | reply->bp_siaddr); | |
356 | bzero(arptable[ARP_SERVER].node, ETHER_ADDR_SIZE); /* Kill arp */ | |
9363f593 | 357 | if (reply->bp_file[0]) { |
db4d0741 | 358 | bcopy(reply->bp_file, bootname, 128); |
9363f593 MR |
359 | bootfile = bootname; |
360 | } | |
db4d0741 MR |
361 | return(1); |
362 | } | |
363 | } | |
364 | bp.bp_secs = htons((currticks()-starttime)/20); | |
365 | } | |
366 | return(0); | |
367 | } | |
368 | ||
369 | /************************************************************************** | |
370 | IPCHKSUM - Checksum IP Header | |
371 | **************************************************************************/ | |
372 | ipchksum(ip, len) | |
373 | unsigned short *ip; | |
374 | int len; | |
375 | { | |
376 | unsigned long sum = 0; | |
377 | len >>= 1; | |
378 | while (len--) { | |
379 | sum += *(ip++); | |
380 | if (sum > 0xFFFF) | |
381 | sum -= 0xFFFF; | |
382 | } | |
383 | return((~sum) & 0x0000FFFF); | |
384 | } | |
385 | ||
386 | /************************************************************************** | |
387 | CHKPACKET - Quick check to see if incoming packet is good | |
388 | **************************************************************************/ | |
389 | chkpacket(size, type) | |
390 | int size, type; | |
391 | { | |
392 | struct iphdr *ip; | |
393 | struct udphdr *udp; | |
394 | if (packetlen < (ETHER_HDR_SIZE + size)) return(0); | |
395 | if (((packet[12] << 8) | packet[13]) != IP) return(0); | |
396 | ip = (struct iphdr *)&packet[ETHER_HDR_SIZE]; | |
397 | if (ip->verhdrlen != 0x45) return(0); | |
398 | if (ipchksum(ip, sizeof(struct iphdr))) return(0); | |
399 | if (ip->protocol != IP_UDP) return(0); | |
400 | udp = (struct udphdr *)&packet[ETHER_HDR_SIZE + sizeof(struct iphdr)]; | |
401 | if (ntohs(udp->dest) != type) return(0); | |
402 | return(1); | |
403 | } | |
404 | ||
405 | /************************************************************************** | |
406 | CONVERT_IPADDR - Convert IP address from net to machine order | |
407 | **************************************************************************/ | |
408 | convert_ipaddr(d, s) | |
409 | char *d,*s; | |
410 | { | |
411 | *(d+3) = *s; | |
412 | *(d+2) = *(s+1); | |
413 | *(d+1) = *(s+2); | |
414 | *d = *(s+3); | |
415 | } |