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