pxe.c revision 64522
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 64522 2000-08-11 05:31:59Z 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 44#include <bootstrap.h> 45#include "btxv86.h" 46#include "pxe.h" 47 48/* 49 * Allocate the PXE buffers statically instead of sticking grimy fingers into 50 * BTX's private data area. The scratch buffer is used to send information to 51 * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. 52 */ 53#define PXE_BUFFER_SIZE 0x2000 54#define PXE_TFTP_BUFFER_SIZE 512 55static char scratch_buffer[PXE_BUFFER_SIZE]; 56static char data_buffer[PXE_BUFFER_SIZE]; 57 58static pxenv_t *pxenv_p = NULL; /* PXENV+ */ 59static pxe_t *pxe_p = NULL; /* !PXE */ 60static BOOTPLAYER bootplayer; /* PXE Cached information. */ 61 62static int pxe_debug = 0; 63static int pxe_sock = -1; 64static int pxe_opens = 0; 65 66void pxe_enable(void *pxeinfo); 67static void (*pxe_call)(int func); 68static void pxenv_call(int func); 69static void bangpxe_call(int func); 70 71static int pxe_init(void); 72static int pxe_strategy(void *devdata, int flag, daddr_t dblk, 73 size_t size, char *buf, size_t *rsize); 74static int pxe_open(struct open_file *f, ...); 75static int pxe_close(struct open_file *f); 76static void pxe_print(int verbose); 77static void pxe_cleanup(void); 78 79static void pxe_perror(int error); 80static int pxe_netif_match(struct netif *nif, void *machdep_hint); 81static int pxe_netif_probe(struct netif *nif, void *machdep_hint); 82static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 83static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, 84 time_t timeout); 85static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 86static void pxe_netif_end(struct netif *nif); 87 88extern struct netif_stats pxe_st[]; 89extern u_int16_t __bangpxeseg; 90extern u_int16_t __bangpxeoff; 91extern void __bangpxeentry(void); 92extern u_int16_t __pxenvseg; 93extern u_int16_t __pxenvoff; 94extern void __pxenventry(void); 95 96struct netif_dif pxe_ifs[] = { 97/* dif_unit dif_nsel dif_stats dif_private */ 98 {0, 1, &pxe_st[0], 0} 99}; 100 101struct netif_stats pxe_st[NENTS(pxe_ifs)]; 102 103struct netif_driver pxenetif = { 104 "pxenet", 105 pxe_netif_match, 106 pxe_netif_probe, 107 pxe_netif_init, 108 pxe_netif_get, 109 pxe_netif_put, 110 pxe_netif_end, 111 pxe_ifs, 112 NENTS(pxe_ifs) 113}; 114 115struct netif_driver *netif_drivers[] = { 116 &pxenetif, 117 NULL 118}; 119 120struct devsw pxedisk = { 121 "pxe", 122 DEVT_NET, 123 pxe_init, 124 pxe_strategy, 125 pxe_open, 126 pxe_close, 127 noioctl, 128 pxe_print, 129 pxe_cleanup 130}; 131 132/* 133 * This function is called by the loader to enable PXE support if we 134 * are booted by PXE. The passed in pointer is a pointer to the 135 * PXENV+ structure. 136 */ 137void 138pxe_enable(void *pxeinfo) 139{ 140 pxenv_p = (pxenv_t *)pxeinfo; 141 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 142 pxenv_p->PXEPtr.offset); 143 pxe_call = NULL; 144} 145 146/* 147 * return true if pxe structures are found/initialized, 148 * also figures out our IP information via the pxe cached info struct 149 */ 150static int 151pxe_init(void) 152{ 153 t_PXENV_GET_CACHED_INFO *gci_p; 154 int counter; 155 uint8_t checksum; 156 uint8_t *checkptr; 157 158 if(pxenv_p == NULL) 159 return (0); 160 161 /* look for "PXENV+" */ 162 if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { 163 pxenv_p = NULL; 164 return (0); 165 } 166 167 /* make sure the size is something we can handle */ 168 if (pxenv_p->Length > sizeof(*pxenv_p)) { 169 printf("PXENV+ structure too large, ignoring\n"); 170 pxenv_p = NULL; 171 return (0); 172 } 173 174 /* 175 * do byte checksum: 176 * add up each byte in the structure, the total should be 0 177 */ 178 checksum = 0; 179 checkptr = (uint8_t *) pxenv_p; 180 for (counter = 0; counter < pxenv_p->Length; counter++) 181 checksum += *checkptr++; 182 if (checksum != 0) { 183 printf("PXENV+ structure failed checksum, ignoring\n"); 184 pxenv_p = NULL; 185 return (0); 186 } 187 188 189 /* 190 * PXENV+ passed, so use that if !PXE is not available or 191 * the checksum fails. 192 */ 193 pxe_call = pxenv_call; 194 if (pxenv_p->Version >= 0x0200) { 195 for (;;) { 196 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 197 pxe_p = NULL; 198 break; 199 } 200 checksum = 0; 201 checkptr = (uint8_t *)pxe_p; 202 for (counter = 0; counter < pxe_p->StructLength; 203 counter++) 204 checksum += *checkptr++; 205 if (checksum != 0) { 206 pxe_p = NULL; 207 break; 208 } 209 pxe_call = bangpxe_call; 210 break; 211 } 212 } 213 214 printf("\nPXE version %d.%d, real mode entry point ", 215 (uint8_t) (pxenv_p->Version >> 8), 216 (uint8_t) (pxenv_p->Version & 0xFF)); 217 if (pxe_call == bangpxe_call) 218 printf("@%04x:%04x\n", 219 pxe_p->EntryPointSP.segment, 220 pxe_p->EntryPointSP.offset); 221 else 222 printf("@%04x:%04x\n", 223 pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); 224 225 gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 226 bzero(gci_p, sizeof(*gci_p)); 227 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 228 pxe_call(PXENV_GET_CACHED_INFO); 229 if (gci_p->Status != 0) { 230 pxe_perror(gci_p->Status); 231 pxe_p = NULL; 232 return (0); 233 } 234 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 235 &bootplayer, gci_p->BufferSize); 236 return (1); 237} 238 239 240static int 241pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 242 char *buf, size_t *rsize) 243{ 244 return (EIO); 245} 246 247static int 248pxe_open(struct open_file *f, ...) 249{ 250 va_list args; 251 char *devname; /* Device part of file name (or NULL). */ 252 char temp[FNAME_SIZE]; 253 int error = 0; 254 int i; 255 256 va_start(args, f); 257 devname = va_arg(args, char*); 258 va_end(args); 259 260 /* On first open, do netif open, mount, etc. */ 261 if (pxe_opens == 0) { 262 /* Find network interface. */ 263 if (pxe_sock < 0) { 264 pxe_sock = netif_open(devname); 265 if (pxe_sock < 0) { 266 printf("pxe_open: netif_open() failed\n"); 267 return (ENXIO); 268 } 269 if (pxe_debug) 270 printf("pxe_open: netif_open() succeeded\n"); 271 } 272 if (rootip.s_addr == 0) { 273 /* 274 * Do a bootp/dhcp request to find out where our 275 * NFS/TFTP server is. Even if we dont get back 276 * the proper information, fall back to the server 277 * which brought us to life and a default rootpath. 278 */ 279 bootp(pxe_sock); 280 if (rootip.s_addr == 0) 281 rootip.s_addr = bootplayer.sip; 282 if (!rootpath[1]) 283 strcpy(rootpath, PXENFSROOTPATH); 284 285 for(i=0; i<FNAME_SIZE; i++) 286 if(rootpath[i] == ':') 287 break; 288 if(i && i != FNAME_SIZE) { 289 i++; 290 bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); 291 bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); 292 } 293 printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); 294 printf("pxe_open: server path: %s\n", rootpath); 295 printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); 296 297 setenv("boot.pxe.server_addr", inet_ntoa(rootip), 1); 298 setenv("boot.pxe.rootpath", rootpath, 1); 299 setenv("boot.pxe.gateway", inet_ntoa(gateip), 1); 300 setenv("boot.pxe.myip", inet_ntoa(myip), 1); 301 setenv("boot.pxe.netmask", intoa(netmask), 1); 302 } 303 } 304 pxe_opens++; 305 f->f_devdata = &pxe_sock; 306 return (error); 307} 308 309static int 310pxe_close(struct open_file *f) 311{ 312 313#ifdef PXE_DEBUG 314 if (pxe_debug) 315 printf("pxe_close: opens=%d\n", pxe_opens); 316#endif 317 318 /* On last close, do netif close, etc. */ 319 f->f_devdata = NULL; 320 /* Extra close call? */ 321 if (pxe_opens <= 0) 322 return (0); 323 pxe_opens--; 324 /* Not last close? */ 325 if (pxe_opens > 0) 326 return(0); 327 328 if (pxe_sock >= 0) { 329#ifdef PXE_DEBUG 330 if (pxe_debug) 331 printf("pxe_close: calling netif_close()\n"); 332#endif 333 netif_close(pxe_sock); 334 pxe_sock = -1; 335 } 336 return (0); 337} 338 339static void 340pxe_print(int verbose) 341{ 342 if (pxe_call != NULL) { 343 if (*bootplayer.Sname == '\0') { 344 printf(" "IP_STR":%s\n", 345 IP_ARGS(htonl(bootplayer.sip)), 346 bootplayer.bootfile); 347 } else { 348 printf(" %s:%s\n", bootplayer.Sname, 349 bootplayer.bootfile); 350 } 351 } 352 353 return; 354} 355 356static void 357pxe_cleanup(void) 358{ 359#ifdef PXE_DEBUG 360 t_PXENV_UNLOAD_STACK *unload_stack_p = 361 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 362 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 363 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 364#endif 365 366 if (pxe_call == NULL) 367 return; 368 369 pxe_call(PXENV_UNDI_SHUTDOWN); 370 371#ifdef PXE_DEBUG 372 if (pxe_debug && undi_shutdown_p->Status != 0) 373 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 374 undi_shutdown_p->Status); 375#endif 376 377 pxe_call(PXENV_UNLOAD_STACK); 378 379#ifdef PXE_DEBUG 380 if (pxe_debug && unload_stack_p->Status != 0) 381 printf("pxe_cleanup: UNLOAD_STACK failed %x\n", 382 unload_stack_p->Status); 383#endif 384} 385 386void 387pxe_perror(int err) 388{ 389 return; 390} 391 392void 393pxenv_call(int func) 394{ 395#ifdef PXE_DEBUG 396 if (pxe_debug) 397 printf("pxenv_call %x\n", func); 398#endif 399 400 bzero(&v86, sizeof(v86)); 401 bzero(data_buffer, sizeof(data_buffer)); 402 403 __pxenvseg = pxenv_p->RMEntry.segment; 404 __pxenvoff = pxenv_p->RMEntry.offset; 405 406 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 407 v86.es = VTOPSEG(scratch_buffer); 408 v86.edi = VTOPOFF(scratch_buffer); 409 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 410 v86.ebx = func; 411 v86int(); 412 v86.ctl = V86_FLAGS; 413} 414 415void 416bangpxe_call(int func) 417{ 418#ifdef PXE_DEBUG 419 if (pxe_debug) 420 printf("bangpxe_call %x\n", func); 421#endif 422 423 bzero(&v86, sizeof(v86)); 424 bzero(data_buffer, sizeof(data_buffer)); 425 426 __bangpxeseg = pxe_p->EntryPointSP.segment; 427 __bangpxeoff = pxe_p->EntryPointSP.offset; 428 429 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 430 v86.edx = VTOPSEG(scratch_buffer); 431 v86.eax = VTOPOFF(scratch_buffer); 432 v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); 433 v86.ebx = func; 434 v86int(); 435 v86.ctl = V86_FLAGS; 436} 437 438 439time_t 440getsecs() 441{ 442 time_t n = 0; 443 time(&n); 444 return n; 445} 446 447static int 448pxe_netif_match(struct netif *nif, void *machdep_hint) 449{ 450 return 1; 451} 452 453 454static int 455pxe_netif_probe(struct netif *nif, void *machdep_hint) 456{ 457 t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 458 459 if (pxe_call == NULL) 460 return -1; 461 462 bzero(udpopen_p, sizeof(*udpopen_p)); 463 udpopen_p->src_ip = bootplayer.yip; 464 pxe_call(PXENV_UDP_OPEN); 465 466 if (udpopen_p->status != 0) { 467 printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 468 return -1; 469 } 470 return 0; 471} 472 473static void 474pxe_netif_end(struct netif *nif) 475{ 476 t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 477 bzero(udpclose_p, sizeof(*udpclose_p)); 478 479 pxe_call(PXENV_UDP_CLOSE); 480 if (udpclose_p->status != 0) 481 printf("pxe_end failed %x\n", udpclose_p->status); 482} 483 484static void 485pxe_netif_init(struct iodesc *desc, void *machdep_hint) 486{ 487 int i; 488 for (i = 0; i < 6; ++i) 489 desc->myea[i] = bootplayer.CAddr[i]; 490 desc->xid = bootplayer.ident; 491} 492 493static int 494pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 495{ 496 return len; 497} 498 499static int 500pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 501{ 502 return len; 503} 504 505ssize_t 506sendudp(struct iodesc *h, void *pkt, size_t len) 507{ 508 t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 509 bzero(udpwrite_p, sizeof(*udpwrite_p)); 510 511 udpwrite_p->ip = h->destip.s_addr; 512 udpwrite_p->dst_port = h->destport; 513 udpwrite_p->src_port = h->myport; 514 udpwrite_p->gw = gateip.s_addr; 515 udpwrite_p->buffer_size = len; 516 udpwrite_p->buffer.segment = VTOPSEG(pkt); 517 udpwrite_p->buffer.offset = VTOPOFF(pkt); 518 519 pxe_call(PXENV_UDP_WRITE); 520 521#if 0 522 /* XXX - I dont know why we need this. */ 523 delay(1000); 524#endif 525 if (udpwrite_p->status != 0) { 526 /* XXX: This happens a lot. It shouldn't. */ 527 if (udpwrite_p->status != 1) 528 printf("sendudp failed %x\n", udpwrite_p->status); 529 return -1; 530 } 531 return len; 532} 533 534ssize_t 535readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 536{ 537 t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 538 struct udphdr *uh = NULL; 539 540 uh = (struct udphdr *) pkt - 1; 541 bzero(udpread_p, sizeof(*udpread_p)); 542 543 udpread_p->dest_ip = h->myip.s_addr; 544 udpread_p->d_port = h->myport; 545 udpread_p->buffer_size = len; 546 udpread_p->buffer.segment = VTOPSEG(data_buffer); 547 udpread_p->buffer.offset = VTOPOFF(data_buffer); 548 549 pxe_call(PXENV_UDP_READ); 550 551#if 0 552 /* XXX - I dont know why we need this. */ 553 delay(1000); 554#endif 555 if (udpread_p->status != 0) { 556 /* XXX: This happens a lot. It shouldn't. */ 557 if (udpread_p->status != 1) 558 printf("readudp failed %x\n", udpread_p->status); 559 return -1; 560 } 561 bcopy(data_buffer, pkt, udpread_p->buffer_size); 562 uh->uh_sport = udpread_p->s_port; 563 return udpread_p->buffer_size; 564} 565