pxe.c revision 1.4 1 /* $NetBSD: pxe.c,v 1.4 2002/03/26 08:02:05 kanaoka 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 pxe_init();
234
235 if (pxe_call == NULL)
236 return (1);
237
238 bzero(uo, sizeof(*uo));
239
240 uo->src_ip = bootplayer.yip;
241
242 pxe_call(PXENV_UDP_OPEN);
243
244 if (uo->status != PXENV_STATUS_SUCCESS) {
245 printf("pxe_netif_probe: PXENV_UDP_OPEN failed: 0x%x\n",
246 uo->status);
247 pxe_fini();
248 return (1);
249 }
250
251 return (0);
252 }
253
254 void
255 pxe_netif_init(struct iodesc *desc, void *machdep_hint)
256 {
257 struct netif *nif = desc->io_netif;
258
259 bcopy(bootplayer.CAddr, desc->myea, ETHER_ADDR_LEN);
260
261 /*
262 * Since the PXE BIOS has already done DHCP, make sure we
263 * don't reuse any of its transaction IDs.
264 */
265 desc->xid = bootplayer.ident;
266
267 printf("%s%d: Ethernet address %s\n", nif->nif_driver->netif_bname,
268 nif->nif_unit, ether_sprintf(desc->myea));
269 }
270
271 int
272 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
273 {
274
275 return (-1);
276 }
277
278 int
279 pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t tleft)
280 {
281
282 return (-1);
283 }
284
285 void
286 pxe_netif_end(struct netif *nif)
287 {
288 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf;
289
290 uc->status = 0;
291
292 pxe_call(PXENV_UDP_CLOSE);
293
294 if (uc->status != PXENV_STATUS_SUCCESS)
295 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
296 uc->status);
297
298 pxe_fini();
299 }
300
301 /*****************************************************************************
302 * PXE initialization and support routines
303 *****************************************************************************/
304
305 u_int16_t pxe_command_buf_seg;
306 u_int16_t pxe_command_buf_off;
307
308 extern u_int16_t bangpxe_off, bangpxe_seg;
309 extern u_int16_t pxenv_off, pxenv_seg;
310
311 static struct btinfo_netif bi_netif;
312
313 int
314 pxe_init(void)
315 {
316 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf;
317 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf;
318 pxenv_t *pxenv;
319 pxe_t *pxe;
320 char *cp;
321 int i;
322 u_int8_t cksum, *ucp;
323
324 /*
325 * Checking for the presence of PXE is a machine-dependent
326 * operation. On the IA-32, this can be done two ways:
327 *
328 * Int 0x1a function 0x5650
329 *
330 * Scan memory for the !PXE or PXENV+ signatures
331 *
332 * We do the latter, since the Int method returns a pointer
333 * to a deprecated structure (PXENV+).
334 */
335
336 pxenv = NULL;
337 pxe = NULL;
338
339 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) {
340 if (pxenv == NULL) {
341 pxenv = (pxenv_t *)cp;
342 if (bcmp(pxenv->Signature, S_SIZE("PXENV+")))
343 pxenv = NULL;
344 else {
345 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
346 i < pxenv->Length; i++)
347 cksum += ucp[i];
348 if (cksum != 0) {
349 printf("pxe_init: bad cksum (0x%x) "
350 "for PXENV+ at 0x%lx\n", cksum,
351 (u_long) cp);
352 pxenv = NULL;
353 }
354 }
355 }
356
357 if (pxe == NULL) {
358 pxe = (pxe_t *)cp;
359 if (bcmp(pxe->Signature, S_SIZE("!PXE")))
360 pxe = NULL;
361 else {
362 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
363 i < pxe->StructLength; i++)
364 cksum += ucp[i];
365 if (cksum != 0) {
366 printf("pxe_init: bad cksum (0x%x) "
367 "for !PXE at 0x%lx\n", cksum,
368 (u_long) cp);
369 pxe = NULL;
370 }
371 }
372 }
373
374 if (pxe != NULL && pxenv != NULL)
375 break;
376 }
377
378 if (pxe == NULL && pxenv == NULL) {
379 printf("pxe_init: No PXE BIOS found.\n");
380 return (1);
381 }
382
383 if (pxenv != NULL) {
384 printf("PXE BIOS Version %d.%d\n",
385 (pxenv->Version >> 8) & 0xff, pxenv->Version & 0xff);
386 if (pxenv->Version >= 0x0201 && pxe != NULL) {
387 /* 2.1 or greater -- don't use PXENV+ */
388 pxenv = NULL;
389 }
390 }
391
392 if (pxe != NULL) {
393 pxe_call = pxecall_bangpxe;
394 bangpxe_off = pxe->EntryPointSP.offset;
395 bangpxe_seg = pxe->EntryPointSP.segment;
396 } else {
397 pxe_call = pxecall_pxenv;
398 pxenv_off = pxenv->RMEntry.offset;
399 pxenv_seg = pxenv->RMEntry.segment;
400 }
401
402 /*
403 * Pre-compute the segment/offset of the pxe_command_buf
404 * to make things nicer in the low-level calling glue.
405 */
406 pxe_command_buf_seg = VTOPSEG(pxe_command_buf);
407 pxe_command_buf_off = VTOPOFF(pxe_command_buf);
408
409 /*
410 * Get the cached info from the server's Discovery reply packet.
411 */
412 bzero(gci, sizeof(*gci));
413 gci->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
414 pxe_call(PXENV_GET_CACHED_INFO);
415 if (gci->Status != PXENV_STATUS_SUCCESS) {
416 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n",
417 gci->Status);
418 return (1);
419 }
420 pvbcopy((void *)((gci->Buffer.segment << 4) + gci->Buffer.offset),
421 &bootplayer, gci->BufferSize);
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