1 /* $NetBSD: pxe.c,v 1.16 2009/10/26 19:16:56 cegger Exp $ */
4 * Copyright 2001 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
40 * All rights reserved.
41 * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
42 * All rights reserved.
43 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
44 * All rights reserved.
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
55 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * Support for the Intel Preboot Execution Environment (PXE).
71 * PXE provides a UDP implementation as well as a UNDI network device
72 * driver. UNDI is much more complicated to use than PXE UDP, so we
73 * use PXE UDP as a cheap and easy way to get PXE support.
76 #include <sys/param.h>
77 #include <sys/socket.h>
80 #include <lib/libkern/libkern.h>
85 #include <netinet/in.h>
86 #include <netinet/in_systm.h>
87 #include <netinet/ip.h>
88 #include <netinet/ip_var.h>
89 #include <netinet/udp.h>
90 #include <netinet/udp_var.h>
92 #include <net/if_ether.h>
94 #include <lib/libsa/stand.h>
95 #include <lib/libsa/net.h>
96 #include <lib/libsa/bootp.h>
103 #include "pxe_netif.h"
105 void (*pxe_call
)(uint16_t);
107 void pxecall_bangpxe(uint16_t); /* pxe_call.S */
108 void pxecall_pxenv(uint16_t); /* pxe_call.S */
110 char pxe_command_buf
[256];
112 BOOTPLAYER bootplayer
;
114 static struct btinfo_netif bi_netif
;
116 /*****************************************************************************
117 * This section is a replacement for libsa/udp.c
118 *****************************************************************************/
120 /* Caller must leave room for ethernet, ip, and udp headers in front!! */
122 sendudp(struct iodesc
*d
, void *pkt
, size_t len
)
124 t_PXENV_UDP_WRITE
*uw
= (void *) pxe_command_buf
;
128 uw
->ip
= d
->destip
.s_addr
;
129 uw
->gw
= gateip
.s_addr
;
130 uw
->src_port
= d
->myport
;
131 uw
->dst_port
= d
->destport
;
132 uw
->buffer_size
= len
;
133 uw
->buffer
.segment
= VTOPSEG(pkt
);
134 uw
->buffer
.offset
= VTOPOFF(pkt
);
136 pxe_call(PXENV_UDP_WRITE
);
138 if (uw
->status
!= PXENV_STATUS_SUCCESS
) {
139 /* XXX This happens a lot; it shouldn't. */
140 if (uw
->status
!= PXENV_STATUS_FAILURE
)
141 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n",
150 * Receive a UDP packet and validate it for us.
151 * Caller leaves room for the headers (Ether, IP, UDP).
154 readudp(struct iodesc
*d
, void *pkt
, size_t len
, saseconds_t tleft
)
156 t_PXENV_UDP_READ
*ur
= (void *) pxe_command_buf
;
160 uh
= (struct udphdr
*)pkt
- 1;
161 ip
= (struct ip
*)uh
- 1;
163 (void)memset(ur
, 0, sizeof(*ur
));
165 ur
->dest_ip
= d
->myip
.s_addr
;
166 ur
->d_port
= d
->myport
;
167 ur
->buffer_size
= len
;
168 ur
->buffer
.segment
= VTOPSEG(pkt
);
169 ur
->buffer
.offset
= VTOPOFF(pkt
);
171 /* XXX Timeout unused. */
173 pxe_call(PXENV_UDP_READ
);
175 if (ur
->status
!= PXENV_STATUS_SUCCESS
) {
176 /* XXX This happens a lot; it shouldn't. */
177 if (ur
->status
!= PXENV_STATUS_FAILURE
)
178 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n",
183 ip
->ip_src
.s_addr
= ur
->src_ip
;
184 uh
->uh_sport
= ur
->s_port
;
185 uh
->uh_dport
= d
->myport
;
187 return (ur
->buffer_size
);
192 * open, close, shutdown: called from dev_net.c
193 * socktodesc: called by network protocol modules
195 * We only allow one open socket.
198 static int pxe_inited
;
199 static struct iodesc desc
;
204 t_PXENV_UDP_OPEN
*uo
= (void *) pxe_command_buf
;
211 BI_ADD(&bi_netif
, BTINFO_NETIF
, sizeof(bi_netif
));
213 (void)memset(uo
, 0, sizeof(*uo
));
215 uo
->src_ip
= bootplayer
.yip
;
217 pxe_call(PXENV_UDP_OPEN
);
219 if (uo
->status
!= PXENV_STATUS_SUCCESS
) {
220 printf("pxe_netif_probe: PXENV_UDP_OPEN failed: 0x%x\n",
225 memcpy(desc
.myea
, bootplayer
.CAddr
, ETHER_ADDR_LEN
);
228 * Since the PXE BIOS has already done DHCP, make sure we
229 * don't reuse any of its transaction IDs.
231 desc
.xid
= bootplayer
.ident
;
237 pxe_netif_close(int sock
)
239 t_PXENV_UDP_CLOSE
*uc
= (void *) pxe_command_buf
;
243 printf("pxe_netif_close: sock=%d\n", sock
);
248 pxe_call(PXENV_UDP_CLOSE
);
250 if (uc
->status
!= PXENV_STATUS_SUCCESS
)
251 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
267 /*****************************************************************************
268 * PXE initialization and support routines
269 *****************************************************************************/
271 uint16_t pxe_command_buf_seg
;
272 uint16_t pxe_command_buf_off
;
274 extern uint16_t bangpxe_off
, bangpxe_seg
;
275 extern uint16_t pxenv_off
, pxenv_seg
;
277 static struct btinfo_netif bi_netif
;
282 t_PXENV_GET_CACHED_INFO
*gci
= (void *) pxe_command_buf
;
283 t_PXENV_UNDI_GET_NIC_TYPE
*gnt
= (void *) pxe_command_buf
;
291 * Checking for the presence of PXE is a machine-dependent
292 * operation. On the IA-32, this can be done two ways:
294 * Int 0x1a function 0x5650
296 * Scan memory for the !PXE or PXENV+ signatures
298 * We do the latter, since the Int method returns a pointer
299 * to a deprecated structure (PXENV+).
305 for (cp
= (char *)0xa0000; cp
> (char *)0x10000; cp
-= 2) {
307 pxenv
= (pxenv_t
*)cp
;
308 if (MEMSTRCMP(pxenv
->Signature
, "PXENV+"))
311 for (i
= 0, ucp
= (uint8_t *)cp
, cksum
= 0;
312 i
< pxenv
->Length
; i
++)
315 printf("pxe_init: bad cksum (0x%x) "
316 "for PXENV+ at 0x%lx\n", cksum
,
325 if (MEMSTRCMP(pxe
->Signature
, "!PXE"))
328 for (i
= 0, ucp
= (uint8_t *)cp
, cksum
= 0;
329 i
< pxe
->StructLength
; i
++)
332 printf("pxe_init: bad cksum (0x%x) "
333 "for !PXE at 0x%lx\n", cksum
,
340 if (pxe
!= NULL
&& pxenv
!= NULL
)
344 if (pxe
== NULL
&& pxenv
== NULL
) {
345 printf("pxe_init: No PXE BIOS found.\n");
350 printf("PXE BIOS Version %d.%d\n",
351 (pxenv
->Version
>> 8) & 0xff, pxenv
->Version
& 0xff);
352 if (pxenv
->Version
>= 0x0201 && pxe
!= NULL
) {
353 /* 2.1 or greater -- don't use PXENV+ */
359 pxe_call
= pxecall_bangpxe
;
360 bangpxe_off
= pxe
->EntryPointSP
.offset
;
361 bangpxe_seg
= pxe
->EntryPointSP
.segment
;
363 pxe_call
= pxecall_pxenv
;
364 pxenv_off
= pxenv
->RMEntry
.offset
;
365 pxenv_seg
= pxenv
->RMEntry
.segment
;
369 * Pre-compute the segment/offset of the pxe_command_buf
370 * to make things nicer in the low-level calling glue.
372 pxe_command_buf_seg
= VTOPSEG(pxe_command_buf
);
373 pxe_command_buf_off
= VTOPOFF(pxe_command_buf
);
376 * Get the cached info from the server's Discovery reply packet.
378 (void)memset(gci
, 0, sizeof(*gci
));
379 gci
->PacketType
= PXENV_PACKET_TYPE_BINL_REPLY
;
380 pxe_call(PXENV_GET_CACHED_INFO
);
381 if (gci
->Status
!= PXENV_STATUS_SUCCESS
) {
382 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n",
386 pvbcopy((void *)((gci
->Buffer
.segment
<< 4) + gci
->Buffer
.offset
),
387 &bootplayer
, gci
->BufferSize
);
390 * Get network interface information.
392 (void)memset(gnt
, 0, sizeof(*gnt
));
393 pxe_call(PXENV_UNDI_GET_NIC_TYPE
);
395 if (gnt
->Status
!= PXENV_STATUS_SUCCESS
) {
396 printf("pxe_init: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n",
401 switch (gnt
->NicType
) {
404 strncpy(bi_netif
.ifname
, "pxe", sizeof(bi_netif
.ifname
));
405 bi_netif
.bus
= BI_BUS_PCI
;
406 bi_netif
.addr
.tag
= gnt
->info
.pci
.BusDevFunc
;
408 printf("Using %s device at bus %d device %d function %d\n",
409 gnt
->NicType
== PCI_NIC
? "PCI" : "CardBus",
410 (gnt
->info
.pci
.BusDevFunc
>> 8) & 0xff,
411 (gnt
->info
.pci
.BusDevFunc
>> 3) & 0x1f,
412 gnt
->info
.pci
.BusDevFunc
& 0x7);
416 /* XXX Make bootinfo work with this. */
417 printf("Using PnP device at 0x%x\n", gnt
->info
.pnp
.CardSelNum
);
420 printf("Ethernet address %s\n", ether_sprintf(bootplayer
.CAddr
));
428 t_PXENV_UNDI_SHUTDOWN
*shutdown
= (void *) pxe_command_buf
;
430 if (pxe_call
== NULL
)
433 pxe_call(PXENV_UNDI_SHUTDOWN
);
435 if (shutdown
->Status
!= PXENV_STATUS_SUCCESS
)
436 printf("pxe_fini: PXENV_UNDI_SHUTDOWN failed: 0x%x\n",