1 /* $OpenBSD: pxe.c,v 1.7 2016/03/07 05:32:47 naddy 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 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 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 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 244 pxe_netif_close(sock) 245 int sock; 246 { 247 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf; 248 249 #ifdef NETIF_DEBUG 250 if (sock != 0) 251 printf("pxe_netif_close: sock=%d\n", sock); 252 #endif 253 254 uc->status = 0; 255 256 pxe_call(PXENV_UDP_CLOSE); 257 258 if (uc->status != PXENV_STATUS_SUCCESS) 259 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n", 260 uc->status); 261 } 262 263 void 264 pxe_netif_shutdown(void) 265 { 266 #ifdef NETIF_DEBUG 267 printf("pxe_netif_shutdown()\n"); 268 #endif 269 270 pxe_shutdown(); 271 } 272 273 struct iodesc * 274 pxesocktodesc(sock) 275 int sock; 276 { 277 278 #ifdef NETIF_DEBUG 279 if (sock != 0) 280 return 0; 281 else 282 #endif 283 return &desc; 284 } 285 286 /***************************************************************************** 287 * PXE initialization and support routines 288 *****************************************************************************/ 289 290 u_int16_t pxe_command_buf_seg; 291 u_int16_t pxe_command_buf_off; 292 293 extern u_int16_t bangpxe_off, bangpxe_seg; 294 extern u_int16_t pxenv_off, pxenv_seg; 295 296 /* static struct btinfo_netif bi_netif; */ 297 298 void 299 pxeprobe(void) 300 { 301 if (!pxe_inited) { 302 if (pxe_init(1) == 0) { 303 pxe_inited = 1; 304 } 305 } 306 } 307 308 int 309 pxe_init(int quiet) 310 { 311 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf; 312 pxenv_t *pxenv; 313 pxe_t *pxe; 314 char *cp; 315 int i; 316 u_int8_t cksum, *ucp; 317 318 /* 319 * Checking for the presence of PXE is a machine-dependent 320 * operation. On the IA-32, this can be done two ways: 321 * 322 * Int 0x1a function 0x5650 323 * 324 * Scan memory for the !PXE or PXENV+ signatures 325 * 326 * We do the latter, since the Int method returns a pointer 327 * to a deprecated structure (PXENV+). 328 */ 329 330 pxenv = NULL; 331 pxe = NULL; 332 333 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) { 334 if (pxenv == NULL) { 335 pxenv = (pxenv_t *)cp; 336 if (memcmp(pxenv->Signature, S_SIZE("PXENV+")) != 0) 337 pxenv = NULL; 338 else { 339 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 340 i < pxenv->Length; i++) 341 cksum += ucp[i]; 342 if (cksum != 0) { 343 printf("\npxe_init: bad cksum (0x%x) " 344 "for PXENV+ at 0x%lx\n", cksum, 345 (u_long) cp); 346 pxenv = NULL; 347 } 348 } 349 } 350 351 if (pxe == NULL) { 352 pxe = (pxe_t *)cp; 353 if (memcmp(pxe->Signature, S_SIZE("!PXE")) != 0) 354 pxe = NULL; 355 else { 356 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 357 i < pxe->StructLength; i++) 358 cksum += ucp[i]; 359 if (cksum != 0) { 360 printf("pxe_init: bad cksum (0x%x) " 361 "for !PXE at 0x%lx\n", cksum, 362 (u_long) cp); 363 pxe = NULL; 364 } 365 } 366 } 367 368 if (pxe != NULL && pxenv != NULL) 369 break; 370 } 371 372 if (pxe == NULL && pxenv == NULL) { 373 if (!quiet) printf("pxe_init: No PXE BIOS found.\n"); 374 return 1; 375 } 376 377 if (pxenv == NULL) { 378 /* assert(pxe != NULL); */ 379 380 printf(quiet ? " pxe!" : "PXE present\n"); 381 } else { /* pxenv != NULL */ 382 int bang = 0; 383 384 if (pxenv->Version >= 0x0201 && pxe != NULL) { 385 /* 2.1 or greater -- don't use PXENV+ */ 386 bang = 1; 387 } 388 389 if (quiet) { 390 printf(" pxe%c[%d.%d]", 391 (bang ? '!' : '+'), 392 (pxenv->Version >> 8) & 0xff, 393 pxenv->Version & 0xff); 394 } else { 395 printf("PXE BIOS Version %d.%d\n", 396 (pxenv->Version >> 8) & 0xff, 397 pxenv->Version & 0xff); 398 } 399 400 if (bang) { 401 pxenv = NULL; 402 } 403 } 404 405 if (pxenv == NULL) { 406 pxe_call = pxecall_bangpxe; 407 bangpxe_off = pxe->EntryPointSP.offset; 408 bangpxe_seg = pxe->EntryPointSP.segment; 409 } else { 410 pxe_call = pxecall_pxenv; 411 pxenv_off = pxenv->RMEntry.offset; 412 pxenv_seg = pxenv->RMEntry.segment; 413 } 414 415 /* 416 * Pre-compute the segment/offset of the pxe_command_buf 417 * to make things nicer in the low-level calling glue. 418 */ 419 pxe_command_buf_seg = VTOPSEG(pxe_command_buf); 420 pxe_command_buf_off = VTOPOFF(pxe_command_buf); 421 422 /* 423 * Get the cached info from the server's Discovery reply packet. 424 */ 425 bzero(gci, sizeof(*gci)); 426 gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY; 427 pxe_call(PXENV_GET_CACHED_INFO); 428 429 if (gci->Status != PXENV_STATUS_SUCCESS) { 430 printf("\npxeinfo: PXENV_GET_CACHED_INFO failed: 0x%x\n", 431 gci->Status); 432 return 1; 433 } 434 435 memcpy(&bootplayer, 436 SEGOFF2FLAT(gci->Buffer.segment, gci->Buffer.offset), 437 gci->BufferSize); 438 439 bcopy(&bootplayer.yip, &myip.s_addr, sizeof(myip.s_addr)); 440 bcopy(&bootplayer.sip, &servip.s_addr, sizeof(servip.s_addr)); 441 442 /* Compute our "natural" netmask. */ 443 if (IN_CLASSA(myip.s_addr)) 444 netmask = IN_CLASSA_NET; 445 else if (IN_CLASSB(myip.s_addr)) 446 netmask = IN_CLASSB_NET; 447 else 448 netmask = IN_CLASSC_NET; 449 450 return 0; 451 } 452 453 void 454 pxeinfo(void) 455 { 456 u_int8_t *p; 457 #ifdef PXE_DEBUG 458 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf; 459 #endif 460 461 printf(" mac %s", ether_sprintf(bootplayer.CAddr)); 462 p = (u_int8_t *)&myip.s_addr; 463 printf(", ip %d.%d.%d.%d", p[0], p[1], p[2], p[3]); 464 p = (u_int8_t *)&servip.s_addr; 465 printf(", server %d.%d.%d.%d", p[0], p[1], p[2], p[3]); 466 467 #ifdef PXE_DEBUG 468 /* 469 * Get network interface information. 470 */ 471 bzero(gnt, sizeof(*gnt)); 472 pxe_call(PXENV_UNDI_GET_NIC_TYPE); 473 474 if (gnt->Status != PXENV_STATUS_SUCCESS) { 475 printf("\npxeinfo: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n", 476 gnt->Status); 477 return; 478 } 479 480 switch (gnt->NicType) { 481 case PCI_NIC: 482 case CardBus_NIC: 483 /* strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); */ 484 /* bi_netif.bus = BI_BUS_PCI; */ 485 /* bi_netif.addr.tag = gnt->info.pci.BusDevFunc; */ 486 487 printf("\nPXE: Using %s device at bus %d device %d function %d\n", 488 gnt->NicType == PCI_NIC ? "PCI" : "CardBus", 489 (gnt->info.pci.BusDevFunc >> 8) & 0xff, 490 (gnt->info.pci.BusDevFunc >> 3) & 0x1f, 491 gnt->info.pci.BusDevFunc & 0x7); 492 break; 493 494 case PnP_NIC: 495 /* XXX Make bootinfo work with this. */ 496 printf("\nPXE: Using PnP device at 0x%x\n", 497 gnt->info.pnp.CardSelNum); 498 } 499 #endif 500 } 501 502 void 503 pxe_shutdown(void) 504 { 505 int try; 506 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf; 507 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf; 508 #ifdef PXE_DEBUG 509 t_PXENV_UDP_CLOSE *close = (void *) pxe_command_buf; 510 #endif 511 512 if (pxe_call == NULL) 513 return; 514 515 /* Close any open UDP connections. Ignore return value. */ 516 pxe_call(PXENV_UDP_CLOSE); 517 #ifdef PXE_DEBUG 518 printf("pxe_shutdown: PXENV_UDP_CLOSE returned 0x%x\n", close->status); 519 #endif 520 521 /* Sometimes PXENV_UNDI_SHUTDOWN doesn't work at first */ 522 for (try = 3; try > 0; try--) { 523 pxe_call(PXENV_UNDI_SHUTDOWN); 524 525 if (shutdown->Status == PXENV_STATUS_SUCCESS) 526 break; 527 528 printf("pxe_shutdown: PXENV_UNDI_SHUTDOWN failed: 0x%x\n", 529 shutdown->Status); 530 531 if (try != 1) 532 sleep(1); 533 } 534 535 /* Have multiple attempts at PXENV_UNLOAD_STACK, too */ 536 for (try = 3; try > 0; try--) { 537 pxe_call(PXENV_UNLOAD_STACK); 538 539 if (unload->Status == PXENV_STATUS_SUCCESS) 540 break; 541 542 printf("pxe_shutdown: PXENV_UNLOAD_STACK failed: 0x%x\n", 543 unload->Status); 544 545 if (try != 1) 546 sleep(1); 547 } 548 } 549