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