pxe.c revision 59456
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 59456 2000-04-21 04:58:51Z 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 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 if (pxenv_p->Version < 0x0201) { 189 printf("PXENV+ is not supported.\n"); 190 return (0); 191 } 192 193 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 194 pxenv_p->PXEPtr.offset); 195 196 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 197 pxe_p = NULL; 198 return(0); 199 } 200 201 checksum = 0; 202 checkptr = (uint8_t *)pxe_p; 203 for (counter = 0; counter < pxe_p->StructLength; counter++) 204 checksum += *checkptr++; 205 if (checksum != 0) { 206 printf("!PXE structure failed checksum. %x\n", checksum); 207 pxe_p = NULL; 208 return(0); 209 } 210 211 212 printf("\n!PXE version %d.%d, real mode entry point @%04x:%04x\n", 213 (uint8_t) (pxenv_p->Version >> 8), 214 (uint8_t) (pxenv_p->Version & 0xFF), 215 pxe_p->EntryPointSP.segment, pxe_p->EntryPointSP.offset); 216 217 gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 218 bzero(gci_p, sizeof(*gci_p)); 219 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 220 pxe_call(PXENV_GET_CACHED_INFO); 221 if (gci_p->Status != 0) { 222 pxe_perror(gci_p->Status); 223 pxe_p = NULL; 224 return (0); 225 } 226 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 227 &bootplayer, gci_p->BufferSize); 228 229 /* 230 * XXX - This is a major cop out. We should request this 231 * from DHCP, but we can't do that until we have full UNDI 232 * support. 233 * 234 * Also set the nfs server's IP. 235 */ 236 strcpy(rootpath, PXENFSROOTPATH); 237 rootip.s_addr = bootplayer.sip; 238 239 return (1); 240} 241 242 243static int 244pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 245 void *buf, size_t *rsize) 246{ 247 return (EIO); 248} 249 250static int 251pxe_open(struct open_file *f, ...) 252{ 253 va_list args; 254 char *devname; /* Device part of file name (or NULL). */ 255 int error = 0; 256 257 va_start(args, f); 258 devname = va_arg(args, char*); 259 va_end(args); 260 261 /* On first open, do netif open, mount, etc. */ 262 if (pxe_opens == 0) { 263 /* Find network interface. */ 264 if (pxe_sock < 0) { 265 pxe_sock = netif_open(devname); 266 if (pxe_sock < 0) { 267 printf("pxe_open: netif_open() failed\n"); 268 return (ENXIO); 269 } 270 if (debug) 271 printf("pxe_open: netif_open() succeeded\n"); 272 } 273 } 274 pxe_opens++; 275 f->f_devdata = &pxe_sock; 276 return (error); 277} 278 279static int 280pxe_close(struct open_file *f) 281{ 282 283#ifdef PXE_DEBUG 284 if (debug) 285 printf("pxe_close: opens=%d\n", pxe_opens); 286#endif 287 288 /* On last close, do netif close, etc. */ 289 f->f_devdata = NULL; 290 /* Extra close call? */ 291 if (pxe_opens <= 0) 292 return (0); 293 pxe_opens--; 294 /* Not last close? */ 295 if (pxe_opens > 0) 296 return(0); 297 rootip.s_addr = 0; 298 if (pxe_sock >= 0) { 299#ifdef PXE_DEBUG 300 if (debug) 301 printf("pxe_close: calling netif_close()\n"); 302#endif 303 netif_close(pxe_sock); 304 pxe_sock = -1; 305 } 306 return (0); 307} 308 309static void 310pxe_print(int verbose) 311{ 312 if (pxe_p != NULL) { 313 if (*bootplayer.Sname == '\0') { 314 printf(" "IP_STR":%s\n", 315 IP_ARGS(htonl(bootplayer.sip)), 316 bootplayer.bootfile); 317 } else { 318 printf(" %s:%s\n", bootplayer.Sname, 319 bootplayer.bootfile); 320 } 321 } 322 323 return; 324} 325 326static void 327pxe_cleanup(void) 328{ 329 t_PXENV_UNLOAD_STACK *unload_stack_p = 330 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 331 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 332 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 333 334 if (pxe_p == NULL) 335 return; 336 337 pxe_call(PXENV_UNDI_SHUTDOWN); 338 if (undi_shutdown_p->Status != 0) 339 panic("pxe_cleanup: UNDI_SHUTDOWN failed %x", 340 undi_shutdown_p->Status); 341 342 pxe_call(PXENV_UNLOAD_STACK); 343 if (unload_stack_p->Status != 0) 344 panic("pxe_cleanup: UNLOAD_STACK failed %x", 345 unload_stack_p->Status); 346} 347 348void 349pxe_perror(int err) 350{ 351 return; 352} 353 354void 355pxe_call(int func) 356{ 357 bzero(&v86, sizeof(v86)); 358 bzero(data_buffer, sizeof(data_buffer)); 359 360 __pxeseg = pxe_p->EntryPointSP.segment; 361 __pxeoff = pxe_p->EntryPointSP.offset; 362 363 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 364 v86.edx = VTOPSEG(scratch_buffer); 365 v86.eax = VTOPOFF(scratch_buffer); 366 v86.addr = (VTOPSEG(__h0h0magic) << 16) | VTOPOFF(__h0h0magic); 367 v86.ebx = func; 368 v86int(); 369 v86.ctl = V86_FLAGS; 370} 371 372 373time_t 374getsecs() 375{ 376 time_t n = 0; 377 time(&n); 378 return n; 379} 380 381static int 382pxe_netif_match(struct netif *nif, void *machdep_hint) 383{ 384 return 1; 385} 386 387 388static int 389pxe_netif_probe(struct netif *nif, void *machdep_hint) 390{ 391 t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 392 393 if (pxe_p == NULL) 394 return -1; 395 396 bzero(udpopen_p, sizeof(*udpopen_p)); 397 udpopen_p->src_ip = bootplayer.yip; 398 pxe_call(PXENV_UDP_OPEN); 399 400 if (udpopen_p->status != 0) { 401 printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 402 return -1; 403 } 404 return 0; 405} 406 407static void 408pxe_netif_end(struct netif *nif) 409{ 410 t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 411 bzero(udpclose_p, sizeof(*udpclose_p)); 412 413 pxe_call(PXENV_UDP_CLOSE); 414 if (udpclose_p->status != 0) 415 printf("pxe_end failed %x\n", udpclose_p->status); 416} 417 418static void 419pxe_netif_init(struct iodesc *desc, void *machdep_hint) 420{ 421} 422 423static int 424pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 425{ 426 return len; 427} 428 429static int 430pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 431{ 432 return len; 433} 434 435ssize_t 436sendudp(struct iodesc *h, void *pkt, size_t len) 437{ 438 t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 439 bzero(udpwrite_p, sizeof(*udpwrite_p)); 440 441 udpwrite_p->ip = bootplayer.sip; 442 udpwrite_p->dst_port = h->destport; 443 udpwrite_p->src_port = h->myport; 444 udpwrite_p->buffer_size = len; 445 udpwrite_p->buffer.segment = VTOPSEG(pkt); 446 udpwrite_p->buffer.offset = VTOPOFF(pkt); 447 448 pxe_call(PXENV_UDP_WRITE); 449 450#if 0 451 /* XXX - I dont know why we need this. */ 452 delay(1000); 453#endif 454 if (udpwrite_p->status != 0) { 455 printf("sendudp failed %x\n", udpwrite_p->status); 456 return -1; 457 } 458 return len; 459} 460 461ssize_t 462readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 463{ 464 t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 465 struct udphdr *uh = NULL; 466 467 uh = (struct udphdr *) pkt - 1; 468 bzero(udpread_p, sizeof(*udpread_p)); 469 470 udpread_p->dest_ip = bootplayer.yip; 471 udpread_p->d_port = h->myport; 472 udpread_p->buffer_size = len; 473 udpread_p->buffer.segment = VTOPSEG(data_buffer); 474 udpread_p->buffer.offset = VTOPOFF(data_buffer); 475 476 pxe_call(PXENV_UDP_READ); 477 478#if 0 479 /* XXX - I dont know why we need this. */ 480 delay(1000); 481#endif 482 if (udpread_p->status != 0) { 483 /* XXX: This happens a lot. It shouldn't. */ 484 if (udpread_p->status != 1) 485 printf("readudp failed %x\n", udpread_p->status); 486 return -1; 487 } 488 bcopy(data_buffer, pkt, udpread_p->buffer_size); 489 uh->uh_sport = udpread_p->s_port; 490 return udpread_p->buffer_size; 491} 492