1 /** $MirOS: src/sys/arch/i386/stand/libsa/pxe.c,v 1.22 2009/10/27 13:54:28 tg Exp $ */
2 /* $OpenBSD: pxe.c,v 1.5 2007/07/27 17:46:56 tom Exp $ */
3 /* $NetBSD: pxe.c,v 1.5 2003/03/11 18:29:00 drochner Exp $ */
4
5 /*
6 * Copyright 2001 Wasabi Systems, Inc.
7 * All rights reserved.
8 *
9 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed for the NetBSD Project by
22 * Wasabi Systems, Inc.
23 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
24 * or promote products derived from this software without specific prior
25 * written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 /*
41 * Copyright (c) 2009 Thorsten Glaser
42 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
43 * All rights reserved.
44 * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
45 * All rights reserved.
46 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
47 * All rights reserved.
48 *
49 * Redistribution and use in source and binary forms, with or without
50 * modification, are permitted provided that the following conditions
51 * are met:
52 * 1. Redistributions of source code must retain the above copyright
53 * notice, this list of conditions and the following disclaimer.
54 * 2. Redistributions in binary form must reproduce the above copyright
55 * notice, this list of conditions and the following disclaimer in the
56 * documentation and/or other materials provided with the distribution.
57 *
58 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * SUCH DAMAGE.
69 */
70
71 /*
72 * Support for the Intel Preboot Execution Environment (PXE).
73 *
74 * PXE provides a UDP implementation as well as a UNDI network device
75 * driver. UNDI is much more complicated to use than PXE UDP, so we
76 * use PXE UDP as a cheap and easy way to get PXE support.
77 */
78
79 #include <sys/param.h>
80 #include <sys/socket.h>
81
82 #ifdef _STANDALONE
83 #include <sys/slibkern.h>
84 #else
85 #include <string.h>
86 #endif
87
88 #include <net/if.h>
89
90 #include <netinet/in.h>
91 #include <netinet/if_ether.h>
92 #include <netinet/in_systm.h>
93 #include <netinet/ip.h>
94 #include <netinet/ip_var.h>
95 #include <netinet/udp.h>
96 #include <netinet/udp_var.h>
97
98 #include <libsa.h>
99 #include <lib/libsa/net.h>
100 #include <lib/libsa/bootp.h>
101
102 #include <stand/boot/bootarg.h>
103
104 #include "pxeboot.h"
105 #include "pxe.h"
106 #include "pxe_netif.h"
107
108 #define PXE_LOUD 0
109
110 extern uint32_t pxe_bang;
111 extern uint32_t pxe_plus;
112 extern uint32_t pxecall_addr;
113
114 int have_pxe = -1;
115
116 int (*pxe_call)(u_int16_t);
117 int Xpxe(void);
118
119 extern int pxecall_bang(u_int16_t);
120 extern int pxecall_plus(u_int16_t);
121
122 extern char pxe_command_buf[256];
123
124 BOOTPLAYER bootplayer;
125
126 struct in_addr servip; /* for tftp */ /* XXX init this */
127
128 extern char *bootmac; /* To pass to kernel */
129
130 /* static struct btinfo_netif bi_netif; */
131
132 /*****************************************************************************
133 * This section is a replacement for libsa/udp.c
134 *****************************************************************************/
135
136 /* Caller must leave room for ethernet, ip, and udp headers in front!! */
137 ssize_t
pxesendudp(struct iodesc * d,void * pkt,size_t len)138 pxesendudp(struct iodesc *d, void *pkt, size_t len)
139 {
140 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf;
141
142 uw->status = 0;
143
144 uw->ip = d->destip.s_addr;
145 uw->gw = gateip.s_addr;
146 uw->src_port = d->myport;
147 uw->dst_port = d->destport;
148 uw->buffer_size = len;
149 uw->buffer.segment = VTOPSEG(pkt);
150 uw->buffer.offset = VTOPOFF(pkt);
151
152 pxe_call(PXENV_UDP_WRITE);
153
154 if (uw->status != PXENV_STATUS_SUCCESS) {
155 /* XXX This happens a lot; it shouldn't. */
156 if (uw->status != PXENV_STATUS_FAILURE)
157 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n",
158 uw->status);
159 return -1;
160 }
161
162 return len;
163 }
164
165 /*
166 * Receive a UDP packet and validate it for us.
167 * Caller leaves room for the headers (Ether, IP, UDP).
168 */
169 ssize_t
pxereadudp(struct iodesc * d,void * pkt,size_t len,time_t tleft)170 pxereadudp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
171 {
172 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf;
173 struct udphdr *uh;
174 struct ip *ip;
175
176 uh = (struct udphdr *)pkt - 1;
177 ip = (struct ip *)uh - 1;
178
179 bzero(ur, sizeof(*ur));
180
181 ur->dest_ip = d->myip.s_addr;
182 ur->d_port = d->myport;
183 ur->buffer_size = len;
184 ur->buffer.segment = VTOPSEG(pkt);
185 ur->buffer.offset = VTOPOFF(pkt);
186
187 /* XXX Timeout unused. */
188
189 pxe_call(PXENV_UDP_READ);
190
191 if (ur->status != PXENV_STATUS_SUCCESS) {
192 /* XXX This happens a lot; it shouldn't. */
193 if (ur->status != PXENV_STATUS_FAILURE)
194 printf("readudp: PXENV_UDP_READ_failed: 0x%x\n",
195 ur->status);
196 return -1;
197 }
198
199 ip->ip_src.s_addr = ur->src_ip;
200 uh->uh_sport = ur->s_port;
201 uh->uh_dport = d->myport;
202
203 return ur->buffer_size;
204 }
205
206 /*
207 * netif layer:
208 * open, close, shutdown: called from dev_net.c
209 * socktodesc: called by network protocol modules
210 *
211 * We only allow one open socket.
212 */
213
214 static struct iodesc desc;
215
216 int
pxe_netif_open()217 pxe_netif_open()
218 {
219 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf;
220
221 #ifdef NETIF_DEBUG
222 printf("pxe_netif_open()\n");
223 #endif
224 if (have_pxe < 0)
225 pxe_init(0);
226 if (!have_pxe)
227 return (-1);
228
229 /* BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); */
230
231 bzero(uo, sizeof(*uo));
232
233 uo->src_ip = bootplayer.yip;
234
235 pxe_call(PXENV_UDP_OPEN);
236
237 if (uo->status != PXENV_STATUS_SUCCESS) {
238 printf("\npxe_netif_open: PXENV_UDP_OPEN failed: 0x%x\n",
239 uo->status);
240 return -1;
241 }
242
243 bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN);
244 bootmac = bootplayer.CAddr;
245
246 /*
247 * Since the PXE BIOS has already done DHCP, make sure we
248 * don't reuse any of its transaction IDs.
249 */
250 desc.xid = bootplayer.ident;
251
252 return 0;
253 }
254
255 void
pxe_netif_close(sock)256 pxe_netif_close(sock)
257 int sock;
258 {
259 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf;
260
261 #ifdef NETIF_DEBUG
262 if (sock != 0)
263 printf("pxe_netif_close: sock=%d\n", sock);
264 #endif
265
266 uc->status = 0;
267
268 pxe_call(PXENV_UDP_CLOSE);
269
270 if (uc->status != PXENV_STATUS_SUCCESS)
271 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n",
272 uc->status);
273 }
274
275 void
pxe_netif_shutdown()276 pxe_netif_shutdown()
277 {
278 #ifdef NETIF_DEBUG
279 printf("pxe_netif_shutdown()\n");
280 #endif
281
282 pxe_shutdown();
283 }
284
285 struct iodesc *
pxesocktodesc(sock)286 pxesocktodesc(sock)
287 int sock;
288 {
289
290 #ifdef NETIF_DEBUG
291 if (sock != 0)
292 return 0;
293 else
294 #endif
295 return &desc;
296 }
297
298 /*****************************************************************************
299 * PXE initialization and support routines
300 *****************************************************************************/
301
302 /* static struct btinfo_netif bi_netif; */
303
304 void
pxeprobe(void)305 pxeprobe(void)
306 {
307 if (have_pxe < 0) {
308 pxe_init(1);
309 }
310 }
311
312 #define try_pxenv(cp) do { \
313 pxenv = (pxenv_t *)cp; \
314 if (pxenv->Signature[0] == 'P' && \
315 pxenv->Signature[1] == 'X' && \
316 pxenv->Signature[2] == 'E' && \
317 pxenv->Signature[3] == 'N' && \
318 pxenv->Signature[4] == 'V' && \
319 pxenv->Signature[5] == '+') { \
320 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; \
321 i < pxenv->Length; i++) \
322 cksum += ucp[i]; \
323 if (cksum != 0) { \
324 printf("\npxe_init: bad cksum (0x%x) " \
325 "for PXENVplus at 0x%lx\n", cksum, \
326 (u_long) cp); \
327 pxenv = NULL; \
328 } \
329 } else \
330 pxenv = NULL; \
331 } while (/* CONSTCOND */ 0)
332
333 #define try_pxe(cp) do { \
334 pxe = (pxe_t *)cp; \
335 if (pxe->Signature[0] == '!' && \
336 pxe->Signature[1] == 'P' && \
337 pxe->Signature[2] == 'X' && \
338 pxe->Signature[3] == 'E') { \
339 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; \
340 i < pxe->StructLength; i++) \
341 cksum += ucp[i]; \
342 if (cksum != 0) { \
343 printf("pxe_init: bad cksum (0x%x) " \
344 "for bangPXE at 0x%lx\n", cksum, \
345 (u_long) cp); \
346 pxe = NULL; \
347 } \
348 } else \
349 pxe = NULL; \
350 } while (/* CONSTCOND */ 0)
351
352 int
pxe_init(int quiet)353 pxe_init(int quiet)
354 {
355 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf;
356 pxenv_t *pxenv;
357 pxe_t *pxe;
358 char *cp;
359 int i;
360 u_int8_t cksum, *ucp;
361
362 if (have_pxe >= 0) {
363 #if PXE_LOUD
364 printf(" {no:have_pxe=%d}", have_pxe);
365 #endif
366 return (have_pxe == 0 ? 1 : 0);
367 }
368 have_pxe = 0;
369 if (!(i386_biosflags & 4)) {
370 #if PXE_LOUD
371 printf(" {pxe_unlikely}");
372 #endif
373 return (1); /* not “probably” booted from PXE */
374 }
375
376 /*
377 * Checking for the presence of PXE is a machine-dependent
378 * operation. On the IA-32, this can be done two ways:
379 *
380 * Int 0x1a function 0x5650
381 *
382 * Scan memory for the !PXE or PXENV+ signatures
383 *
384 * We do the latter, since the Int method returns a pointer
385 * to a deprecated structure (PXENV+).
386 */
387
388 pxenv = NULL;
389 pxe = NULL;
390
391 if (pxe_plus) {
392 #if PXE_LOUD
393 printf(" {try:plus=%X:%X}", pxe_plus >> 16,
394 pxe_plus & 0xFFFF);
395 #endif
396 try_pxenv(PTOV(pxe_plus >> 16, pxe_plus & 0xFFFF));
397 }
398
399 if (pxe_bang) {
400 #if PXE_LOUD
401 printf(" {try:bang=%X:%X}", pxe_bang >> 16,
402 pxe_bang & 0xFFFF);
403 #endif
404 try_pxe(PTOV(pxe_bang >> 16, pxe_bang & 0xFFFF));
405 }
406
407 if (pxe_plus == pxe_bang && (pxe || pxenv)) {
408 #if PXE_LOUD
409 printf(" {pxelinux}");
410 #endif
411 goto got_one; /* probably from SYSLINUX */
412 }
413
414 if (!(i386_dosdev & 1)) {
415 #if PXE_LOUD
416 printf(" {pxe_disabled}");
417 #endif
418 return (1); /* PXE scan disabled */
419 }
420
421 for (cp = (char *)0x90000; cp >= (char *)0x10000; cp -= 0x10) {
422 if (pxenv == NULL)
423 try_pxenv(cp);
424 if (pxe == NULL)
425 try_pxe(cp);
426
427 if (pxe != NULL && pxenv != NULL)
428 break;
429 }
430
431 if (pxe == NULL && pxenv == NULL) {
432 #if !PXE_LOUD
433 if (!quiet)
434 #endif
435 printf("pxe_init: No PXE BIOS found.\n");
436 return 1;
437 }
438 #if PXE_LOUD
439 printf(" {scan:pxenv=%X,pxe=%X}", (unsigned)pxenv, (unsigned)pxe);
440 #endif
441
442 got_one:
443 if (pxenv && (pxenv != PTOV(pxe_plus >> 16, pxe_plus & 0xFFFF)))
444 pxe_plus = (((uint32_t)pxenv & 0xFFFF0) << 12) |
445 ((uint32_t)pxenv & 0x0000F);
446 if (pxe && (pxe != PTOV(pxe_bang >> 16, pxe_bang & 0xFFFF)))
447 pxe_bang = (((uint32_t)pxe & 0xFFFF0) << 12) |
448 ((uint32_t)pxe & 0x0000F);
449
450 if (pxenv == NULL) {
451 /* assert(pxe != NULL); */
452
453 printf(quiet ? " pxe!" : "PXE present\n");
454 } else { /* pxenv != NULL */
455 int bang = 0;
456
457 if (pxenv->Version >= 0x0201 && pxe != NULL) {
458 /* 2.1 or greater -- don't use PXENV+ */
459 bang = 1;
460 }
461
462 if (quiet) {
463 printf(" pxe%c[%d.%d]",
464 (bang ? '!' : '+'),
465 (pxenv->Version >> 8) & 0xff,
466 pxenv->Version & 0xff);
467 } else {
468 printf("PXE BIOS Version %d.%d\n",
469 (pxenv->Version >> 8) & 0xff,
470 pxenv->Version & 0xff);
471 }
472
473 if (bang) {
474 pxenv = NULL;
475 }
476 }
477
478 if (pxenv == NULL) {
479 pxe_call = pxecall_bang;
480 pxecall_addr = ((uint32_t)pxe->EntryPointSP.segment << 16) |
481 pxe->EntryPointSP.offset;
482 } else {
483 pxe_call = pxecall_plus;
484 pxecall_addr = ((uint32_t)pxenv->RMEntry.segment << 16) |
485 pxenv->RMEntry.offset;
486 }
487
488 /*
489 * Get the cached info from the server's Discovery reply packet.
490 */
491 bzero(gci, sizeof(*gci));
492 gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
493 pxe_call(PXENV_GET_CACHED_INFO);
494
495 if (gci->Status != PXENV_STATUS_SUCCESS) {
496 printf("\npxeinfo: PXENV_GET_CACHED_INFO failed: 0x%x\n",
497 gci->Status);
498 return 1;
499 }
500
501 memcpy(&bootplayer, PTOV(gci->Buffer.segment, gci->Buffer.offset),
502 gci->BufferSize);
503
504 bcopy(&bootplayer.yip, &myip.s_addr, sizeof(myip.s_addr));
505 bcopy(&bootplayer.sip, &servip.s_addr, sizeof(servip.s_addr));
506
507 /* Compute our "natural" netmask. */
508 if (IN_CLASSA(myip.s_addr))
509 netmask = IN_CLASSA_NET;
510 else if (IN_CLASSB(myip.s_addr))
511 netmask = IN_CLASSB_NET;
512 else
513 netmask = IN_CLASSC_NET;
514
515 have_pxe = 1;
516 return 0;
517 }
518
519 void
pxeinfo(void)520 pxeinfo(void)
521 {
522 u_int8_t *p;
523 #ifdef PXE_DEBUG
524 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf;
525 #endif
526
527 if (have_pxe <= 0) {
528 printf(" %s\n", have_pxe ? "err" : "none");
529 return;
530 }
531
532 printf(" %s %X mac %s", pxe_call == pxecall_bang ? "!PXE" : "PXENV+",
533 (unsigned)pxecall_addr, ether_sprintf(bootplayer.CAddr));
534 p = (u_int8_t *)&myip.s_addr;
535 printf(", ip %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
536 p = (u_int8_t *)&servip.s_addr;
537 printf(", server %d.%d.%d.%d", p[0], p[1], p[2], p[3]);
538
539 #ifdef PXE_DEBUG
540 /*
541 * Get network interface information.
542 */
543 bzero(gnt, sizeof(*gnt));
544 pxe_call(PXENV_UNDI_GET_NIC_TYPE);
545
546 if (gnt->Status != PXENV_STATUS_SUCCESS) {
547 printf("\npxeinfo: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n",
548 gnt->Status);
549 return;
550 }
551
552 switch (gnt->NicType) {
553 case PCI_NIC:
554 case CardBus_NIC:
555 /* strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); */
556 /* bi_netif.bus = BI_BUS_PCI; */
557 /* bi_netif.addr.tag = gnt->info.pci.BusDevFunc; */
558
559 printf("\nPXE: Using %s device at bus %d device %d function %d\n",
560 gnt->NicType == PCI_NIC ? "PCI" : "CardBus",
561 (gnt->info.pci.BusDevFunc >> 8) & 0xff,
562 (gnt->info.pci.BusDevFunc >> 3) & 0x1f,
563 gnt->info.pci.BusDevFunc & 0x7);
564 break;
565
566 case PnP_NIC:
567 /* XXX Make bootinfo work with this. */
568 printf("\nPXE: Using PnP device at 0x%x\n",
569 gnt->info.pnp.CardSelNum);
570 }
571 #endif
572 }
573
574 void
pxe_shutdown(void)575 pxe_shutdown(void)
576 {
577 int try;
578 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf;
579 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf;
580 #ifdef PXE_DEBUG
581 t_PXENV_UDP_CLOSE *close = (void *) pxe_command_buf;
582 #endif
583
584 if (pxe_call == NULL)
585 return;
586
587 /* Close any open UDP connections. Ignore return value. */
588 pxe_call(PXENV_UDP_CLOSE);
589 #ifdef PXE_DEBUG
590 printf("pxe_shutdown: PXENV_UDP_CLOSE returned 0x%x\n", close->status);
591 #endif
592
593 /* Sometimes PXENV_UNDI_SHUTDOWN doesn't work at first */
594 for (try = 3; try > 0; try--) {
595 pxe_call(PXENV_UNDI_SHUTDOWN);
596
597 if (shutdown->Status == PXENV_STATUS_SUCCESS)
598 break;
599
600 printf("pxe_shutdown: PXENV_UNDI_SHUTDOWN failed: 0x%x\n",
601 shutdown->Status);
602
603 if (try != 1)
604 sleep(1);
605 }
606
607 /* Have multiple attempts at PXENV_UNLOAD_STACK, too */
608 for (try = 3; try > 0; try--) {
609 pxe_call(PXENV_UNLOAD_STACK);
610
611 if (unload->Status == PXENV_STATUS_SUCCESS)
612 break;
613
614 printf("pxe_shutdown: PXENV_UNLOAD_STACK failed: 0x%x\n",
615 unload->Status);
616
617 if (try != 1)
618 sleep(1);
619 }
620 }
621
622 extern void pxecheck(void);
623
624 int
Xpxe(void)625 Xpxe(void)
626 {
627 have_pxe = -1;
628 #if 0
629 pxe_plus = pxe_bang = 0; /* override potentially passed stuff */
630 #endif
631 i386_biosflags |= 4; /* no pxe_unlikely */
632 i386_dosdev |= 1; /* no pxe_disabled */
633
634 if (pxe_init(0))
635 printf("Sorry, scan for PXE yielded no result.\n");
636 else {
637 printf("Yes, we have found a PXE boot ROM.\n=>");
638 i386_biosflags &= ~4; /* tftp is not the boot device */
639 pxecheck();
640 pxeinfo();
641 putchar('\n');
642 }
643 return (0);
644 }
645