1 /*- 2 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> 3 * Copyright (c) 2000 Paul Saab <ps@freebsd.org> 4 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/sys/boot/i386/libi386/pxe.c,v 1.20 2003/08/25 23:28:31 obrien Exp $ 29 */ 30 31 #include <sys/param.h> 32 #include <stand.h> 33 #include <string.h> 34 #include <stdarg.h> 35 36 #include <netinet/in_systm.h> 37 #include <netinet/in.h> 38 #include <netinet/udp.h> 39 #include <netinet/ip.h> 40 41 #include <net.h> 42 #include <netif.h> 43 #include <nfsv2.h> 44 #include <iodesc.h> 45 46 #include <bootp.h> 47 #include <bootstrap.h> 48 #include "btxv86.h" 49 #include "pxe.h" 50 51 /* 52 * Allocate the PXE buffers statically instead of sticking grimy fingers into 53 * BTX's private data area. The scratch buffer is used to send information to 54 * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. 55 */ 56 #define PXE_BUFFER_SIZE 0x2000 57 #define PXE_TFTP_BUFFER_SIZE 512 58 static char scratch_buffer[PXE_BUFFER_SIZE]; 59 static char data_buffer[PXE_BUFFER_SIZE]; 60 61 static pxenv_t *pxenv_p = NULL; /* PXENV+ */ 62 static pxe_t *pxe_p = NULL; /* !PXE */ 63 static BOOTPLAYER bootplayer; /* PXE Cached information. */ 64 65 static int pxe_debug = 0; 66 static int pxe_sock = -1; 67 static int pxe_opens = 0; 68 static int bugged_bios_pxe = 0; 69 70 void pxe_enable(void *pxeinfo); 71 static void (*pxe_call)(int func); 72 static void pxenv_call(int func); 73 static void bangpxe_call(int func); 74 75 static int pxe_init(void); 76 static int pxe_strategy(void *devdata, int flag, daddr_t dblk, 77 size_t size, char *buf, size_t *rsize); 78 static int pxe_open(struct open_file *f, ...); 79 static int pxe_close(struct open_file *f); 80 static void pxe_print(int verbose); 81 static void pxe_cleanup(void); 82 static void pxe_setnfshandle(char *rootpath); 83 84 static void pxe_perror(int error); 85 static int pxe_netif_match(struct netif *nif, void *machdep_hint); 86 static int pxe_netif_probe(struct netif *nif, void *machdep_hint); 87 static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 88 static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, 89 time_t timeout); 90 static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 91 static void pxe_netif_end(struct netif *nif); 92 93 extern struct netif_stats pxe_st[]; 94 extern u_int16_t __bangpxeseg; 95 extern u_int16_t __bangpxeoff; 96 extern void __bangpxeentry(void); 97 extern u_int16_t __pxenvseg; 98 extern u_int16_t __pxenvoff; 99 extern void __pxenventry(void); 100 101 struct netif_dif pxe_ifs[] = { 102 /* dif_unit dif_nsel dif_stats dif_private */ 103 {0, 1, &pxe_st[0], 0} 104 }; 105 106 struct netif_stats pxe_st[NENTS(pxe_ifs)]; 107 108 struct netif_driver pxenetif = { 109 "pxenet", 110 pxe_netif_match, 111 pxe_netif_probe, 112 pxe_netif_init, 113 pxe_netif_get, 114 pxe_netif_put, 115 pxe_netif_end, 116 pxe_ifs, 117 NENTS(pxe_ifs) 118 }; 119 120 struct netif_driver *netif_drivers[] = { 121 &pxenetif, 122 NULL 123 }; 124 125 struct devsw pxedisk = { 126 "pxe", 127 DEVT_NET, 128 pxe_init, 129 pxe_strategy, 130 pxe_open, 131 pxe_close, 132 noioctl, 133 pxe_print, 134 pxe_cleanup 135 }; 136 137 /* 138 * This function is called by the loader to enable PXE support if we 139 * are booted by PXE. The passed in pointer is a pointer to the 140 * PXENV+ structure. 141 */ 142 void 143 pxe_enable(void *pxeinfo) 144 { 145 pxenv_p = (pxenv_t *)pxeinfo; 146 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 147 pxenv_p->PXEPtr.offset); 148 pxe_call = NULL; 149 } 150 151 /* 152 * return true if pxe structures are found/initialized, 153 * also figures out our IP information via the pxe cached info struct 154 */ 155 static int 156 pxe_init(void) 157 { 158 t_PXENV_GET_CACHED_INFO *gci_p; 159 int counter; 160 uint8_t checksum; 161 uint8_t *checkptr; 162 163 if(pxenv_p == NULL) 164 return (0); 165 166 /* look for "PXENV+" */ 167 if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { 168 pxenv_p = NULL; 169 return (0); 170 } 171 172 /* make sure the size is something we can handle */ 173 if (pxenv_p->Length > sizeof(*pxenv_p)) { 174 printf("PXENV+ structure too large, ignoring\n"); 175 pxenv_p = NULL; 176 return (0); 177 } 178 179 /* 180 * do byte checksum: 181 * add up each byte in the structure, the total should be 0 182 */ 183 checksum = 0; 184 checkptr = (uint8_t *) pxenv_p; 185 for (counter = 0; counter < pxenv_p->Length; counter++) 186 checksum += *checkptr++; 187 if (checksum != 0) { 188 printf("PXENV+ structure failed checksum, ignoring\n"); 189 pxenv_p = NULL; 190 return (0); 191 } 192 193 194 /* 195 * PXENV+ passed, so use that if !PXE is not available or 196 * the checksum fails. 197 */ 198 pxe_call = pxenv_call; 199 if (pxenv_p->Version >= 0x0200) { 200 for (;;) { 201 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 202 pxe_p = NULL; 203 break; 204 } 205 checksum = 0; 206 checkptr = (uint8_t *)pxe_p; 207 for (counter = 0; counter < pxe_p->StructLength; 208 counter++) 209 checksum += *checkptr++; 210 if (checksum != 0) { 211 pxe_p = NULL; 212 break; 213 } 214 pxe_call = bangpxe_call; 215 break; 216 } 217 } 218 219 printf("\nPXE version %d.%d, real mode entry point ", 220 (uint8_t) (pxenv_p->Version >> 8), 221 (uint8_t) (pxenv_p->Version & 0xFF)); 222 if (pxe_call == bangpxe_call) 223 printf("@%04x:%04x\n", 224 pxe_p->EntryPointSP.segment, 225 pxe_p->EntryPointSP.offset); 226 else 227 printf("@%04x:%04x\n", 228 pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); 229 230 gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 231 bzero(gci_p, sizeof(*gci_p)); 232 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 233 pxe_call(PXENV_GET_CACHED_INFO); 234 if (gci_p->Status != 0) { 235 pxe_perror(gci_p->Status); 236 pxe_p = NULL; 237 return (0); 238 } 239 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 240 &bootplayer, gci_p->BufferSize); 241 return (1); 242 } 243 244 245 static int 246 pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 247 char *buf, size_t *rsize) 248 { 249 return (EIO); 250 } 251 252 static int 253 pxe_open(struct open_file *f, ...) 254 { 255 va_list args; 256 char *devname; /* Device part of file name (or NULL). */ 257 char temp[FNAME_SIZE]; 258 int error = 0; 259 int i; 260 261 va_start(args, f); 262 devname = va_arg(args, char*); 263 va_end(args); 264 265 /* On first open, do netif open, mount, etc. */ 266 if (pxe_opens == 0) { 267 /* Find network interface. */ 268 if (pxe_sock < 0) { 269 pxe_sock = netif_open(devname); 270 if (pxe_sock < 0) { 271 printf("pxe_open: netif_open() failed\n"); 272 return (ENXIO); 273 } 274 if (pxe_debug) 275 printf("pxe_open: netif_open() succeeded\n"); 276 } 277 if (rootip.s_addr == 0) { 278 /* 279 * Do a bootp/dhcp request to find out where our 280 * NFS/TFTP server is. Even if we dont get back 281 * the proper information, fall back to the server 282 * which brought us to life and a default rootpath. 283 */ 284 bootp(pxe_sock, BOOTP_PXE); 285 if (rootip.s_addr == 0) 286 rootip.s_addr = bootplayer.sip; 287 if (!rootpath[1]) 288 strcpy(rootpath, PXENFSROOTPATH); 289 290 for (i = 0; i < FNAME_SIZE && rootpath[i] != '\0'; i++) { 291 if (rootpath[i] == ':') 292 break; 293 } 294 if (i && i != FNAME_SIZE && rootpath[i] == ':') { 295 rootpath[i++] = '\0'; 296 if (inet_addr(&rootpath[0]) != INADDR_NONE) 297 rootip.s_addr = inet_addr(&rootpath[0]); 298 bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); 299 bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); 300 } 301 printf("pxe_open: ip address : %s\n", inet_ntoa(myip)); 302 printf("pxe_open: ip netmask : %s\n", intoa(netmask)); 303 printf("pxe_open: nfs root mount: %s:%s\n", inet_ntoa(rootip), rootpath); 304 printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); 305 306 setenv("boot.netif.ip", inet_ntoa(myip), 1); 307 setenv("boot.netif.netmask", intoa(netmask), 1); 308 setenv("boot.netif.gateway", inet_ntoa(gateip), 1); 309 if (bootplayer.Hardware == ETHER_TYPE) 310 setenv("boot.netif.hwaddr", ether_sprintf(bootplayer.CAddr), 1); 311 312 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 313 setenv("boot.nfsroot.path", rootpath, 1); 314 315 if (bootplayer.yip != INADDR_ANY && 316 bootplayer.yip != myip.s_addr) { 317 printf("Warning: PXE negotiated a different IP " 318 "in the preloader\n"); 319 bugged_bios_pxe = 1; 320 } 321 } 322 } 323 pxe_opens++; 324 devreplace(f, &pxe_sock); 325 326 return (error); 327 } 328 329 static int 330 pxe_close(struct open_file *f) 331 { 332 333 #ifdef PXE_DEBUG 334 if (pxe_debug) 335 printf("pxe_close: opens=%d\n", pxe_opens); 336 #endif 337 338 /* On last close, do netif close, etc. */ 339 f->f_devdata = NULL; 340 /* Extra close call? */ 341 if (pxe_opens <= 0) 342 return (0); 343 pxe_opens--; 344 /* Not last close? */ 345 if (pxe_opens > 0) 346 return(0); 347 348 /* get an NFS filehandle for our root filesystem */ 349 pxe_setnfshandle(rootpath); 350 351 if (pxe_sock >= 0) { 352 353 #ifdef PXE_DEBUG 354 if (pxe_debug) 355 printf("pxe_close: calling netif_close()\n"); 356 #endif 357 netif_close(pxe_sock); 358 pxe_sock = -1; 359 } 360 return (0); 361 } 362 363 static void 364 pxe_print(int verbose) 365 { 366 if (pxe_call != NULL) { 367 if (*bootplayer.Sname == '\0') { 368 printf(" "IP_STR":%s\n", 369 IP_ARGS(htonl(bootplayer.sip)), 370 bootplayer.bootfile); 371 } else { 372 printf(" %s:%s\n", bootplayer.Sname, 373 bootplayer.bootfile); 374 } 375 } 376 377 return; 378 } 379 380 static void 381 pxe_cleanup(void) 382 { 383 #ifdef PXE_DEBUG 384 t_PXENV_UNLOAD_STACK *unload_stack_p = 385 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 386 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 387 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 388 #endif 389 390 if (pxe_call == NULL) 391 return; 392 393 pxe_call(PXENV_UNDI_SHUTDOWN); 394 395 #ifdef PXE_DEBUG 396 if (pxe_debug && undi_shutdown_p->Status != 0) 397 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 398 undi_shutdown_p->Status); 399 #endif 400 401 pxe_call(PXENV_UNLOAD_STACK); 402 403 #ifdef PXE_DEBUG 404 if (pxe_debug && unload_stack_p->Status != 0) 405 printf("pxe_cleanup: UNLOAD_STACK failed %x\n", 406 unload_stack_p->Status); 407 #endif 408 } 409 410 void 411 pxe_perror(int err) 412 { 413 return; 414 } 415 416 /* To prevent LTO warnings. Must match stand/lib/nfs.c struct. */ 417 struct nfsv2_fattrs { 418 n_long fa_type; 419 n_long fa_mode; 420 n_long fa_nlink; 421 n_long fa_uid; 422 n_long fa_gid; 423 n_long fa_size; 424 n_long fa_blocksize; 425 n_long fa_rdev; 426 n_long fa_blocks; 427 n_long fa_fsid; 428 n_long fa_fileid; 429 struct nfsv2_time fa_atime; 430 struct nfsv2_time fa_mtime; 431 struct nfsv2_time fa_ctime; 432 }; 433 434 /* 435 * Reach inside the libstand NFS code and dig out an NFS handle 436 * for the root filesystem. If there is no nfs handle but a NFS root 437 * path was dynamically requested (not just as a default), then try 438 * to get the handle. This occurs if we are compiled for TFTP operation 439 * but still want to pass an NFS root to the kernel. 440 */ 441 struct nfs_iodesc { 442 struct iodesc *iodesc; 443 off_t off; 444 u_char fh[NFS_FHSIZE]; 445 /* structure truncated here */ 446 struct nfsv2_fattrs unused; /* unused */ 447 }; 448 extern struct nfs_iodesc nfs_root_node; 449 450 static void 451 pxe_setnfshandle(char *rootpath) 452 { 453 int i; 454 u_char *fh; 455 char buf[2 * NFS_FHSIZE + 3], *cp; 456 457 fh = &nfs_root_node.fh[0]; 458 459 /* 460 * If no file handle exists but a root path was dynamically 461 * requested, try to get a good handle. 462 */ 463 for (i = 0; i < NFS_FHSIZE; ++i) { 464 if (fh[i]) 465 break; 466 } 467 if (i != NFS_FHSIZE) { 468 buf[0] = 'X'; 469 cp = &buf[1]; 470 for (i = 0; i < NFS_FHSIZE; i++, cp += 2) 471 sprintf(cp, "%02x", fh[i]); 472 sprintf(cp, "X"); 473 setenv("boot.nfsroot.nfshandle", buf, 1); 474 } 475 } 476 477 void 478 pxenv_call(int func) 479 { 480 #ifdef PXE_DEBUG 481 if (pxe_debug) 482 printf("pxenv_call %x\n", func); 483 #endif 484 485 bzero(&v86, sizeof(v86)); 486 bzero(data_buffer, sizeof(data_buffer)); 487 488 __pxenvseg = pxenv_p->RMEntry.segment; 489 __pxenvoff = pxenv_p->RMEntry.offset; 490 491 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 492 v86.es = VTOPSEG(scratch_buffer); 493 v86.edi = VTOPOFF(scratch_buffer); 494 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 495 v86.ebx = func; 496 v86int(); 497 v86.ctl = V86_FLAGS; 498 } 499 500 void 501 bangpxe_call(int func) 502 { 503 #ifdef PXE_DEBUG 504 if (pxe_debug) 505 printf("bangpxe_call %x\n", func); 506 #endif 507 508 bzero(&v86, sizeof(v86)); 509 bzero(data_buffer, sizeof(data_buffer)); 510 511 __bangpxeseg = pxe_p->EntryPointSP.segment; 512 __bangpxeoff = pxe_p->EntryPointSP.offset; 513 514 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 515 v86.edx = VTOPSEG(scratch_buffer); 516 v86.eax = VTOPOFF(scratch_buffer); 517 v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); 518 v86.ebx = func; 519 v86int(); 520 v86.ctl = V86_FLAGS; 521 } 522 523 524 time_t 525 getsecs(void) 526 { 527 time_t n = 0; 528 time(&n); 529 return n; 530 } 531 532 static int 533 pxe_netif_match(struct netif *nif, void *machdep_hint) 534 { 535 return 1; 536 } 537 538 539 static int 540 pxe_netif_probe(struct netif *nif, void *machdep_hint) 541 { 542 t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 543 544 if (pxe_call == NULL) 545 return -1; 546 547 bzero(udpopen_p, sizeof(*udpopen_p)); 548 udpopen_p->src_ip = bootplayer.yip; 549 pxe_call(PXENV_UDP_OPEN); 550 551 if (udpopen_p->status != 0) { 552 printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 553 return -1; 554 } 555 return 0; 556 } 557 558 static void 559 pxe_netif_end(struct netif *nif) 560 { 561 t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 562 bzero(udpclose_p, sizeof(*udpclose_p)); 563 564 pxe_call(PXENV_UDP_CLOSE); 565 if (udpclose_p->status != 0) 566 printf("pxe_end failed %x\n", udpclose_p->status); 567 } 568 569 static void 570 pxe_netif_init(struct iodesc *desc, void *machdep_hint) 571 { 572 int i; 573 for (i = 0; i < 6; ++i) 574 desc->myea[i] = bootplayer.CAddr[i]; 575 desc->xid = bootplayer.ident; 576 } 577 578 static int 579 pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 580 { 581 return len; 582 } 583 584 static int 585 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 586 { 587 return len; 588 } 589 590 ssize_t 591 sendudp(struct iodesc *h, void *pkt, size_t len) 592 { 593 t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 594 bzero(udpwrite_p, sizeof(*udpwrite_p)); 595 596 udpwrite_p->ip = h->destip.s_addr; 597 udpwrite_p->dst_port = h->destport; 598 udpwrite_p->src_port = h->myport; 599 udpwrite_p->buffer_size = len; 600 udpwrite_p->buffer.segment = VTOPSEG(pkt); 601 udpwrite_p->buffer.offset = VTOPOFF(pkt); 602 603 if (netmask == 0 || SAMENET(myip, h->destip, netmask)) 604 udpwrite_p->gw = 0; 605 else 606 udpwrite_p->gw = gateip.s_addr; 607 608 pxe_call(PXENV_UDP_WRITE); 609 610 #if 0 611 /* XXX - I dont know why we need this. */ 612 delay(1000); 613 #endif 614 if (udpwrite_p->status != 0) { 615 /* XXX: This happens a lot. It shouldn't. */ 616 if (udpwrite_p->status != 1) 617 printf("sendudp failed %x\n", udpwrite_p->status); 618 return -1; 619 } 620 return len; 621 } 622 623 ssize_t 624 readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 625 { 626 t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 627 struct udphdr *uh; 628 struct ip *ip; 629 630 uh = (struct udphdr *) pkt - 1; 631 ip = (struct ip *)uh - 1; 632 again: 633 bzero(udpread_p, sizeof(*udpread_p)); 634 635 /* 636 * Bugged BIOSes (e.g. Gigabyte H97N-WIFI) can wind up asking for 637 * a different IP than we negotiated, then using that IP instead 638 * of the one we specified in the udpopen(). 639 */ 640 if (bugged_bios_pxe) 641 udpread_p->dest_ip = INADDR_ANY; 642 else 643 udpread_p->dest_ip = h->myip.s_addr; 644 udpread_p->d_port = h->myport; 645 udpread_p->buffer_size = len; 646 udpread_p->buffer.segment = VTOPSEG(data_buffer); 647 udpread_p->buffer.offset = VTOPOFF(data_buffer); 648 649 pxe_call(PXENV_UDP_READ); 650 651 if (udpread_p->status != 0) { 652 /* XXX: This happens a lot. It shouldn't. */ 653 if (udpread_p->status != 1) 654 printf("readudp failed %x\n", udpread_p->status); 655 return -1; 656 } 657 658 /* 659 * If the BIOS is bugged in this manner we were forced to allow 660 * any address in dest_ip and have to filter the packets ourselves. 661 * The bugged BIOS used the wrong IP in the udpwrite (it used the 662 * previously negotiated bootplayer.yip IP). So make sure the IP 663 * is either that one or the one we negotiated and specified in the 664 * udpopen ourselves. 665 */ 666 if (bugged_bios_pxe) { 667 if (udpread_p->dest_ip != h->myip.s_addr && 668 udpread_p->dest_ip != bootplayer.yip && 669 udpread_p->dest_ip != INADDR_ANY) { 670 goto again; 671 } 672 } 673 674 bcopy(data_buffer, pkt, udpread_p->buffer_size); 675 uh->uh_sport = udpread_p->s_port; 676 ip->ip_src.s_addr = udpread_p->src_ip; 677 return udpread_p->buffer_size; 678 } 679