Annotation of sys/arch/i386/stand/libsa/pxe.c, Revision 1.1.1.1
1.1 nbrk 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: }
CVSweb