pxe.c revision 59408
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 59408 2000-04-20 00:06:15Z 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); 73void pxe_call(int func); 74 75static int pxe_init(void); 76static int pxe_strategy(void *devdata, int flag, daddr_t dblk, 77 size_t size, void *buf, size_t *rsize); 78static int pxe_open(struct open_file *f, ...); 79static int pxe_close(struct open_file *f); 80static void pxe_print(int verbose); 81static void pxe_cleanup(void); 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 struct in_addr rootip; 94extern char rootpath[FNAME_SIZE]; 95extern u_int16_t __pxeseg; 96extern u_int16_t __pxeoff; 97extern void __h0h0magic(void); 98 99struct netif_dif pxe_ifs[] = { 100/* dif_unit dif_nsel dif_stats dif_private */ 101 {0, 1, &pxe_st[0], 0} 102}; 103 104struct netif_stats pxe_st[NENTS(pxe_ifs)]; 105 106struct netif_driver pxenetif = { 107 "pxenet", 108 pxe_netif_match, 109 pxe_netif_probe, 110 pxe_netif_init, 111 pxe_netif_get, 112 pxe_netif_put, 113 pxe_netif_end, 114 pxe_ifs, 115 NENTS(pxe_ifs) 116}; 117 118struct netif_driver *netif_drivers[] = { 119 &pxenetif, 120 NULL 121}; 122 123struct devsw pxedisk = { 124 "pxe", 125 DEVT_NET, 126 pxe_init, 127 pxe_strategy, 128 pxe_open, 129 pxe_close, 130 noioctl, 131 pxe_print, 132 pxe_cleanup 133}; 134 135/* 136 * This function is called by the loader to enable PXE support if we 137 * are booted by PXE. The passed in pointer is a pointer to the 138 * PXENV+ structure. 139 */ 140void 141pxe_enable(void *pxeinfo) 142{ 143 pxenv_p = (pxenv_t *)pxeinfo; 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 return (0); 164 165 /* make sure the size is something we can handle */ 166 if (pxenv_p->Length > sizeof(*pxenv_p)) { 167 printf("PXENV+ structure too large, ignoring\n"); 168 pxenv_p = NULL; 169 return (0); 170 } 171 172 /* 173 * do byte checksum: 174 * add up each byte in the structure, the total should be 0 175 */ 176 checksum = 0; 177 checkptr = (uint8_t *) pxenv_p; 178 for (counter = 0; counter < pxenv_p->Length; counter++) 179 checksum += *checkptr++; 180 if (checksum != 0) { 181 printf("PXENV+ structure failed checksum, ignoring\n"); 182 pxenv_p = NULL; 183 return (0); 184 } 185 186 if (pxenv_p->Version < 0x0201) { 187 printf("PXENV+ is not supported.\n"); 188 return (0); 189 } 190 191 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 192 pxenv_p->PXEPtr.offset); 193 194 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) 195 return(0); 196 197 checksum = 0; 198 checkptr = (uint8_t *)pxe_p; 199 for (counter = 0; counter < pxe_p->StructLength; counter++) 200 checksum += *checkptr++; 201 if (checksum != 0) { 202 printf("!PXE structure failed checksum. %x\n", checksum); 203 return(0); 204 } 205 206 207 printf("\n!PXE version %d.%d, real mode entry point @%04x:%04x\n", 208 (uint8_t) (pxenv_p->Version >> 8), 209 (uint8_t) (pxenv_p->Version & 0xFF), 210 pxe_p->EntryPointSP.segment, pxe_p->EntryPointSP.offset); 211 212 gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 213 bzero(gci_p, sizeof(*gci_p)); 214 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 215 pxe_call(PXENV_GET_CACHED_INFO); 216 if (gci_p->Status != 0) { 217 pxe_perror(gci_p->Status); 218 pxenv_p = NULL; 219 return (0); 220 } 221 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 222 &bootplayer, gci_p->BufferSize); 223 224 /* 225 * XXX - This is a major cop out. We should request this 226 * from DHCP, but we can't do that until we have full UNDI 227 * support. 228 * 229 * Also set the nfs server's IP. 230 */ 231 strcpy(rootpath, PXENFSROOTPATH); 232 rootip.s_addr = bootplayer.sip; 233 234 return (1); 235} 236 237 238static int 239pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 240 void *buf, size_t *rsize) 241{ 242 return (EIO); 243} 244 245static int 246pxe_open(struct open_file *f, ...) 247{ 248 va_list args; 249 char *devname; /* Device part of file name (or NULL). */ 250 int error = 0; 251 252 va_start(args, f); 253 devname = va_arg(args, char*); 254 va_end(args); 255 256 /* On first open, do netif open, mount, etc. */ 257 if (pxe_opens == 0) { 258 /* Find network interface. */ 259 if (pxe_sock < 0) { 260 pxe_sock = netif_open(devname); 261 if (pxe_sock < 0) { 262 printf("pxe_open: netif_open() failed\n"); 263 return (ENXIO); 264 } 265 if (debug) 266 printf("pxe_open: netif_open() succeeded\n"); 267 } 268 } 269 pxe_opens++; 270 f->f_devdata = &pxe_sock; 271 return (error); 272} 273 274static int 275pxe_close(struct open_file *f) 276{ 277 278#ifdef PXE_DEBUG 279 if (debug) 280 printf("pxe_close: opens=%d\n", pxe_opens); 281#endif 282 283 /* On last close, do netif close, etc. */ 284 f->f_devdata = NULL; 285 /* Extra close call? */ 286 if (pxe_opens <= 0) 287 return (0); 288 pxe_opens--; 289 /* Not last close? */ 290 if (pxe_opens > 0) 291 return(0); 292 rootip.s_addr = 0; 293 if (pxe_sock >= 0) { 294#ifdef PXE_DEBUG 295 if (debug) 296 printf("pxe_close: calling netif_close()\n"); 297#endif 298 netif_close(pxe_sock); 299 pxe_sock = -1; 300 } 301 return (0); 302} 303 304static void 305pxe_print(int verbose) 306{ 307 if (pxenv_p != NULL) { 308 if (*bootplayer.Sname == '\0') { 309 printf(" "IP_STR":%s\n", 310 IP_ARGS(htonl(bootplayer.sip)), 311 bootplayer.bootfile); 312 } else { 313 printf(" %s:%s\n", bootplayer.Sname, 314 bootplayer.bootfile); 315 } 316 } 317 318 return; 319} 320 321static void 322pxe_cleanup(void) 323{ 324 t_PXENV_UNLOAD_STACK *unload_stack_p = 325 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 326 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 327 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 328 329 pxe_call(PXENV_UNDI_SHUTDOWN); 330 if (undi_shutdown_p->Status != 0) 331 panic("pxe_cleanup: UNDI_SHUTDOWN failed %x", 332 undi_shutdown_p->Status); 333 334 pxe_call(PXENV_UNLOAD_STACK); 335 if (unload_stack_p->Status != 0) 336 panic("pxe_cleanup: UNLOAD_STACK failed %x", 337 unload_stack_p->Status); 338} 339 340void 341pxe_perror(int err) 342{ 343 return; 344} 345 346void 347pxe_call(int func) 348{ 349 bzero(&v86, sizeof(v86)); 350 bzero(data_buffer, sizeof(data_buffer)); 351 352 __pxeseg = pxe_p->EntryPointSP.segment; 353 __pxeoff = pxe_p->EntryPointSP.offset; 354 355 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 356 v86.edx = VTOPSEG(scratch_buffer); 357 v86.eax = VTOPOFF(scratch_buffer); 358 v86.addr = (VTOPSEG(__h0h0magic) << 16) | VTOPOFF(__h0h0magic); 359 v86.ebx = func; 360 v86int(); 361 v86.ctl = V86_FLAGS; 362} 363 364 365time_t 366getsecs() 367{ 368 time_t n = 0; 369 time(&n); 370 return n; 371} 372 373static int 374pxe_netif_match(struct netif *nif, void *machdep_hint) 375{ 376 return 1; 377} 378 379 380static int 381pxe_netif_probe(struct netif *nif, void *machdep_hint) 382{ 383 t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 384 bzero(udpopen_p, sizeof(*udpopen_p)); 385 386 udpopen_p->src_ip = bootplayer.yip; 387 pxe_call(PXENV_UDP_OPEN); 388 389 if (udpopen_p->status != 0) { 390 printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 391 return -1; 392 } 393 return 0; 394} 395 396static void 397pxe_netif_end(struct netif *nif) 398{ 399 t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 400 bzero(udpclose_p, sizeof(*udpclose_p)); 401 402 pxe_call(PXENV_UDP_CLOSE); 403 if (udpclose_p->status != 0) 404 printf("pxe_end failed %x\n", udpclose_p->status); 405} 406 407static void 408pxe_netif_init(struct iodesc *desc, void *machdep_hint) 409{ 410} 411 412static int 413pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 414{ 415 return len; 416} 417 418static int 419pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 420{ 421 return len; 422} 423 424ssize_t 425sendudp(struct iodesc *h, void *pkt, size_t len) 426{ 427 t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 428 bzero(udpwrite_p, sizeof(*udpwrite_p)); 429 430 udpwrite_p->ip = bootplayer.sip; 431 udpwrite_p->dst_port = h->destport; 432 udpwrite_p->src_port = h->myport; 433 udpwrite_p->buffer_size = len; 434 udpwrite_p->buffer.segment = VTOPSEG(pkt); 435 udpwrite_p->buffer.offset = VTOPOFF(pkt); 436 437 pxe_call(PXENV_UDP_WRITE); 438 439#if 0 440 /* XXX - I dont know why we need this. */ 441 delay(1000); 442#endif 443 if (udpwrite_p->status != 0) { 444 printf("sendudp failed %x\n", udpwrite_p->status); 445 return -1; 446 } 447 return len; 448} 449 450ssize_t 451readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 452{ 453 t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 454 struct udphdr *uh = NULL; 455 456 uh = (struct udphdr *) pkt - 1; 457 bzero(udpread_p, sizeof(*udpread_p)); 458 459 udpread_p->dest_ip = bootplayer.yip; 460 udpread_p->d_port = h->myport; 461 udpread_p->buffer_size = len; 462 udpread_p->buffer.segment = VTOPSEG(data_buffer); 463 udpread_p->buffer.offset = VTOPOFF(data_buffer); 464 465 pxe_call(PXENV_UDP_READ); 466 467#if 0 468 /* XXX - I dont know why we need this. */ 469 delay(1000); 470#endif 471 if (udpread_p->status != 0) { 472 /* XXX: This happens a lot. It shouldn't. */ 473 if (udpread_p->status != 1) 474 printf("readudp failed %x\n", udpread_p->status); 475 return -1; 476 } 477 bcopy(data_buffer, pkt, udpread_p->buffer_size); 478 uh->uh_sport = udpread_p->s_port; 479 return udpread_p->buffer_size; 480} 481