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