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