1 /* $OpenBSD: pxe.c,v 1.8 2022/12/27 07:34:05 jca 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/ip.h>
91 #include <netinet/udp.h>
92
93 #include <lib/libsa/stand.h>
94 #include <lib/libsa/net.h>
95 #include <lib/libsa/bootp.h>
96
97 #include <stand/boot/bootarg.h>
98 #include <machine/biosvar.h>
99
100 #include "pxeboot.h"
101 #include "pxe.h"
102 #include "pxe_netif.h"
103
104 void (*pxe_call)(u_int16_t);
105
106 void pxecall_bangpxe(u_int16_t); /* pxe_call.S */
107 void pxecall_pxenv(u_int16_t); /* pxe_call.S */
108
109 char pxe_command_buf[256];
110
111 BOOTPLAYER bootplayer;
112
113 struct in_addr servip; /* for tftp */ /* XXX init this */
114
115 extern char *bootmac; /* To pass to kernel */
116
117 /* static struct btinfo_netif bi_netif; */
118
119 /*****************************************************************************
120 * This section is a replacement for libsa/udp.c
121 *****************************************************************************/
122
123 /* Caller must leave room for ethernet, ip, and udp headers in front!! */
124 ssize_t
pxesendudp(struct iodesc * d,void * pkt,size_t len)125 pxesendudp(struct iodesc *d, void *pkt, size_t len)
126 {
127 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf;
128
129 uw->status = 0;
130
131 uw->ip = d->destip.s_addr;
132 uw->gw = gateip.s_addr;
133 uw->src_port = d->myport;
134 uw->dst_port = d->destport;
135 uw->buffer_size = len;
136 uw->buffer.segment = VTOPSEG(pkt);
137 uw->buffer.offset = VTOPOFF(pkt);
138
139 pxe_call(PXENV_UDP_WRITE);
140
141 if (uw->status != PXENV_STATUS_SUCCESS) {
142 /* XXX This happens a lot; it shouldn't. */
143 if (uw->status != PXENV_STATUS_FAILURE)
144 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n",
145 uw->status);
146 return -1;
147 }
148
149 return len;
150 }
151
152 /*
153 * Receive a UDP packet and validate it for us.
154 * Caller leaves room for the headers (Ether, IP, UDP).
155 */
156 ssize_t
pxereadudp(struct iodesc * d,void * pkt,size_t len,time_t tleft)157 pxereadudp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
158 {
159 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf;
160 struct udphdr *uh;
161 struct ip *ip;
162
163 uh = (struct udphdr *)pkt - 1;
164 ip = (struct ip *)uh - 1;
165
166 bzero(ur, sizeof(*ur));
167
168 ur->dest_ip = d->myip.s_addr;
169 ur->d_port = d->myport;
170 ur->buffer_size = len;
171 ur->buffer.segment = VTOPSEG(pkt);
172 ur->buffer.offset = VTOPOFF(pkt);
173
174 /* XXX Timeout unused. */
175
176 pxe_call(PXENV_UDP_READ);
177
178 if (ur->status != PXENV_STATUS_SUCCESS) {
179 /* XXX This happens a lot; it shouldn't. */
180 if (ur->status != PXENV_STATUS_FAILURE)
181 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n",
182 ur->status);
183 return -1;
184 }
185
186 ip->ip_src.s_addr = ur->src_ip;
187 uh->uh_sport = ur->s_port;
188 uh->uh_dport = d->myport;
189
190 return ur->buffer_size;
191 }
192
193 /*
194 * netif layer:
195 * open, close, shutdown: called from dev_net.c
196 * socktodesc: called by network protocol modules
197 *
198 * We only allow one open socket.
199 */
200
201 static int pxe_inited;
202 static struct iodesc desc;
203
204 int
pxe_netif_open(void)205 pxe_netif_open(void)
206 {
207 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf;
208
209 #ifdef NETIF_DEBUG
210 printf("pxe_netif_open()\n");
211 #endif
212 if (!pxe_inited) {
213 if (pxe_init(0) != 0)
214 return -1;
215 pxe_inited = 1;
216 }
217 /* BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); */
218
219 bzero(uo, sizeof(*uo));
220
221 uo->src_ip = bootplayer.yip;
222
223 pxe_call(PXENV_UDP_OPEN);
224
225 if (uo->status != PXENV_STATUS_SUCCESS) {
226 printf("\npxe_netif_open: PXENV_UDP_OPEN failed: 0x%x\n",
227 uo->status);
228 return -1;
229 }
230
231 bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN);
232 bootmac = bootplayer.CAddr;
233
234 /*
235 * Since the PXE BIOS has already done DHCP, make sure we
236 * don't reuse any of its transaction IDs.
237 */
238 desc.xid = bootplayer.ident;
239
240 return 0;
241 }
242
243 void
pxe_netif_close(int sock)244 pxe_netif_close(int sock)
245 {
246 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf;
247
248 #ifdef NETIF_DEBUG
249 if (sock != 0)
250 printf("pxe_netif_close: sock=%d\n", sock);
251 #endif
252
253 uc->status = 0;
254
255 pxe_call(PXENV_UDP_CLOSE);
256
257 if (uc->status != PXENV_STATUS_SUCCESS)
258 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
259 uc->status);
260 }
261
262 void
pxe_netif_shutdown(void)263 pxe_netif_shutdown(void)
264 {
265 #ifdef NETIF_DEBUG
266 printf("pxe_netif_shutdown()\n");
267 #endif
268
269 pxe_shutdown();
270 }
271
272 struct iodesc *
pxesocktodesc(int sock)273 pxesocktodesc(int sock)
274 {
275
276 #ifdef NETIF_DEBUG
277 if (sock != 0)
278 return 0;
279 else
280 #endif
281 return &desc;
282 }
283
284 /*****************************************************************************
285 * PXE initialization and support routines
286 *****************************************************************************/
287
288 u_int16_t pxe_command_buf_seg;
289 u_int16_t pxe_command_buf_off;
290
291 extern u_int16_t bangpxe_off, bangpxe_seg;
292 extern u_int16_t pxenv_off, pxenv_seg;
293
294 /* static struct btinfo_netif bi_netif; */
295
296 void
pxeprobe(void)297 pxeprobe(void)
298 {
299 if (!pxe_inited) {
300 if (pxe_init(1) == 0) {
301 pxe_inited = 1;
302 }
303 }
304 }
305
306 int
pxe_init(int quiet)307 pxe_init(int quiet)
308 {
309 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf;
310 pxenv_t *pxenv;
311 pxe_t *pxe;
312 char *cp;
313 int i;
314 u_int8_t cksum, *ucp;
315
316 /*
317 * Checking for the presence of PXE is a machine-dependent
318 * operation. On the IA-32, this can be done two ways:
319 *
320 * Int 0x1a function 0x5650
321 *
322 * Scan memory for the !PXE or PXENV+ signatures
323 *
324 * We do the latter, since the Int method returns a pointer
325 * to a deprecated structure (PXENV+).
326 */
327
328 pxenv = NULL;
329 pxe = NULL;
330
331 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) {
332 if (pxenv == NULL) {
333 pxenv = (pxenv_t *)cp;
334 if (memcmp(pxenv->Signature, S_SIZE("PXENV+")) != 0)
335 pxenv = NULL;
336 else {
337 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
338 i < pxenv->Length; i++)
339 cksum += ucp[i];
340 if (cksum != 0) {
341 printf("\npxe_init: bad cksum (0x%x) "
342 "for PXENV+ at 0x%lx\n", cksum,
343 (u_long) cp);
344 pxenv = NULL;
345 }
346 }
347 }
348
349 if (pxe == NULL) {
350 pxe = (pxe_t *)cp;
351 if (memcmp(pxe->Signature, S_SIZE("!PXE")) != 0)
352 pxe = NULL;
353 else {
354 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0;
355 i < pxe->StructLength; i++)
356 cksum += ucp[i];
357 if (cksum != 0) {
358 printf("pxe_init: bad cksum (0x%x) "
359 "for !PXE at 0x%lx\n", cksum,
360 (u_long) cp);
361 pxe = NULL;
362 }
363 }
364 }
365
366 if (pxe != NULL && pxenv != NULL)
367 break;
368 }
369
370 if (pxe == NULL && pxenv == NULL) {
371 if (!quiet) printf("pxe_init: No PXE BIOS found.\n");
372 return 1;
373 }
374
375 if (pxenv == NULL) {
376 /* assert(pxe != NULL); */
377
378 printf(quiet ? " pxe!" : "PXE present\n");
379 } else { /* pxenv != NULL */
380 int bang = 0;
381
382 if (pxenv->Version >= 0x0201 && pxe != NULL) {
383 /* 2.1 or greater -- don't use PXENV+ */
384 bang = 1;
385 }
386
387 if (quiet) {
388 printf(" pxe%c[%d.%d]",
389 (bang ? '!' : '+'),
390 (pxenv->Version >> 8) & 0xff,
391 pxenv->Version & 0xff);
392 } else {
393 printf("PXE BIOS Version %d.%d\n",
394 (pxenv->Version >> 8) & 0xff,
395 pxenv->Version & 0xff);
396 }
397
398 if (bang) {
399 pxenv = NULL;
400 }
401 }
402
403 if (pxenv == NULL) {
404 pxe_call = pxecall_bangpxe;
405 bangpxe_off = pxe->EntryPointSP.offset;
406 bangpxe_seg = pxe->EntryPointSP.segment;
407 } else {
408 pxe_call = pxecall_pxenv;
409 pxenv_off = pxenv->RMEntry.offset;
410 pxenv_seg = pxenv->RMEntry.segment;
411 }
412
413 /*
414 * Pre-compute the segment/offset of the pxe_command_buf
415 * to make things nicer in the low-level calling glue.
416 */
417 pxe_command_buf_seg = VTOPSEG(pxe_command_buf);
418 pxe_command_buf_off = VTOPOFF(pxe_command_buf);
419
420 /*
421 * Get the cached info from the server's Discovery reply packet.
422 */
423 bzero(gci, sizeof(*gci));
424 gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
425 pxe_call(PXENV_GET_CACHED_INFO);
426
427 if (gci->Status != PXENV_STATUS_SUCCESS) {
428 printf("\npxeinfo: PXENV_GET_CACHED_INFO failed: 0x%x\n",
429 gci->Status);
430 return 1;
431 }
432
433 memcpy(&bootplayer,
434 SEGOFF2FLAT(gci->Buffer.segment, gci->Buffer.offset),
435 gci->BufferSize);
436
437 bcopy(&bootplayer.yip, &myip.s_addr, sizeof(myip.s_addr));
438 bcopy(&bootplayer.sip, &servip.s_addr, sizeof(servip.s_addr));
439
440 /* Compute our "natural" netmask. */
441 if (IN_CLASSA(myip.s_addr))
442 netmask = IN_CLASSA_NET;
443 else if (IN_CLASSB(myip.s_addr))
444 netmask = IN_CLASSB_NET;
445 else
446 netmask = IN_CLASSC_NET;
447
448 return 0;
449 }
450
451 void
pxeinfo(void)452 pxeinfo(void)
453 {
454 u_int8_t *p;
455 #ifdef PXE_DEBUG
456 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf;
457 #endif
458
459 printf(" mac %s", ether_sprintf(bootplayer.CAddr));
460 p = (u_int8_t *)&myip.s_addr;
461 printf(", ip %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
462 p = (u_int8_t *)&servip.s_addr;
463 printf(", server %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
464
465 #ifdef PXE_DEBUG
466 /*
467 * Get network interface information.
468 */
469 bzero(gnt, sizeof(*gnt));
470 pxe_call(PXENV_UNDI_GET_NIC_TYPE);
471
472 if (gnt->Status != PXENV_STATUS_SUCCESS) {
473 printf("\npxeinfo: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n",
474 gnt->Status);
475 return;
476 }
477
478 switch (gnt->NicType) {
479 case PCI_NIC:
480 case CardBus_NIC:
481 /* strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); */
482 /* bi_netif.bus = BI_BUS_PCI; */
483 /* bi_netif.addr.tag = gnt->info.pci.BusDevFunc; */
484
485 printf("\nPXE: Using %s device at bus %d device %d function %d\n",
486 gnt->NicType == PCI_NIC ? "PCI" : "CardBus",
487 (gnt->info.pci.BusDevFunc >> 8) & 0xff,
488 (gnt->info.pci.BusDevFunc >> 3) & 0x1f,
489 gnt->info.pci.BusDevFunc & 0x7);
490 break;
491
492 case PnP_NIC:
493 /* XXX Make bootinfo work with this. */
494 printf("\nPXE: Using PnP device at 0x%x\n",
495 gnt->info.pnp.CardSelNum);
496 }
497 #endif
498 }
499
500 void
pxe_shutdown(void)501 pxe_shutdown(void)
502 {
503 int try;
504 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf;
505 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf;
506 #ifdef PXE_DEBUG
507 t_PXENV_UDP_CLOSE *close = (void *) pxe_command_buf;
508 #endif
509
510 if (pxe_call == NULL)
511 return;
512
513 /* Close any open UDP connections. Ignore return value. */
514 pxe_call(PXENV_UDP_CLOSE);
515 #ifdef PXE_DEBUG
516 printf("pxe_shutdown: PXENV_UDP_CLOSE returned 0x%x\n", close->status);
517 #endif
518
519 /* Sometimes PXENV_UNDI_SHUTDOWN doesn't work at first */
520 for (try = 3; try > 0; try--) {
521 pxe_call(PXENV_UNDI_SHUTDOWN);
522
523 if (shutdown->Status == PXENV_STATUS_SUCCESS)
524 break;
525
526 printf("pxe_shutdown: PXENV_UNDI_SHUTDOWN failed: 0x%x\n",
527 shutdown->Status);
528
529 if (try != 1)
530 sleep(1);
531 }
532
533 /* Have multiple attempts at PXENV_UNLOAD_STACK, too */
534 for (try = 3; try > 0; try--) {
535 pxe_call(PXENV_UNLOAD_STACK);
536
537 if (unload->Status == PXENV_STATUS_SUCCESS)
538 break;
539
540 printf("pxe_shutdown: PXENV_UNLOAD_STACK failed: 0x%x\n",
541 unload->Status);
542
543 if (try != 1)
544 sleep(1);
545 }
546 }
547