1 /*- 2 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> 3 * Copyright (c) 2000 Paul Saab <ps@freebsd.org> 4 * All rights reserved. 5 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <stand.h> 33 #include <stddef.h> 34 #include <string.h> 35 #include <stdarg.h> 36 #include <sys/param.h> 37 38 #include <net/ethernet.h> 39 #include <netinet/in_systm.h> 40 #include <netinet/in.h> 41 #include <netinet/ip.h> 42 #include <netinet/udp.h> 43 44 #include <net.h> 45 #include <netif.h> 46 #include <nfsv2.h> 47 #include <iodesc.h> 48 49 #include <bootp.h> 50 #include <bootstrap.h> 51 #include "libi386.h" 52 #include "btxv86.h" 53 #include "pxe.h" 54 55 static pxenv_t *pxenv_p = NULL; /* PXENV+ */ 56 static pxe_t *pxe_p = NULL; /* !PXE */ 57 58 #ifdef PXE_DEBUG 59 static int pxe_debug = 0; 60 #endif 61 62 void pxe_enable(void *pxeinfo); 63 static void (*pxe_call)(int func, void *ptr); 64 static void pxenv_call(int func, void *ptr); 65 static void bangpxe_call(int func, void *ptr); 66 67 static int pxe_init(void); 68 static int pxe_print(int verbose); 69 static void pxe_cleanup(void); 70 71 static void pxe_perror(int error); 72 static int pxe_netif_match(struct netif *nif, void *machdep_hint); 73 static int pxe_netif_probe(struct netif *nif, void *machdep_hint); 74 static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 75 static ssize_t pxe_netif_get(struct iodesc *, void **, time_t); 76 static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 77 static void pxe_netif_end(struct netif *nif); 78 79 extern struct netif_stats pxe_st[]; 80 extern uint16_t __bangpxeseg; 81 extern uint16_t __bangpxeoff; 82 extern void __bangpxeentry(void); 83 extern uint16_t __pxenvseg; 84 extern uint16_t __pxenvoff; 85 extern void __pxenventry(void); 86 87 struct netif_dif pxe_ifs[] = { 88 /* dif_unit dif_nsel dif_stats dif_private */ 89 {0, 1, &pxe_st[0], 0} 90 }; 91 92 struct netif_stats pxe_st[nitems(pxe_ifs)]; 93 94 struct netif_driver pxenetif = { 95 .netif_bname = "pxenet", 96 .netif_match = pxe_netif_match, 97 .netif_probe = pxe_netif_probe, 98 .netif_init = pxe_netif_init, 99 .netif_get = pxe_netif_get, 100 .netif_put = pxe_netif_put, 101 .netif_end = pxe_netif_end, 102 .netif_ifs = pxe_ifs, 103 .netif_nifs = nitems(pxe_ifs) 104 }; 105 106 struct netif_driver *netif_drivers[] = { 107 &pxenetif, 108 NULL 109 }; 110 111 struct devsw pxedisk = { 112 .dv_name = "net", 113 .dv_type = DEVT_NET, 114 .dv_init = pxe_init, 115 .dv_strategy = NULL, /* Will be set in pxe_init */ 116 .dv_open = NULL, /* Will be set in pxe_init */ 117 .dv_close = NULL, /* Will be set in pxe_init */ 118 .dv_ioctl = noioctl, 119 .dv_print = pxe_print, 120 .dv_cleanup = pxe_cleanup 121 }; 122 123 /* 124 * This function is called by the loader to enable PXE support if we 125 * are booted by PXE. The passed in pointer is a pointer to the PXENV+ 126 * structure. 127 */ 128 void 129 pxe_enable(void *pxeinfo) 130 { 131 pxenv_p = (pxenv_t *)pxeinfo; 132 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 133 pxenv_p->PXEPtr.offset); 134 pxe_call = NULL; 135 } 136 137 /* 138 * return true if pxe structures are found/initialized, 139 * also figures out our IP information via the pxe cached info struct 140 */ 141 static int 142 pxe_init(void) 143 { 144 t_PXENV_GET_CACHED_INFO *gci_p; 145 int counter; 146 uint8_t checksum; 147 uint8_t *checkptr; 148 extern struct devsw netdev; 149 150 if (pxenv_p == NULL) 151 return (0); 152 153 /* look for "PXENV+" */ 154 if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { 155 pxenv_p = NULL; 156 return (0); 157 } 158 159 /* make sure the size is something we can handle */ 160 if (pxenv_p->Length > sizeof(*pxenv_p)) { 161 printf("PXENV+ structure too large, ignoring\n"); 162 pxenv_p = NULL; 163 return (0); 164 } 165 166 /* 167 * do byte checksum: 168 * add up each byte in the structure, the total should be 0 169 */ 170 checksum = 0; 171 checkptr = (uint8_t *) pxenv_p; 172 for (counter = 0; counter < pxenv_p->Length; counter++) 173 checksum += *checkptr++; 174 if (checksum != 0) { 175 printf("PXENV+ structure failed checksum, ignoring\n"); 176 pxenv_p = NULL; 177 return (0); 178 } 179 180 /* 181 * PXENV+ passed, so use that if !PXE is not available or 182 * the checksum fails. 183 */ 184 pxe_call = pxenv_call; 185 if (pxenv_p->Version >= 0x0200) { 186 for (;;) { 187 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 188 pxe_p = NULL; 189 break; 190 } 191 checksum = 0; 192 checkptr = (uint8_t *)pxe_p; 193 for (counter = 0; counter < pxe_p->StructLength; 194 counter++) 195 checksum += *checkptr++; 196 if (checksum != 0) { 197 pxe_p = NULL; 198 break; 199 } 200 pxe_call = bangpxe_call; 201 break; 202 } 203 } 204 205 pxedisk.dv_open = netdev.dv_open; 206 pxedisk.dv_close = netdev.dv_close; 207 pxedisk.dv_strategy = netdev.dv_strategy; 208 209 printf("\nPXE version %d.%d, real mode entry point ", 210 (uint8_t) (pxenv_p->Version >> 8), 211 (uint8_t) (pxenv_p->Version & 0xFF)); 212 if (pxe_call == bangpxe_call) 213 printf("@%04x:%04x\n", 214 pxe_p->EntryPointSP.segment, 215 pxe_p->EntryPointSP.offset); 216 else 217 printf("@%04x:%04x\n", 218 pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); 219 220 gci_p = bio_alloc(sizeof(*gci_p)); 221 if (gci_p == NULL) { 222 pxe_p = NULL; 223 return (0); 224 } 225 bzero(gci_p, sizeof(*gci_p)); 226 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 227 pxe_call(PXENV_GET_CACHED_INFO, gci_p); 228 if (gci_p->Status != 0) { 229 pxe_perror(gci_p->Status); 230 bio_free(gci_p, sizeof(*gci_p)); 231 pxe_p = NULL; 232 return (0); 233 } 234 free(bootp_response); 235 if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { 236 bootp_response_size = gci_p->BufferSize; 237 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 238 bootp_response, bootp_response_size); 239 } 240 bio_free(gci_p, sizeof(*gci_p)); 241 return (1); 242 } 243 244 static int 245 pxe_print(int verbose) 246 { 247 if (pxe_call == NULL) 248 return (0); 249 250 printf("%s devices:", pxedisk.dv_name); 251 if (pager_output("\n") != 0) 252 return (1); 253 printf(" %s0:", pxedisk.dv_name); 254 if (verbose) { 255 printf(" %s:%s", inet_ntoa(rootip), rootpath); 256 } 257 return (pager_output("\n")); 258 } 259 260 static void 261 pxe_cleanup(void) 262 { 263 t_PXENV_UNLOAD_STACK *unload_stack_p; 264 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p; 265 266 if (pxe_call == NULL) 267 return; 268 269 undi_shutdown_p = bio_alloc(sizeof(*undi_shutdown_p)); 270 if (undi_shutdown_p != NULL) { 271 bzero(undi_shutdown_p, sizeof(*undi_shutdown_p)); 272 pxe_call(PXENV_UNDI_SHUTDOWN, undi_shutdown_p); 273 274 #ifdef PXE_DEBUG 275 if (pxe_debug && undi_shutdown_p->Status != 0) 276 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 277 undi_shutdown_p->Status); 278 #endif 279 bio_free(undi_shutdown_p, sizeof(*undi_shutdown_p)); 280 } 281 282 unload_stack_p = bio_alloc(sizeof(*unload_stack_p)); 283 if (unload_stack_p != NULL) { 284 bzero(unload_stack_p, sizeof(*unload_stack_p)); 285 pxe_call(PXENV_UNLOAD_STACK, unload_stack_p); 286 287 #ifdef PXE_DEBUG 288 if (pxe_debug && unload_stack_p->Status != 0) 289 printf("pxe_cleanup: UNLOAD_STACK failed %x\n", 290 unload_stack_p->Status); 291 #endif 292 bio_free(unload_stack_p, sizeof(*unload_stack_p)); 293 } 294 } 295 296 void 297 pxe_perror(int err) 298 { 299 return; 300 } 301 302 void 303 pxenv_call(int func, void *ptr) 304 { 305 #ifdef PXE_DEBUG 306 if (pxe_debug) 307 printf("pxenv_call %x\n", func); 308 #endif 309 310 bzero(&v86, sizeof(v86)); 311 312 __pxenvseg = pxenv_p->RMEntry.segment; 313 __pxenvoff = pxenv_p->RMEntry.offset; 314 315 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 316 v86.es = VTOPSEG(ptr); 317 v86.edi = VTOPOFF(ptr); 318 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 319 v86.ebx = func; 320 v86int(); 321 v86.ctl = V86_FLAGS; 322 } 323 324 void 325 bangpxe_call(int func, void *ptr) 326 { 327 #ifdef PXE_DEBUG 328 if (pxe_debug) 329 printf("bangpxe_call %x\n", func); 330 #endif 331 332 bzero(&v86, sizeof(v86)); 333 334 __bangpxeseg = pxe_p->EntryPointSP.segment; 335 __bangpxeoff = pxe_p->EntryPointSP.offset; 336 337 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 338 v86.edx = VTOPSEG(ptr); 339 v86.eax = VTOPOFF(ptr); 340 v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); 341 v86.ebx = func; 342 v86int(); 343 v86.ctl = V86_FLAGS; 344 } 345 346 347 static int 348 pxe_netif_match(struct netif *nif, void *machdep_hint) 349 { 350 return (1); 351 } 352 353 static int 354 pxe_netif_probe(struct netif *nif, void *machdep_hint) 355 { 356 if (pxe_call == NULL) 357 return (-1); 358 359 return (0); 360 } 361 362 static void 363 pxe_netif_end(struct netif *nif) 364 { 365 t_PXENV_UNDI_CLOSE *undi_close_p; 366 367 undi_close_p = bio_alloc(sizeof(*undi_close_p)); 368 if (undi_close_p != NULL) { 369 bzero(undi_close_p, sizeof(*undi_close_p)); 370 pxe_call(PXENV_UNDI_CLOSE, undi_close_p); 371 if (undi_close_p->Status != 0) 372 printf("undi close failed: %x\n", undi_close_p->Status); 373 bio_free(undi_close_p, sizeof(*undi_close_p)); 374 } 375 } 376 377 static void 378 pxe_netif_init(struct iodesc *desc, void *machdep_hint) 379 { 380 t_PXENV_UNDI_GET_INFORMATION *undi_info_p; 381 t_PXENV_UNDI_OPEN *undi_open_p; 382 uint8_t *mac; 383 int i, len; 384 385 undi_info_p = bio_alloc(sizeof(*undi_info_p)); 386 if (undi_info_p == NULL) 387 return; 388 389 bzero(undi_info_p, sizeof(*undi_info_p)); 390 pxe_call(PXENV_UNDI_GET_INFORMATION, undi_info_p); 391 if (undi_info_p->Status != 0) { 392 printf("undi get info failed: %x\n", undi_info_p->Status); 393 bio_free(undi_info_p, sizeof(*undi_info_p)); 394 return; 395 } 396 397 /* Make sure the CurrentNodeAddress is valid. */ 398 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 399 if (undi_info_p->CurrentNodeAddress[i] != 0) 400 break; 401 } 402 if (i < undi_info_p->HwAddrLen) { 403 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 404 if (undi_info_p->CurrentNodeAddress[i] != 0xff) 405 break; 406 } 407 } 408 if (i < undi_info_p->HwAddrLen) 409 mac = undi_info_p->CurrentNodeAddress; 410 else 411 mac = undi_info_p->PermNodeAddress; 412 413 len = min(sizeof (desc->myea), undi_info_p->HwAddrLen); 414 for (i = 0; i < len; ++i) 415 desc->myea[i] = mac[i]; 416 417 if (bootp_response != NULL) 418 desc->xid = bootp_response->bp_xid; 419 else 420 desc->xid = 0; 421 422 bio_free(undi_info_p, sizeof(*undi_info_p)); 423 undi_open_p = bio_alloc(sizeof(*undi_open_p)); 424 if (undi_open_p == NULL) 425 return; 426 bzero(undi_open_p, sizeof(*undi_open_p)); 427 undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; 428 pxe_call(PXENV_UNDI_OPEN, undi_open_p); 429 if (undi_open_p->Status != 0) 430 printf("undi open failed: %x\n", undi_open_p->Status); 431 bio_free(undi_open_p, sizeof(*undi_open_p)); 432 } 433 434 static int 435 pxe_netif_receive(void **pkt) 436 { 437 t_PXENV_UNDI_ISR *isr; 438 char *buf, *ptr, *frame; 439 size_t size, rsize; 440 441 isr = bio_alloc(sizeof(*isr)); 442 if (isr == NULL) 443 return (-1); 444 445 bzero(isr, sizeof(*isr)); 446 isr->FuncFlag = PXENV_UNDI_ISR_IN_START; 447 pxe_call(PXENV_UNDI_ISR, isr); 448 if (isr->Status != 0) { 449 bio_free(isr, sizeof(*isr)); 450 return (-1); 451 } 452 453 bzero(isr, sizeof(*isr)); 454 isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; 455 pxe_call(PXENV_UNDI_ISR, isr); 456 if (isr->Status != 0) { 457 bio_free(isr, sizeof(*isr)); 458 return (-1); 459 } 460 461 while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { 462 /* 463 * Wait till transmit is done. 464 */ 465 bzero(isr, sizeof(*isr)); 466 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 467 pxe_call(PXENV_UNDI_ISR, isr); 468 if (isr->Status != 0 || 469 isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { 470 bio_free(isr, sizeof(*isr)); 471 return (-1); 472 } 473 } 474 475 while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { 476 if (isr->Status != 0 || 477 isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { 478 bio_free(isr, sizeof(*isr)); 479 return (-1); 480 } 481 bzero(isr, sizeof(*isr)); 482 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 483 pxe_call(PXENV_UNDI_ISR, isr); 484 } 485 486 size = isr->FrameLength; 487 buf = malloc(size + ETHER_ALIGN); 488 if (buf == NULL) { 489 bio_free(isr, sizeof(*isr)); 490 return (-1); 491 } 492 ptr = buf + ETHER_ALIGN; 493 rsize = 0; 494 495 while (rsize < size) { 496 frame = (char *)((uintptr_t)isr->Frame.segment << 4); 497 frame += isr->Frame.offset; 498 bcopy(PTOV(frame), ptr, isr->BufferLength); 499 ptr += isr->BufferLength; 500 rsize += isr->BufferLength; 501 502 bzero(isr, sizeof(*isr)); 503 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 504 pxe_call(PXENV_UNDI_ISR, isr); 505 if (isr->Status != 0) { 506 bio_free(isr, sizeof(*isr)); 507 free(buf); 508 return (-1); 509 } 510 511 /* Did we got another update? */ 512 if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) 513 continue; 514 break; 515 } 516 517 *pkt = buf; 518 bio_free(isr, sizeof(*isr)); 519 return (rsize); 520 } 521 522 static ssize_t 523 pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) 524 { 525 time_t t; 526 void *ptr; 527 int ret = -1; 528 529 t = getsecs(); 530 while ((getsecs() - t) < timeout) { 531 ret = pxe_netif_receive(&ptr); 532 if (ret != -1) { 533 *pkt = ptr; 534 break; 535 } 536 } 537 return (ret); 538 } 539 540 static ssize_t 541 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 542 { 543 t_PXENV_UNDI_TRANSMIT *trans_p; 544 t_PXENV_UNDI_TBD *tbd_p; 545 char *data; 546 ssize_t rv = -1; 547 548 trans_p = bio_alloc(sizeof(*trans_p)); 549 tbd_p = bio_alloc(sizeof(*tbd_p)); 550 data = bio_alloc(len); 551 552 if (trans_p != NULL && tbd_p != NULL && data != NULL) { 553 bzero(trans_p, sizeof(*trans_p)); 554 bzero(tbd_p, sizeof(*tbd_p)); 555 556 trans_p->TBD.segment = VTOPSEG(tbd_p); 557 trans_p->TBD.offset = VTOPOFF(tbd_p); 558 559 tbd_p->ImmedLength = len; 560 tbd_p->Xmit.segment = VTOPSEG(data); 561 tbd_p->Xmit.offset = VTOPOFF(data); 562 bcopy(pkt, data, len); 563 564 pxe_call(PXENV_UNDI_TRANSMIT, trans_p); 565 if (trans_p->Status == 0) 566 rv = len; 567 } 568 569 bio_free(data, len); 570 bio_free(tbd_p, sizeof(*tbd_p)); 571 bio_free(trans_p, sizeof(*trans_p)); 572 return (rv); 573 } 574