1 /* $OpenBSD: pxe.c,v 1.5 2007/07/27 17:46:56 tom Exp $ */
2 /* $NetBSD: pxe.c,v 1.5 2003/03/11 18:29:00 drochner Exp $ */
3
4 /*
5 * Copyright 2001 Wasabi Systems, Inc.
6 * All rights reserved.
7 *
8 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed for the NetBSD Project by
21 * Wasabi Systems, Inc.
22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
24 * written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
41 * All rights reserved.
42 * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
43 * All rights reserved.
44 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
45 * All rights reserved.
46 *
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 */
68
69 /*
70 * Support for the Intel Preboot Execution Environment (PXE).
71 *
72 * PXE provides a UDP implementation as well as a UNDI network device
73 * driver. UNDI is much more complicated to use than PXE UDP, so we
74 * use PXE UDP as a cheap and easy way to get PXE support.
75 */
76
77 #include <sys/param.h>
78 #include <sys/socket.h>
79
80 #ifdef _STANDALONE
81 #include <lib/libkern/libkern.h>
82 #else
83 #include <string.h>
84 #endif
85
86 #include <net/if.h>
87
88 #include <netinet/in.h>
89 #include <netinet/if_ether.h>
90 #include <netinet/in_systm.h>
91 #include <netinet/ip.h>
92 #include <netinet/ip_var.h>
93 #include <netinet/udp.h>
94 #include <netinet/udp_var.h>
95
96 #include <lib/libsa/stand.h>
97 #include <lib/libsa/net.h>
98 #include <lib/libsa/bootp.h>
99
100 #include <stand/boot/bootarg.h>
101 #include <machine/biosvar.h>
102
103 #include "pxeboot.h"
104 #include "pxe.h"
105 #include "pxe_netif.h"
106
107 void (*pxe_call)(u_int16_t);
108
109 void pxecall_bangpxe(u_int16_t); /* pxe_call.S */
110 void pxecall_pxenv(u_int16_t); /* pxe_call.S */
111
112 char pxe_command_buf[256];
113
114 BOOTPLAYER bootplayer;
115
116 struct in_addr servip; /* for tftp */ /* XXX init this */
117
118 extern char *bootmac; /* To pass to kernel */
119
120 /* static struct btinfo_netif bi_netif; */
121
122 /*****************************************************************************
123 * This section is a replacement for libsa/udp.c
124 *****************************************************************************/
125
126 /* Caller must leave room for ethernet, ip, and udp headers in front!! */
127 ssize_t
128 pxesendudp(struct iodesc *d, void *pkt, size_t len)
129 {
130 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf;
131
132 uw->status = 0;
133
134 uw->ip = d->destip.s_addr;
135 uw->gw = gateip.s_addr;
136 uw->src_port = d->myport;
137 uw->dst_port = d->destport;
138 uw->buffer_size = len;
139 uw->buffer.segment = VTOPSEG(pkt);
140 uw->buffer.offset = VTOPOFF(pkt);
141
142 pxe_call(PXENV_UDP_WRITE);
143
144 if (uw->status != PXENV_STATUS_SUCCESS) {
145 /* XXX This happens a lot; it shouldn't. */
146 if (uw->status != PXENV_STATUS_FAILURE)
147 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n",
148 uw->status);
149 return -1;
150 }
151
152 return len;
153 }
154
155 /*
156 * Receive a UDP packet and validate it for us.
157 * Caller leaves room for the headers (Ether, IP, UDP).
158 */
159 ssize_t
160 pxereadudp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
161 {
162 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf;
163 struct udphdr *uh;
164 struct ip *ip;
165
166 uh = (struct udphdr *)pkt - 1;
167 ip = (struct ip *)uh - 1;
168
169 bzero(ur, sizeof(*ur));
170
171 ur->dest_ip = d->myip.s_addr;
172 ur->d_port = d->myport;
173 ur->buffer_size = len;
174 ur->buffer.segment = VTOPSEG(pkt);
175 ur->buffer.offset = VTOPOFF(pkt);
176
177 /* XXX Timeout unused. */
178
179 pxe_call(PXENV_UDP_READ);
180
181 if (ur->status != PXENV_STATUS_SUCCESS) {
182 /* XXX This happens a lot; it shouldn't. */
183 if (ur->status != PXENV_STATUS_FAILURE)
184 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n",
185 ur->status);
186 return -1;
187 }
188
189 ip->ip_src.s_addr = ur->src_ip;
190 uh->uh_sport = ur->s_port;
191 uh->uh_dport = d->myport;
192
193 return ur->buffer_size;
194 }
195
196 /*
197 * netif layer:
198 * open, close, shutdown: called from dev_net.c
199 * socktodesc: called by network protocol modules
200 *
201 * We only allow one open socket.
202 */
203
204 static int pxe_inited;
205 static struct iodesc desc;
206
207 int
208 pxe_netif_open()
209 {
210 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf;
211
212 #ifdef NETIF_DEBUG
213 printf("pxe_netif_open()\n");
214 #endif
215 if (!pxe_inited) {
216 if (pxe_init(0) != 0)
217 return -1;
218 pxe_inited = 1;
219 }
220 /* BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); */
221
222 bzero(uo, sizeof(*uo));
223
224 uo->src_ip = bootplayer.yip;
225
226 pxe_call(PXENV_UDP_OPEN);
227
228 if (uo->status != PXENV_STATUS_SUCCESS) {
229 printf("\npxe_netif_open: PXENV_UDP_OPEN failed: 0x%x\n",
230 uo->status);
231 return -1;
232 }
233
234 bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN);
235 bootmac = bootplayer.CAddr;
236
237 /*
238 * Since the PXE BIOS has already done DHCP, make sure we
239 * don't reuse any of its transaction IDs.
240 */
241 desc.xid = bootplayer.ident;
242
243 return 0;
244 }
245
246 void
247 pxe_netif_close(sock)
248 int sock;
249 {
250 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf;
251
252 #ifdef NETIF_DEBUG
253 if (sock != 0)
254 printf("pxe_netif_close: sock=%d\n", sock);
255 #endif
256
257 uc->status = 0;
258
259 pxe_call(PXENV_UDP_CLOSE);
260
261 if (uc->status != PXENV_STATUS_SUCCESS)
262 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
263 uc->status);
264 }
265
266 void
267 pxe_netif_shutdown()
268 {
269 #ifdef NETIF_DEBUG
270 printf("pxe_netif_shutdown()\n");
271 #endif
272
273 pxe_shutdown();
274 }
275
276 struct iodesc *
277 pxesocktodesc(sock)
278 int sock;
279 {
280
281 #ifdef NETIF_DEBUG
282 if (sock != 0)
283 return 0;
284 else
285 #endif
286 return &desc;
287 }
288
289 /*****************************************************************************
290 * PXE initialization and support routines
291 *****************************************************************************/
292
293 u_int16_t pxe_command_buf_seg;
294 u_int16_t pxe_command_buf_off;
295
296 extern u_int16_t bangpxe_off, bangpxe_seg;
297 extern u_int16_t pxenv_off, pxenv_seg;
298
299 /* static struct btinfo_netif bi_netif; */
300
301 void
302 pxeprobe(void)
303 {
304 if (!pxe_inited) {
305 if (pxe_init(1) == 0) {
306 pxe_inited = 1;
307 }
308 }
309 }
310
311 int
312 pxe_init(int quiet)
313 {
314 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf;
315 pxenv_t *pxenv;
316 pxe_t *pxe;
317 char *cp;
318 int i;
319 u_int8_t cksum, *ucp;
320
321 /*
322 * Checking for the presence of PXE is a machine-dependent
323 * operation. On the IA-32, this can be done two ways:
324 *
325 * Int 0x1a function 0x5650
326 *
327 * Scan memory for the !PXE or PXENV+ signatures
328 *
329 * We do the latter, since the Int method returns a pointer
330 * to a deprecated structure (PXENV+).
331 */
332
333 pxenv = NULL;
334 pxe = NULL;
335
336 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) {
337 if (pxenv == NULL) {
338 pxenv = (pxenv_t *)cp;
339 if (memcmp(pxenv->Signature, S_SIZE("PXENV+")) != 0)
340 pxenv = NULL;
341 else {
342 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
343 i < pxenv->Length; i++)
344 cksum += ucp[i];
345 if (cksum != 0) {
346 printf("\npxe_init: bad cksum (0x%x) "
347 "for PXENV+ at 0x%lx\n", cksum,
348 (u_long) cp);
349 pxenv = NULL;
350 }
351 }
352 }
353
354 if (pxe == NULL) {
355 pxe = (pxe_t *)cp;
356 if (memcmp(pxe->Signature, S_SIZE("!PXE")) != 0)
357 pxe = NULL;
358 else {
359 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
360 i < pxe->StructLength; i++)
361 cksum += ucp[i];
362 if (cksum != 0) {
363 printf("pxe_init: bad cksum (0x%x) "
364 "for !PXE at 0x%lx\n", cksum,
365 (u_long) cp);
366 pxe = NULL;
367 }
368 }
369 }
370
371 if (pxe != NULL && pxenv != NULL)
372 break;
373 }
374
375 if (pxe == NULL && pxenv == NULL) {
376 if (!quiet) printf("pxe_init: No PXE BIOS found.\n");
377 return 1;
378 }
379
380 if (pxenv == NULL) {
381 /* assert(pxe != NULL); */
382
383 printf(quiet ? " pxe!" : "PXE present\n");
384 } else { /* pxenv != NULL */
385 int bang = 0;
386
387 if (pxenv->Version >= 0x0201 && pxe != NULL) {
388 /* 2.1 or greater -- don't use PXENV+ */
389 bang = 1;
390 }
391
392 if (quiet) {
393 printf(" pxe%c[%d.%d]",
394 (bang ? '!' : '+'),
395 (pxenv->Version >> 8) & 0xff,
396 pxenv->Version & 0xff);
397 } else {
398 printf("PXE BIOS Version %d.%d\n",
399 (pxenv->Version >> 8) & 0xff,
400 pxenv->Version & 0xff);
401 }
402
403 if (bang) {
404 pxenv = NULL;
405 }
406 }
407
408 if (pxenv == NULL) {
409 pxe_call = pxecall_bangpxe;
410 bangpxe_off = pxe->EntryPointSP.offset;
411 bangpxe_seg = pxe->EntryPointSP.segment;
412 } else {
413 pxe_call = pxecall_pxenv;
414 pxenv_off = pxenv->RMEntry.offset;
415 pxenv_seg = pxenv->RMEntry.segment;
416 }
417
418 /*
419 * Pre-compute the segment/offset of the pxe_command_buf
420 * to make things nicer in the low-level calling glue.
421 */
422 pxe_command_buf_seg = VTOPSEG(pxe_command_buf);
423 pxe_command_buf_off = VTOPOFF(pxe_command_buf);
424
425 /*
426 * Get the cached info from the server's Discovery reply packet.
427 */
428 bzero(gci, sizeof(*gci));
429 gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
430 pxe_call(PXENV_GET_CACHED_INFO);
431
432 if (gci->Status != PXENV_STATUS_SUCCESS) {
433 printf("\npxeinfo: PXENV_GET_CACHED_INFO failed: 0x%x\n",
434 gci->Status);
435 return 1;
436 }
437
438 memcpy(&bootplayer,
439 SEGOFF2FLAT(gci->Buffer.segment, gci->Buffer.offset),
440 gci->BufferSize);
441
442 bcopy(&bootplayer.yip, &myip.s_addr, sizeof(myip.s_addr));
443 bcopy(&bootplayer.sip, &servip.s_addr, sizeof(servip.s_addr));
444
445 /* Compute our "natural" netmask. */
446 if (IN_CLASSA(myip.s_addr))
447 netmask = IN_CLASSA_NET;
448 else if (IN_CLASSB(myip.s_addr))
449 netmask = IN_CLASSB_NET;
450 else
451 netmask = IN_CLASSC_NET;
452
453 return 0;
454 }
455
456 void
457 pxeinfo(void)
458 {
459 u_int8_t *p;
460 #ifdef PXE_DEBUG
461 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf;
462 #endif
463
464 printf(" mac %s", ether_sprintf(bootplayer.CAddr));
465 p = (u_int8_t *)&myip.s_addr;
466 printf(", ip %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
467 p = (u_int8_t *)&servip.s_addr;
468 printf(", server %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
469
470 #ifdef PXE_DEBUG
471 /*
472 * Get network interface information.
473 */
474 bzero(gnt, sizeof(*gnt));
475 pxe_call(PXENV_UNDI_GET_NIC_TYPE);
476
477 if (gnt->Status != PXENV_STATUS_SUCCESS) {
478 printf("\npxeinfo: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n",
479 gnt->Status);
480 return;
481 }
482
483 switch (gnt->NicType) {
484 case PCI_NIC:
485 case CardBus_NIC:
486 /* strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); */
487 /* bi_netif.bus = BI_BUS_PCI; */
488 /* bi_netif.addr.tag = gnt->info.pci.BusDevFunc; */
489
490 printf("\nPXE: Using %s device at bus %d device %d function %d\n",
491 gnt->NicType == PCI_NIC ? "PCI" : "CardBus",
492 (gnt->info.pci.BusDevFunc >> 8) & 0xff,
493 (gnt->info.pci.BusDevFunc >> 3) & 0x1f,
494 gnt->info.pci.BusDevFunc & 0x7);
495 break;
496
497 case PnP_NIC:
498 /* XXX Make bootinfo work with this. */
499 printf("\nPXE: Using PnP device at 0x%x\n",
500 gnt->info.pnp.CardSelNum);
501 }
502 #endif
503 }
504
505 void
506 pxe_shutdown(void)
507 {
508 int try;
509 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf;
510 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf;
511 #ifdef PXE_DEBUG
512 t_PXENV_UDP_CLOSE *close = (void *) pxe_command_buf;
513 #endif
514
515 if (pxe_call == NULL)
516 return;
517
518 /* Close any open UDP connections. Ignore return value. */
519 pxe_call(PXENV_UDP_CLOSE);
520 #ifdef PXE_DEBUG
521 printf("pxe_shutdown: PXENV_UDP_CLOSE returned 0x%x\n", close->status);
522 #endif
523
524 /* Sometimes PXENV_UNDI_SHUTDOWN doesn't work at first */
525 for (try = 3; try > 0; try--) {
526 pxe_call(PXENV_UNDI_SHUTDOWN);
527
528 if (shutdown->Status == PXENV_STATUS_SUCCESS)
529 break;
530
531 printf("pxe_shutdown: PXENV_UNDI_SHUTDOWN failed: 0x%x\n",
532 shutdown->Status);
533
534 if (try != 1)
535 sleep(1);
536 }
537
538 /* Have multiple attempts at PXENV_UNLOAD_STACK, too */
539 for (try = 3; try > 0; try--) {
540 pxe_call(PXENV_UNLOAD_STACK);
541
542 if (unload->Status == PXENV_STATUS_SUCCESS)
543 break;
544
545 printf("pxe_shutdown: PXENV_UNLOAD_STACK failed: 0x%x\n",
546 unload->Status);
547
548 if (try != 1)
549 sleep(1);
550 }
551 }