pxe.c revision 1.1 1 /* $NetBSD: pxe.c,v 1.1 2002/02/16 03:37:40 thorpej Exp $ */
2
3 /*
4 * Copyright 2001 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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
23 * written permission.
24 *
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.
36 */
37
38 /*
39 * Copyright (c) 2000 Alfred Perlstein <alfred (at) freebsd.org>
40 * All rights reserved.
41 * Copyright (c) 2000 Paul Saab <ps (at) freebsd.org>
42 * All rights reserved.
43 * Copyright (c) 2000 John Baldwin <jhb (at) freebsd.org>
44 * All rights reserved.
45 *
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
48 * are met:
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.
54 *
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
65 * SUCH DAMAGE.
66 */
67
68 /*
69 * Support for the Intel Preboot Execution Environment (PXE).
70 *
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.
74 */
75
76 #include <sys/param.h>
77 #include <sys/socket.h>
78
79 #ifdef _STANDALONE
80 #include <lib/libkern/libkern.h>
81 #else
82 #include <string.h>
83 #endif
84
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>
91
92 #include <net/if_ether.h>
93
94 #include <lib/libsa/stand.h>
95 #include <lib/libsa/net.h>
96 #include <lib/libsa/netif.h>
97 #include <lib/libsa/bootp.h>
98
99 #include <libi386.h>
100 #include <bootinfo.h>
101
102 #include "pxeboot.h"
103 #include "pxe.h"
104
105 void (*pxe_call)(u_int16_t);
106
107 void pxecall_bangpxe(u_int16_t); /* pxe_call.S */
108 void pxecall_pxenv(u_int16_t); /* pxe_call.S */
109
110 char pxe_command_buf[256];
111
112 BOOTPLAYER bootplayer;
113
114 /*****************************************************************************
115 * This section is a replacement for libsa/udp.c
116 *****************************************************************************/
117
118 /* Caller must leave room for ethernet, ip, and udp headers in front!! */
119 ssize_t
120 sendudp(struct iodesc *d, void *pkt, size_t len)
121 {
122 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf;
123
124 uw->status = 0;
125
126 uw->ip = d->destip.s_addr;
127 uw->gw = gateip.s_addr;
128 uw->src_port = d->myport;
129 uw->dst_port = d->destport;
130 uw->buffer_size = len;
131 uw->buffer.segment = VTOPSEG(pkt);
132 uw->buffer.offset = VTOPOFF(pkt);
133
134 pxe_call(PXENV_UDP_WRITE);
135
136 if (uw->status != PXENV_STATUS_SUCCESS) {
137 /* XXX This happens a lot; it shouldn't. */
138 if (uw->status != PXENV_STATUS_FAILURE)
139 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n",
140 uw->status);
141 return (-1);
142 }
143
144 return (len);
145 }
146
147 /*
148 * Receive a UDP packet and validate it for us.
149 * Caller leaves room for the headers (Ether, IP, UDP).
150 */
151 ssize_t
152 readudp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
153 {
154 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf;
155 struct udphdr *uh;
156 struct ip *ip;
157
158 uh = (struct udphdr *)pkt - 1;
159 ip = (struct ip *)uh - 1;
160
161 bzero(ur, sizeof(*ur));
162
163 ur->dest_ip = d->myip.s_addr;
164 ur->d_port = d->myport;
165 ur->buffer_size = len;
166 ur->buffer.segment = VTOPSEG(pkt);
167 ur->buffer.offset = VTOPOFF(pkt);
168
169 /* XXX Timeout unused. */
170
171 pxe_call(PXENV_UDP_READ);
172
173 if (ur->status != PXENV_STATUS_SUCCESS) {
174 /* XXX This happens a lot; it shouldn't. */
175 if (ur->status != PXENV_STATUS_FAILURE)
176 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n",
177 ur->status);
178 return (-1);
179 }
180
181 ip->ip_src.s_addr = ur->src_ip;
182 uh->uh_sport = ur->s_port;
183 uh->uh_dport = d->myport;
184
185 return (len);
186 }
187
188 /*****************************************************************************
189 * PXE "netif" back-end
190 *****************************************************************************/
191
192 int pxe_netif_match(struct netif *, void *);
193 int pxe_netif_probe(struct netif *, void *);
194 void pxe_netif_init(struct iodesc *, void *);
195 int pxe_netif_get(struct iodesc *, void *, size_t, time_t);
196 int pxe_netif_put(struct iodesc *, void *, size_t);
197 void pxe_netif_end(struct netif *);
198
199 struct netif_stats pxe_netif_stats;
200
201 struct netif_dif pxe0_dif = {
202 0, /* unit */
203 1, /* nsel */
204 &pxe_netif_stats,
205 0,
206 0,
207 };
208
209 struct netif_driver pxe_netif_driver = {
210 "pxe", /* netif_bname */
211 pxe_netif_match, /* match */
212 pxe_netif_probe, /* probe */
213 pxe_netif_init, /* init */
214 pxe_netif_get, /* get */
215 pxe_netif_put, /* put */
216 pxe_netif_end, /* end */
217 &pxe0_dif, /* netif_ifs */
218 1, /* netif_nifs */
219 };
220
221 int
222 pxe_netif_match(struct netif *nif, void *machdep_hint)
223 {
224
225 return (1);
226 }
227
228 int
229 pxe_netif_probe(struct netif *nif, void *machdep_hint)
230 {
231 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf;
232
233 if (pxe_call == NULL)
234 return (1);
235
236 bzero(uo, sizeof(*uo));
237
238 uo->src_ip = bootplayer.yip;
239
240 pxe_call(PXENV_UDP_OPEN);
241
242 if (uo->status != PXENV_STATUS_SUCCESS) {
243 printf("pxe_netif_probe: PXENV_UDP_OPEN failed: 0x%x\n",
244 uo->status);
245 return (1);
246 }
247
248 return (0);
249 }
250
251 void
252 pxe_netif_init(struct iodesc *desc, void *machdep_hint)
253 {
254 struct netif *nif = desc->io_netif;
255
256 bcopy(bootplayer.CAddr, desc->myea, ETHER_ADDR_LEN);
257
258 /*
259 * Since the PXE BIOS has already done DHCP, make sure we
260 * don't reuse any of its transaction IDs.
261 */
262 desc->xid = bootplayer.ident;
263
264 printf("%s%d: Ethernet address %s\n", nif->nif_driver->netif_bname,
265 nif->nif_unit, ether_sprintf(desc->myea));
266 }
267
268 int
269 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
270 {
271
272 return (-1);
273 }
274
275 int
276 pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t tleft)
277 {
278
279 return (-1);
280 }
281
282 void
283 pxe_netif_end(struct netif *nif)
284 {
285 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf;
286
287 uc->status = 0;
288
289 pxe_call(PXENV_UDP_CLOSE);
290
291 if (uc->status != PXENV_STATUS_SUCCESS)
292 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
293 uc->status);
294 }
295
296 /*****************************************************************************
297 * PXE initialization and support routines
298 *****************************************************************************/
299
300 u_int16_t pxe_command_buf_seg;
301 u_int16_t pxe_command_buf_off;
302
303 extern u_int16_t bangpxe_off, bangpxe_seg;
304 extern u_int16_t pxenv_off, pxenv_seg;
305
306 static struct btinfo_netif bi_netif;
307
308 int
309 pxe_init(void)
310 {
311 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf;
312 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf;
313 pxenv_t *pxenv;
314 pxe_t *pxe;
315 char *cp;
316 int i;
317 u_int8_t cksum, *ucp;
318
319 /*
320 * Checking for the presence of PXE is a machine-dependent
321 * operation. On the IA-32, this can be done two ways:
322 *
323 * Int 0x1a function 0x5650
324 *
325 * Scan memory for the !PXE or PXENV+ signatures
326 *
327 * We do the latter, since the Int method returns a pointer
328 * to a deprecated structure (PXENV+).
329 */
330
331 pxenv = NULL;
332 pxe = NULL;
333
334 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 16) {
335 if (pxenv == NULL) {
336 pxenv = (pxenv_t *)cp;
337 if (bcmp(pxenv->Signature, S_SIZE("PXENV+")))
338 pxenv = NULL;
339 else {
340 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
341 i < pxenv->Length; i++)
342 cksum += ucp[i];
343 if (cksum != 0) {
344 printf("pxe_init: bad cksum (0x%x) "
345 "for PXENV+ at 0x%lx\n", cksum,
346 (u_long) cp);
347 pxenv = NULL;
348 }
349 }
350 }
351
352 if (pxe == NULL) {
353 pxe = (pxe_t *)cp;
354 if (bcmp(pxe->Signature, S_SIZE("!PXE")))
355 pxe = NULL;
356 else {
357 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
358 i < pxe->StructLength; i++)
359 cksum += ucp[i];
360 if (cksum != 0) {
361 printf("pxe_init: bad cksum (0x%x) "
362 "for !PXE at 0x%lx\n", cksum,
363 (u_long) cp);
364 pxe = NULL;
365 }
366 }
367 }
368
369 if (pxe != NULL && pxenv != NULL)
370 break;
371 }
372
373 if (pxe == NULL && pxenv == NULL) {
374 printf("pxe_init: No PXE BIOS found.\n");
375 return (1);
376 }
377
378 if (pxenv != NULL) {
379 printf("PXE BIOS Version %d.%d\n",
380 (pxenv->Version >> 8) & 0xff, pxenv->Version & 0xff);
381 if (pxenv->Version >= 0x0201 && pxe != NULL) {
382 /* 2.1 or greater -- don't use PXENV+ */
383 pxenv = NULL;
384 }
385 }
386
387 if (pxe != NULL) {
388 pxe_call = pxecall_bangpxe;
389 bangpxe_off = pxe->EntryPointSP.offset;
390 bangpxe_seg = pxe->EntryPointSP.segment;
391 } else {
392 pxe_call = pxecall_pxenv;
393 pxenv_off = pxenv->RMEntry.offset;
394 pxenv_seg = pxenv->RMEntry.segment;
395 }
396
397 /*
398 * Pre-compute the segment/offset of the pxe_command_buf
399 * to make things nicer in the low-level calling glue.
400 */
401 pxe_command_buf_seg = VTOPSEG(pxe_command_buf);
402 pxe_command_buf_off = VTOPOFF(pxe_command_buf);
403
404 /*
405 * Get the cached info from the server's Discovery reply packet.
406 */
407 bzero(gci, sizeof(*gci));
408 gci->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
409 pxe_call(PXENV_GET_CACHED_INFO);
410 if (gci->Status != PXENV_STATUS_SUCCESS) {
411 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n",
412 gci->Status);
413 return (1);
414 }
415 pvbcopy((void *)((gci->Buffer.segment << 4) + gci->Buffer.offset),
416 &bootplayer, gci->BufferSize);
417
418 /*
419 * Make sure that bootp() presents us as a PXE client.
420 */
421 bootp_flags |= BOOTP_PXE;
422
423 /*
424 * Get network interface information.
425 */
426 bzero(gnt, sizeof(*gnt));
427 pxe_call(PXENV_UNDI_GET_NIC_TYPE);
428
429 if (gnt->Status != PXENV_STATUS_SUCCESS) {
430 printf("pxe_init: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n",
431 gnt->Status);
432 return (0);
433 }
434
435 switch (gnt->NicType) {
436 case PCI_NIC:
437 case CardBus_NIC:
438 strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname));
439 bi_netif.bus = BI_BUS_PCI;
440 bi_netif.addr.tag = gnt->info.pci.BusDevFunc;
441
442 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif));
443
444 printf("Using %s device at bus %d device %d function %d\n",
445 gnt->NicType == PCI_NIC ? "PCI" : "CardBus",
446 (gnt->info.pci.BusDevFunc >> 8) & 0xff,
447 (gnt->info.pci.BusDevFunc >> 3) & 0x1f,
448 gnt->info.pci.BusDevFunc & 0x7);
449 break;
450
451 case PnP_NIC:
452 /* XXX Make bootinfo work with this. */
453 printf("Using PnP device at 0x%x\n", gnt->info.pnp.CardSelNum);
454 }
455
456 return (0);
457 }
458
459 void
460 pxe_fini(void)
461 {
462 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf;
463 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf;
464
465 if (pxe_call == NULL)
466 return;
467
468 pxe_call(PXENV_UNDI_SHUTDOWN);
469
470 if (shutdown->Status != PXENV_STATUS_SUCCESS)
471 printf("pxe_fini: PXENV_UNDI_SHUTDOWN failed: 0x%x\n",
472 shutdown->Status);
473
474 pxe_call(PXENV_UNLOAD_STACK);
475
476 if (unload->Status != PXENV_STATUS_SUCCESS)
477 printf("pxe_fini: PXENV_UNLOAD_STACK failed: 0x%x\n",
478 unload->Status);
479 }
480