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 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(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 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 * 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 297 pxeprobe(void) 298 { 299 if (!pxe_inited) { 300 if (pxe_init(1) == 0) { 301 pxe_inited = 1; 302 } 303 } 304 } 305 306 int 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 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 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