1 /* 2 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> 3 * Copyright (c) 2000 Paul Saab <ps@freebsd.org> 4 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> 5 * All rights reserved. 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 31 #include <stand.h> 32 #include <errno.h> 33 #include <stdbool.h> 34 #include <stddef.h> 35 #include <string.h> 36 #include <stdarg.h> 37 #include <sys/param.h> 38 39 #include <net/ethernet.h> 40 #include <netinet/in_systm.h> 41 #include <netinet/in.h> 42 #include <netinet/ip.h> 43 #include <netinet/udp.h> 44 45 #include <net.h> 46 #include <netif.h> 47 #include <nfsv2.h> 48 #include <iodesc.h> 49 50 #include <bootp.h> 51 #include <bootstrap.h> 52 #include "libi386.h" 53 #include "btxv86.h" 54 #include "pxe.h" 55 56 static pxenv_t *pxenv_p = NULL; /* PXENV+ */ 57 static pxe_t *pxe_p = NULL; /* !PXE */ 58 59 #ifdef PXE_DEBUG 60 static int pxe_debug = 0; 61 #endif 62 63 void pxe_enable(void *pxeinfo); 64 static void (*pxe_call)(int func, void *ptr); 65 static void pxenv_call(int func, void *ptr); 66 static void bangpxe_call(int func, void *ptr); 67 68 static int pxe_init(void); 69 static int pxe_print(int verbose); 70 static void pxe_cleanup(void); 71 72 static void pxe_perror(int error); 73 static int pxe_netif_match(struct netif *nif, void *machdep_hint); 74 static int pxe_netif_probe(struct netif *nif, void *machdep_hint); 75 static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 76 static ssize_t pxe_netif_get(struct iodesc *, void **, time_t); 77 static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 78 static void pxe_netif_end(struct netif *nif); 79 80 extern struct netif_stats pxe_st[]; 81 extern uint16_t __bangpxeseg; 82 extern uint16_t __bangpxeoff; 83 extern void __bangpxeentry(void); 84 extern uint16_t __pxenvseg; 85 extern uint16_t __pxenvoff; 86 extern void __pxenventry(void); 87 88 struct netif_dif pxe_ifs[] = { 89 { 90 .dif_unit = 0, 91 .dif_nsel = 1, 92 .dif_stats = &pxe_st[0], 93 .dif_private = NULL, 94 .dif_used = 0 95 } 96 }; 97 98 struct netif_stats pxe_st[nitems(pxe_ifs)]; 99 100 struct netif_driver pxenetif = { 101 .netif_bname = "pxenet", 102 .netif_match = pxe_netif_match, 103 .netif_probe = pxe_netif_probe, 104 .netif_init = pxe_netif_init, 105 .netif_get = pxe_netif_get, 106 .netif_put = pxe_netif_put, 107 .netif_end = pxe_netif_end, 108 .netif_ifs = pxe_ifs, 109 .netif_nifs = nitems(pxe_ifs) 110 }; 111 112 struct netif_driver *netif_drivers[] = { 113 &pxenetif, 114 NULL 115 }; 116 117 struct devsw pxedisk = { 118 .dv_name = "net", 119 .dv_type = DEVT_NET, 120 .dv_init = pxe_init, 121 .dv_strategy = NULL, /* Will be set in pxe_init */ 122 .dv_open = NULL, /* Will be set in pxe_init */ 123 .dv_close = NULL, /* Will be set in pxe_init */ 124 .dv_ioctl = noioctl, 125 .dv_print = pxe_print, 126 .dv_cleanup = pxe_cleanup 127 }; 128 129 /* 130 * This function is called by the loader to enable PXE support if we 131 * are booted by PXE. The passed in pointer is a pointer to the 132 * PXENV+ structure. 133 */ 134 void 135 pxe_enable(void *pxeinfo) 136 { 137 pxenv_p = (pxenv_t *)pxeinfo; 138 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 139 pxenv_p->PXEPtr.offset); 140 pxe_call = NULL; 141 } 142 143 /* 144 * return true if pxe structures are found/initialized, 145 * also figures out our IP information via the pxe cached info struct 146 */ 147 static int 148 pxe_init(void) 149 { 150 t_PXENV_GET_CACHED_INFO *gci_p; 151 int counter; 152 uint8_t checksum; 153 uint8_t *checkptr; 154 extern struct devsw netdev; 155 156 if (pxenv_p == NULL) 157 return (0); 158 159 /* look for "PXENV+" */ 160 if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { 161 pxenv_p = NULL; 162 return (0); 163 } 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 /* 187 * PXENV+ passed, so use that if !PXE is not available or 188 * the checksum fails. 189 */ 190 pxe_call = pxenv_call; 191 if (pxenv_p->Version >= 0x0200) { 192 for (;;) { 193 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 194 pxe_p = NULL; 195 break; 196 } 197 checksum = 0; 198 checkptr = (uint8_t *)pxe_p; 199 for (counter = 0; counter < pxe_p->StructLength; 200 counter++) { 201 checksum += *checkptr++; 202 } 203 if (checksum != 0) { 204 pxe_p = NULL; 205 break; 206 } 207 pxe_call = bangpxe_call; 208 break; 209 } 210 } 211 212 pxedisk.dv_open = netdev.dv_open; 213 pxedisk.dv_close = netdev.dv_close; 214 pxedisk.dv_strategy = netdev.dv_strategy; 215 216 printf("\nPXE version %d.%d, real mode entry point ", 217 (uint8_t)(pxenv_p->Version >> 8), 218 (uint8_t)(pxenv_p->Version & 0xFF)); 219 if (pxe_call == bangpxe_call) { 220 printf("@%04x:%04x\n", 221 pxe_p->EntryPointSP.segment, 222 pxe_p->EntryPointSP.offset); 223 } else { 224 printf("@%04x:%04x\n", 225 pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); 226 } 227 228 gci_p = bio_alloc(sizeof (*gci_p)); 229 if (gci_p == NULL) { 230 pxe_p = NULL; 231 return (0); 232 } 233 bzero(gci_p, sizeof (*gci_p)); 234 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 235 pxe_call(PXENV_GET_CACHED_INFO, gci_p); 236 if (gci_p->Status != 0) { 237 pxe_perror(gci_p->Status); 238 bio_free(gci_p, sizeof (*gci_p)); 239 pxe_p = NULL; 240 return (0); 241 } 242 243 free(bootp_response); 244 if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { 245 bootp_response_size = gci_p->BufferSize; 246 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 247 bootp_response, bootp_response_size); 248 } 249 250 bio_free(gci_p, sizeof (*gci_p)); 251 return (1); 252 } 253 254 static int 255 pxe_print(int verbose) 256 { 257 if (pxe_call == NULL) 258 return (0); 259 260 printf("%s devices:", pxedisk.dv_name); 261 if (pager_output("\n") != 0) 262 return (1); 263 printf(" %s0:", pxedisk.dv_name); 264 if (verbose) { 265 printf(" %s:%s", inet_ntoa(rootip), rootpath); 266 } 267 return (pager_output("\n")); 268 } 269 270 static void 271 pxe_cleanup(void) 272 { 273 t_PXENV_UNLOAD_STACK *unload_stack_p; 274 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p; 275 276 if (pxe_call == NULL) 277 return; 278 279 undi_shutdown_p = bio_alloc(sizeof (*undi_shutdown_p)); 280 if (undi_shutdown_p != NULL) { 281 bzero(undi_shutdown_p, sizeof (*undi_shutdown_p)); 282 pxe_call(PXENV_UNDI_SHUTDOWN, undi_shutdown_p); 283 284 #ifdef PXE_DEBUG 285 if (pxe_debug && undi_shutdown_p->Status != 0) 286 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 287 undi_shutdown_p->Status); 288 #endif 289 bio_free(undi_shutdown_p, sizeof (*undi_shutdown_p)); 290 } 291 292 unload_stack_p = bio_alloc(sizeof (*unload_stack_p)); 293 if (unload_stack_p != NULL) { 294 bzero(unload_stack_p, sizeof (*unload_stack_p)); 295 pxe_call(PXENV_UNLOAD_STACK, unload_stack_p); 296 297 #ifdef PXE_DEBUG 298 if (pxe_debug && unload_stack_p->Status != 0) 299 printf("pxe_cleanup: UNLOAD_STACK failed %x\n", 300 unload_stack_p->Status); 301 #endif 302 bio_free(unload_stack_p, sizeof (*unload_stack_p)); 303 } 304 } 305 306 void 307 pxe_perror(int err __unused) 308 { 309 } 310 311 void 312 pxenv_call(int func, void *ptr) 313 { 314 #ifdef PXE_DEBUG 315 if (pxe_debug) 316 printf("pxenv_call %x\n", func); 317 #endif 318 319 bzero(&v86, sizeof (v86)); 320 321 __pxenvseg = pxenv_p->RMEntry.segment; 322 __pxenvoff = pxenv_p->RMEntry.offset; 323 324 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 325 v86.es = VTOPSEG(ptr); 326 v86.edi = VTOPOFF(ptr); 327 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 328 v86.ebx = func; 329 v86int(); 330 v86.ctl = V86_FLAGS; 331 } 332 333 void 334 bangpxe_call(int func, void *ptr) 335 { 336 #ifdef PXE_DEBUG 337 if (pxe_debug) 338 printf("bangpxe_call %x\n", func); 339 #endif 340 341 bzero(&v86, sizeof (v86)); 342 343 __bangpxeseg = pxe_p->EntryPointSP.segment; 344 __bangpxeoff = pxe_p->EntryPointSP.offset; 345 346 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 347 v86.edx = VTOPSEG(ptr); 348 v86.eax = VTOPOFF(ptr); 349 v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); 350 v86.ebx = func; 351 v86int(); 352 v86.ctl = V86_FLAGS; 353 } 354 355 356 static int 357 pxe_netif_match(struct netif *nif __unused, void *machdep_hint __unused) 358 { 359 return (1); 360 } 361 362 363 static int 364 pxe_netif_probe(struct netif *nif __unused, void *machdep_hint __unused) 365 { 366 if (pxe_call == NULL) 367 return (-1); 368 369 return (0); 370 } 371 372 static void 373 pxe_netif_end(struct netif *nif __unused) 374 { 375 t_PXENV_UNDI_CLOSE *undi_close_p; 376 377 undi_close_p = bio_alloc(sizeof (*undi_close_p)); 378 if (undi_close_p != NULL) { 379 bzero(undi_close_p, sizeof (*undi_close_p)); 380 pxe_call(PXENV_UNDI_CLOSE, undi_close_p); 381 if (undi_close_p->Status != 0) 382 printf("undi close failed: %x\n", undi_close_p->Status); 383 bio_free(undi_close_p, sizeof (*undi_close_p)); 384 } 385 } 386 387 static void 388 pxe_netif_init(struct iodesc *desc, void *machdep_hint __unused) 389 { 390 t_PXENV_UNDI_GET_INFORMATION *undi_info_p; 391 t_PXENV_UNDI_OPEN *undi_open_p; 392 uint8_t *mac; 393 int i, len; 394 395 undi_info_p = bio_alloc(sizeof (*undi_info_p)); 396 if (undi_info_p == NULL) 397 return; 398 399 bzero(undi_info_p, sizeof (*undi_info_p)); 400 pxe_call(PXENV_UNDI_GET_INFORMATION, undi_info_p); 401 if (undi_info_p->Status != 0) { 402 printf("undi get info failed: %x\n", undi_info_p->Status); 403 bio_free(undi_info_p, sizeof (*undi_info_p)); 404 return; 405 } 406 407 /* Make sure the CurrentNodeAddress is valid. */ 408 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 409 if (undi_info_p->CurrentNodeAddress[i] != 0) 410 break; 411 } 412 if (i < undi_info_p->HwAddrLen) { 413 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 414 if (undi_info_p->CurrentNodeAddress[i] != 0xff) 415 break; 416 } 417 } 418 if (i < undi_info_p->HwAddrLen) 419 mac = undi_info_p->CurrentNodeAddress; 420 else 421 mac = undi_info_p->PermNodeAddress; 422 423 len = min(sizeof (desc->myea), undi_info_p->HwAddrLen); 424 for (i = 0; i < len; ++i) { 425 desc->myea[i] = mac[i]; 426 } 427 428 if (bootp_response != NULL) 429 desc->xid = bootp_response->bp_xid; 430 else 431 desc->xid = 0; 432 433 bio_free(undi_info_p, sizeof (*undi_info_p)); 434 undi_open_p = bio_alloc(sizeof (*undi_open_p)); 435 if (undi_open_p == NULL) 436 return; 437 bzero(undi_open_p, sizeof (*undi_open_p)); 438 undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; 439 pxe_call(PXENV_UNDI_OPEN, undi_open_p); 440 if (undi_open_p->Status != 0) 441 printf("undi open failed: %x\n", undi_open_p->Status); 442 bio_free(undi_open_p, sizeof (*undi_open_p)); 443 } 444 445 static int 446 pxe_netif_receive_isr(t_PXENV_UNDI_ISR *isr, void **pkt, ssize_t *retsize) 447 { 448 static bool data_pending; 449 char *buf, *ptr, *frame; 450 size_t size, rsize; 451 452 buf = NULL; 453 size = rsize = 0; 454 455 /* 456 * We can save ourselves the next two pxe calls because we already know 457 * we weren't done grabbing everything. 458 */ 459 if (data_pending) { 460 data_pending = false; 461 goto nextbuf; 462 } 463 464 /* 465 * We explicitly don't check for OURS/NOT_OURS as a result of START; 466 * it's been reported that some cards are known to mishandle these. 467 */ 468 bzero(isr, sizeof (*isr)); 469 isr->FuncFlag = PXENV_UNDI_ISR_IN_START; 470 pxe_call(PXENV_UNDI_ISR, isr); 471 /* We could translate Status... */ 472 if (isr->Status != 0) { 473 return (ENXIO); 474 } 475 476 bzero(isr, sizeof (*isr)); 477 isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; 478 pxe_call(PXENV_UNDI_ISR, isr); 479 if (isr->Status != 0) { 480 return (ENXIO); 481 } 482 if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_BUSY) { 483 /* 484 * Let the caller decide if we need to be restarted. It will 485 * currently blindly restart us, but it could check timeout in 486 * the future. 487 */ 488 return (ERESTART); 489 } 490 491 /* 492 * By design, we'll hardly ever hit this terminal condition unless we 493 * pick up nothing but tx interrupts here. More frequently, we will 494 * process rx buffers until we hit the terminal condition in the middle. 495 */ 496 while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_DONE) { 497 /* 498 * This might have given us PXENV_UNDI_ISR_OUT_TRANSMIT, in 499 * which case we can just disregard and move on to the next 500 * buffer/frame. 501 */ 502 if (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) 503 goto nextbuf; 504 505 if (buf == NULL) { 506 /* 507 * Grab size from the first Frame that we picked up, 508 * allocate an rx buf to hold. Careful here, as we may 509 * see a fragmented frame that's spread out across 510 * multiple GET_NEXT calls. 511 */ 512 size = isr->FrameLength; 513 buf = malloc(size + ETHER_ALIGN); 514 if (buf == NULL) 515 return (ENOMEM); 516 517 ptr = buf + ETHER_ALIGN; 518 } 519 520 frame = (char *)((uintptr_t)isr->Frame.segment << 4); 521 frame += isr->Frame.offset; 522 bcopy(PTOV(frame), ptr, isr->BufferLength); 523 ptr += isr->BufferLength; 524 rsize += isr->BufferLength; 525 526 /* 527 * Stop here before we risk catching the start of another frame. 528 * It would be nice to continue reading until we actually get a 529 * PXENV_UNDI_ISR_OUT_DONE, but our network stack in libsa isn't 530 * suitable for reading more than one packet at a time. 531 */ 532 if (rsize >= size) { 533 data_pending = true; 534 break; 535 } 536 537 nextbuf: 538 bzero(isr, sizeof (*isr)); 539 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 540 pxe_call(PXENV_UNDI_ISR, isr); 541 if (isr->Status != 0) { 542 free(buf); 543 return (ENXIO); 544 } 545 } 546 547 /* 548 * We may have never picked up a frame at all (all tx), in which case 549 * the caller should restart us. 550 */ 551 if (rsize == 0) { 552 return (ERESTART); 553 } 554 555 *pkt = buf; 556 *retsize = rsize; 557 return (0); 558 } 559 560 static int 561 pxe_netif_receive(void **pkt, ssize_t *size) 562 { 563 t_PXENV_UNDI_ISR *isr; 564 int ret; 565 566 isr = bio_alloc(sizeof (*isr)); 567 if (isr == NULL) 568 return (ENOMEM); 569 570 /* 571 * This completely ignores the timeout specified in pxe_netif_get(), but 572 * we shouldn't be running long enough here for that to make a 573 * difference. 574 */ 575 for (;;) { 576 /* We'll only really re-enter for PXENV_UNDI_ISR_OUT_BUSY. */ 577 ret = pxe_netif_receive_isr(isr, pkt, size); 578 if (ret != ERESTART) 579 break; 580 } 581 582 bio_free(isr, sizeof (*isr)); 583 return (ret); 584 } 585 586 static ssize_t 587 pxe_netif_get(struct iodesc *desc __unused, void **pkt, time_t timeout) 588 { 589 time_t t; 590 void *ptr; 591 int ret = -1; 592 ssize_t size; 593 594 t = getsecs(); 595 size = 0; 596 while ((getsecs() - t) < timeout) { 597 ret = pxe_netif_receive(&ptr, &size); 598 if (ret != -1) { 599 *pkt = ptr; 600 break; 601 } 602 } 603 604 return (ret == 0 ? size : -1); 605 } 606 607 static ssize_t 608 pxe_netif_put(struct iodesc *desc __unused, void *pkt, size_t len) 609 { 610 t_PXENV_UNDI_TRANSMIT *trans_p; 611 t_PXENV_UNDI_TBD *tbd_p; 612 char *data; 613 ssize_t rv = -1; 614 615 trans_p = bio_alloc(sizeof (*trans_p)); 616 tbd_p = bio_alloc(sizeof (*tbd_p)); 617 data = bio_alloc(len); 618 619 if (trans_p != NULL && tbd_p != NULL && data != NULL) { 620 bzero(trans_p, sizeof (*trans_p)); 621 bzero(tbd_p, sizeof (*tbd_p)); 622 623 trans_p->TBD.segment = VTOPSEG(tbd_p); 624 trans_p->TBD.offset = VTOPOFF(tbd_p); 625 626 tbd_p->ImmedLength = len; 627 tbd_p->Xmit.segment = VTOPSEG(data); 628 tbd_p->Xmit.offset = VTOPOFF(data); 629 bcopy(pkt, data, len); 630 631 pxe_call(PXENV_UNDI_TRANSMIT, trans_p); 632 if (trans_p->Status == 0) 633 rv = len; 634 } 635 636 bio_free(data, len); 637 bio_free(tbd_p, sizeof (*tbd_p)); 638 bio_free(trans_p, sizeof (*trans_p)); 639 return (rv); 640 } 641