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