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