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 #include <stand.h> 31 #include <errno.h> 32 #include <stdbool.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_isr(t_PXENV_UNDI_ISR *isr, void **pkt, ssize_t *retsize) 436 { 437 static bool data_pending; 438 char *buf, *ptr, *frame; 439 size_t size, rsize; 440 441 buf = NULL; 442 size = rsize = 0; 443 444 /* 445 * We can save ourselves the next two pxe calls because we already know 446 * we weren't done grabbing everything. 447 */ 448 if (data_pending) { 449 data_pending = false; 450 goto nextbuf; 451 } 452 453 /* 454 * We explicitly don't check for OURS/NOT_OURS as a result of START; 455 * it's been reported that some cards are known to mishandle these. 456 */ 457 bzero(isr, sizeof(*isr)); 458 isr->FuncFlag = PXENV_UNDI_ISR_IN_START; 459 pxe_call(PXENV_UNDI_ISR, isr); 460 /* We could translate Status... */ 461 if (isr->Status != 0) { 462 return (ENXIO); 463 } 464 465 bzero(isr, sizeof(*isr)); 466 isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; 467 pxe_call(PXENV_UNDI_ISR, isr); 468 if (isr->Status != 0) { 469 return (ENXIO); 470 } 471 if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_BUSY) { 472 /* 473 * Let the caller decide if we need to be restarted. It will 474 * currently blindly restart us, but it could check timeout in 475 * the future. 476 */ 477 return (ERESTART); 478 } 479 480 /* 481 * By design, we'll hardly ever hit this terminal condition unless we 482 * pick up nothing but tx interrupts here. More frequently, we will 483 * process rx buffers until we hit the terminal condition in the middle. 484 */ 485 while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_DONE) { 486 /* 487 * This might have given us PXENV_UNDI_ISR_OUT_TRANSMIT, in 488 * which case we can just disregard and move on to the next 489 * buffer/frame. 490 */ 491 if (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) 492 goto nextbuf; 493 494 if (buf == NULL) { 495 /* 496 * Grab size from the first Frame that we picked up, 497 * allocate an rx buf to hold. Careful here, as we may 498 * see a fragmented frame that's spread out across 499 * multiple GET_NEXT calls. 500 */ 501 size = isr->FrameLength; 502 buf = malloc(size + ETHER_ALIGN); 503 if (buf == NULL) 504 return (ENOMEM); 505 506 ptr = buf + ETHER_ALIGN; 507 } 508 509 frame = (char *)((uintptr_t)isr->Frame.segment << 4); 510 frame += isr->Frame.offset; 511 bcopy(PTOV(frame), ptr, isr->BufferLength); 512 ptr += isr->BufferLength; 513 rsize += isr->BufferLength; 514 515 /* 516 * Stop here before we risk catching the start of another frame. 517 * It would be nice to continue reading until we actually get a 518 * PXENV_UNDI_ISR_OUT_DONE, but our network stack in libsa isn't 519 * suitable for reading more than one packet at a time. 520 */ 521 if (rsize >= size) { 522 data_pending = true; 523 break; 524 } 525 526 nextbuf: 527 bzero(isr, sizeof(*isr)); 528 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 529 pxe_call(PXENV_UNDI_ISR, isr); 530 if (isr->Status != 0) { 531 free(buf); 532 return (ENXIO); 533 } 534 } 535 536 /* 537 * We may have never picked up a frame at all (all tx), in which case 538 * the caller should restart us. 539 */ 540 if (rsize == 0) { 541 return (ERESTART); 542 } 543 544 *pkt = buf; 545 *retsize = rsize; 546 return (0); 547 } 548 549 static int 550 pxe_netif_receive(void **pkt, ssize_t *size) 551 { 552 t_PXENV_UNDI_ISR *isr; 553 int ret; 554 555 isr = bio_alloc(sizeof(*isr)); 556 if (isr == NULL) 557 return (ENOMEM); 558 559 /* 560 * This completely ignores the timeout specified in pxe_netif_get(), but 561 * we shouldn't be running long enough here for that to make a 562 * difference. 563 */ 564 for (;;) { 565 /* We'll only really re-enter for PXENV_UNDI_ISR_OUT_BUSY. */ 566 ret = pxe_netif_receive_isr(isr, pkt, size); 567 if (ret != ERESTART) 568 break; 569 } 570 571 bio_free(isr, sizeof(*isr)); 572 return (ret); 573 } 574 575 static ssize_t 576 pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) 577 { 578 time_t t; 579 void *ptr; 580 int ret = -1; 581 ssize_t size; 582 583 t = getsecs(); 584 size = 0; 585 while ((getsecs() - t) < timeout) { 586 ret = pxe_netif_receive(&ptr, &size); 587 if (ret != -1) { 588 *pkt = ptr; 589 break; 590 } 591 } 592 593 return (ret == 0 ? size : -1); 594 } 595 596 static ssize_t 597 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 598 { 599 t_PXENV_UNDI_TRANSMIT *trans_p; 600 t_PXENV_UNDI_TBD *tbd_p; 601 char *data; 602 ssize_t rv = -1; 603 604 trans_p = bio_alloc(sizeof(*trans_p)); 605 tbd_p = bio_alloc(sizeof(*tbd_p)); 606 data = bio_alloc(len); 607 608 if (trans_p != NULL && tbd_p != NULL && data != NULL) { 609 bzero(trans_p, sizeof(*trans_p)); 610 bzero(tbd_p, sizeof(*tbd_p)); 611 612 trans_p->TBD.segment = VTOPSEG(tbd_p); 613 trans_p->TBD.offset = VTOPOFF(tbd_p); 614 615 tbd_p->ImmedLength = len; 616 tbd_p->Xmit.segment = VTOPSEG(data); 617 tbd_p->Xmit.offset = VTOPOFF(data); 618 bcopy(pkt, data, len); 619 620 pxe_call(PXENV_UNDI_TRANSMIT, trans_p); 621 if (trans_p->Status == 0) 622 rv = len; 623 } 624 625 bio_free(data, len); 626 bio_free(tbd_p, sizeof(*tbd_p)); 627 bio_free(trans_p, sizeof(*trans_p)); 628 return (rv); 629 } 630