pxe.c revision 65498
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 65498 2000-09-05 22:32:31Z msmith $ 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 i++; 294 bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); 295 bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); 296 } 297 printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); 298 printf("pxe_open: server path: %s\n", rootpath); 299 printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); 300 301 setenv("boot.netif.ip", inet_ntoa(myip), 1); 302 setenv("boot.netif.netmask", intoa(netmask), 1); 303 setenv("boot.netif.gateway", inet_ntoa(gateip), 1); 304 if (bootplayer.Hardware == ETHER_TYPE) { 305 sprintf(temp, "%6D", bootplayer.CAddr, ":"); 306 setenv("boot.netif.hwaddr", temp, 1); 307 } 308 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); 309 setenv("boot.nfsroot.path", rootpath, 1); 310 } 311 } 312 pxe_opens++; 313 f->f_devdata = &pxe_sock; 314 return (error); 315} 316 317static int 318pxe_close(struct open_file *f) 319{ 320 321#ifdef PXE_DEBUG 322 if (pxe_debug) 323 printf("pxe_close: opens=%d\n", pxe_opens); 324#endif 325 326 /* On last close, do netif close, etc. */ 327 f->f_devdata = NULL; 328 /* Extra close call? */ 329 if (pxe_opens <= 0) 330 return (0); 331 pxe_opens--; 332 /* Not last close? */ 333 if (pxe_opens > 0) 334 return(0); 335 336 /* get an NFS filehandle for our root filesystem */ 337 pxe_setnfshandle(rootpath); 338 339 if (pxe_sock >= 0) { 340 341#ifdef PXE_DEBUG 342 if (pxe_debug) 343 printf("pxe_close: calling netif_close()\n"); 344#endif 345 netif_close(pxe_sock); 346 pxe_sock = -1; 347 } 348 return (0); 349} 350 351static void 352pxe_print(int verbose) 353{ 354 if (pxe_call != NULL) { 355 if (*bootplayer.Sname == '\0') { 356 printf(" "IP_STR":%s\n", 357 IP_ARGS(htonl(bootplayer.sip)), 358 bootplayer.bootfile); 359 } else { 360 printf(" %s:%s\n", bootplayer.Sname, 361 bootplayer.bootfile); 362 } 363 } 364 365 return; 366} 367 368static void 369pxe_cleanup(void) 370{ 371#ifdef PXE_DEBUG 372 t_PXENV_UNLOAD_STACK *unload_stack_p = 373 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 374 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 375 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 376#endif 377 378 if (pxe_call == NULL) 379 return; 380 381 pxe_call(PXENV_UNDI_SHUTDOWN); 382 383#ifdef PXE_DEBUG 384 if (pxe_debug && undi_shutdown_p->Status != 0) 385 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 386 undi_shutdown_p->Status); 387#endif 388 389 pxe_call(PXENV_UNLOAD_STACK); 390 391#ifdef PXE_DEBUG 392 if (pxe_debug && unload_stack_p->Status != 0) 393 printf("pxe_cleanup: UNLOAD_STACK failed %x\n", 394 unload_stack_p->Status); 395#endif 396} 397 398void 399pxe_perror(int err) 400{ 401 return; 402} 403 404/* 405 * Reach inside the libstand NFS code and dig out an NFS handle 406 * for the root filesystem. 407 */ 408struct nfs_iodesc { 409 struct iodesc *iodesc; 410 off_t off; 411 u_char fh[NFS_FHSIZE]; 412 /* structure truncated here */ 413}; 414extern struct nfs_iodesc nfs_root_node; 415 416static void 417pxe_setnfshandle(char *rootpath) 418{ 419 int i; 420 u_char *fh; 421 char buf[2 * NFS_FHSIZE + 3], *cp; 422 423 fh = &nfs_root_node.fh[0]; 424 buf[0] = 'X'; 425 cp = &buf[1]; 426 for (i = 0; i < NFS_FHSIZE; i++, cp += 2) 427 sprintf(cp, "%02x", fh[i]); 428 sprintf(cp, "X"); 429 setenv("boot.nfsroot.nfshandle", buf, 1); 430} 431 432void 433pxenv_call(int func) 434{ 435#ifdef PXE_DEBUG 436 if (pxe_debug) 437 printf("pxenv_call %x\n", func); 438#endif 439 440 bzero(&v86, sizeof(v86)); 441 bzero(data_buffer, sizeof(data_buffer)); 442 443 __pxenvseg = pxenv_p->RMEntry.segment; 444 __pxenvoff = pxenv_p->RMEntry.offset; 445 446 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 447 v86.es = VTOPSEG(scratch_buffer); 448 v86.edi = VTOPOFF(scratch_buffer); 449 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 450 v86.ebx = func; 451 v86int(); 452 v86.ctl = V86_FLAGS; 453} 454 455void 456bangpxe_call(int func) 457{ 458#ifdef PXE_DEBUG 459 if (pxe_debug) 460 printf("bangpxe_call %x\n", func); 461#endif 462 463 bzero(&v86, sizeof(v86)); 464 bzero(data_buffer, sizeof(data_buffer)); 465 466 __bangpxeseg = pxe_p->EntryPointSP.segment; 467 __bangpxeoff = pxe_p->EntryPointSP.offset; 468 469 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 470 v86.edx = VTOPSEG(scratch_buffer); 471 v86.eax = VTOPOFF(scratch_buffer); 472 v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); 473 v86.ebx = func; 474 v86int(); 475 v86.ctl = V86_FLAGS; 476} 477 478 479time_t 480getsecs() 481{ 482 time_t n = 0; 483 time(&n); 484 return n; 485} 486 487static int 488pxe_netif_match(struct netif *nif, void *machdep_hint) 489{ 490 return 1; 491} 492 493 494static int 495pxe_netif_probe(struct netif *nif, void *machdep_hint) 496{ 497 t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 498 499 if (pxe_call == NULL) 500 return -1; 501 502 bzero(udpopen_p, sizeof(*udpopen_p)); 503 udpopen_p->src_ip = bootplayer.yip; 504 pxe_call(PXENV_UDP_OPEN); 505 506 if (udpopen_p->status != 0) { 507 printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 508 return -1; 509 } 510 return 0; 511} 512 513static void 514pxe_netif_end(struct netif *nif) 515{ 516 t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 517 bzero(udpclose_p, sizeof(*udpclose_p)); 518 519 pxe_call(PXENV_UDP_CLOSE); 520 if (udpclose_p->status != 0) 521 printf("pxe_end failed %x\n", udpclose_p->status); 522} 523 524static void 525pxe_netif_init(struct iodesc *desc, void *machdep_hint) 526{ 527 int i; 528 for (i = 0; i < 6; ++i) 529 desc->myea[i] = bootplayer.CAddr[i]; 530 desc->xid = bootplayer.ident; 531} 532 533static int 534pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 535{ 536 return len; 537} 538 539static int 540pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 541{ 542 return len; 543} 544 545ssize_t 546sendudp(struct iodesc *h, void *pkt, size_t len) 547{ 548 t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 549 bzero(udpwrite_p, sizeof(*udpwrite_p)); 550 551 udpwrite_p->ip = h->destip.s_addr; 552 udpwrite_p->dst_port = h->destport; 553 udpwrite_p->src_port = h->myport; 554 udpwrite_p->gw = gateip.s_addr; 555 udpwrite_p->buffer_size = len; 556 udpwrite_p->buffer.segment = VTOPSEG(pkt); 557 udpwrite_p->buffer.offset = VTOPOFF(pkt); 558 559 pxe_call(PXENV_UDP_WRITE); 560 561#if 0 562 /* XXX - I dont know why we need this. */ 563 delay(1000); 564#endif 565 if (udpwrite_p->status != 0) { 566 /* XXX: This happens a lot. It shouldn't. */ 567 if (udpwrite_p->status != 1) 568 printf("sendudp failed %x\n", udpwrite_p->status); 569 return -1; 570 } 571 return len; 572} 573 574ssize_t 575readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 576{ 577 t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 578 struct udphdr *uh = NULL; 579 580 uh = (struct udphdr *) pkt - 1; 581 bzero(udpread_p, sizeof(*udpread_p)); 582 583 udpread_p->dest_ip = h->myip.s_addr; 584 udpread_p->d_port = h->myport; 585 udpread_p->buffer_size = len; 586 udpread_p->buffer.segment = VTOPSEG(data_buffer); 587 udpread_p->buffer.offset = VTOPOFF(data_buffer); 588 589 pxe_call(PXENV_UDP_READ); 590 591#if 0 592 /* XXX - I dont know why we need this. */ 593 delay(1000); 594#endif 595 if (udpread_p->status != 0) { 596 /* XXX: This happens a lot. It shouldn't. */ 597 if (udpread_p->status != 1) 598 printf("readudp failed %x\n", udpread_p->status); 599 return -1; 600 } 601 bcopy(data_buffer, pkt, udpread_p->buffer_size); 602 uh->uh_sport = udpread_p->s_port; 603 return udpread_p->buffer_size; 604} 605