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 <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 "btxv86.h" 51 #include "pxe.h" 52 53 /* 54 * Allocate the PXE buffers statically instead of sticking grimy fingers into 55 * BTX's private data area. The scratch buffer is used to send information to 56 * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. 57 */ 58 #define PXE_BUFFER_SIZE 0x2000 59 static char scratch_buffer[PXE_BUFFER_SIZE]; 60 static char data_buffer[PXE_BUFFER_SIZE]; 61 62 static pxenv_t *pxenv_p = NULL; /* PXENV+ */ 63 static pxe_t *pxe_p = NULL; /* !PXE */ 64 65 #ifdef PXE_DEBUG 66 static int pxe_debug = 0; 67 #endif 68 69 void pxe_enable(void *pxeinfo); 70 static void (*pxe_call)(int func); 71 static void pxenv_call(int func); 72 static void bangpxe_call(int func); 73 74 static int pxe_init(void); 75 static int pxe_print(int verbose); 76 static void pxe_cleanup(void); 77 78 static void pxe_perror(int error); 79 static int pxe_netif_match(struct netif *nif, void *machdep_hint); 80 static int pxe_netif_probe(struct netif *nif, void *machdep_hint); 81 static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 82 static ssize_t pxe_netif_get(struct iodesc *, void **, time_t); 83 static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 84 static void pxe_netif_end(struct netif *nif); 85 86 extern struct netif_stats pxe_st[]; 87 extern u_int16_t __bangpxeseg; 88 extern u_int16_t __bangpxeoff; 89 extern void __bangpxeentry(void); 90 extern u_int16_t __pxenvseg; 91 extern u_int16_t __pxenvoff; 92 extern void __pxenventry(void); 93 94 struct netif_dif pxe_ifs[] = { 95 /* dif_unit dif_nsel dif_stats dif_private */ 96 {0, 1, &pxe_st[0], 0} 97 }; 98 99 struct netif_stats pxe_st[nitems(pxe_ifs)]; 100 101 struct netif_driver pxenetif = { 102 .netif_bname = "pxenet", 103 .netif_match = pxe_netif_match, 104 .netif_probe = pxe_netif_probe, 105 .netif_init = pxe_netif_init, 106 .netif_get = pxe_netif_get, 107 .netif_put = pxe_netif_put, 108 .netif_end = pxe_netif_end, 109 .netif_ifs = pxe_ifs, 110 .netif_nifs = nitems(pxe_ifs) 111 }; 112 113 struct netif_driver *netif_drivers[] = { 114 &pxenetif, 115 NULL 116 }; 117 118 struct devsw pxedisk = { 119 .dv_name = "net", 120 .dv_type = DEVT_NET, 121 .dv_init = pxe_init, 122 .dv_strategy = NULL, /* Will be set in pxe_init */ 123 .dv_open = NULL, /* Will be set in pxe_init */ 124 .dv_close = NULL, /* Will be set in pxe_init */ 125 .dv_ioctl = noioctl, 126 .dv_print = pxe_print, 127 .dv_cleanup = pxe_cleanup 128 }; 129 130 /* 131 * This function is called by the loader to enable PXE support if we 132 * are booted by PXE. The passed in pointer is a pointer to the 133 * PXENV+ structure. 134 */ 135 void 136 pxe_enable(void *pxeinfo) 137 { 138 pxenv_p = (pxenv_t *)pxeinfo; 139 pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 140 pxenv_p->PXEPtr.offset); 141 pxe_call = NULL; 142 } 143 144 /* 145 * return true if pxe structures are found/initialized, 146 * also figures out our IP information via the pxe cached info struct 147 */ 148 static int 149 pxe_init(void) 150 { 151 t_PXENV_GET_CACHED_INFO *gci_p; 152 int counter; 153 uint8_t checksum; 154 uint8_t *checkptr; 155 extern struct devsw netdev; 156 157 if(pxenv_p == NULL) 158 return (0); 159 160 /* look for "PXENV+" */ 161 if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { 162 pxenv_p = NULL; 163 return (0); 164 } 165 166 /* make sure the size is something we can handle */ 167 if (pxenv_p->Length > sizeof(*pxenv_p)) { 168 printf("PXENV+ structure too large, ignoring\n"); 169 pxenv_p = NULL; 170 return (0); 171 } 172 173 /* 174 * do byte checksum: 175 * add up each byte in the structure, the total should be 0 176 */ 177 checksum = 0; 178 checkptr = (uint8_t *) pxenv_p; 179 for (counter = 0; counter < pxenv_p->Length; counter++) 180 checksum += *checkptr++; 181 if (checksum != 0) { 182 printf("PXENV+ structure failed checksum, ignoring\n"); 183 pxenv_p = NULL; 184 return (0); 185 } 186 187 /* 188 * PXENV+ passed, so use that if !PXE is not available or 189 * the checksum fails. 190 */ 191 pxe_call = pxenv_call; 192 if (pxenv_p->Version >= 0x0200) { 193 for (;;) { 194 if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { 195 pxe_p = NULL; 196 break; 197 } 198 checksum = 0; 199 checkptr = (uint8_t *)pxe_p; 200 for (counter = 0; counter < pxe_p->StructLength; 201 counter++) 202 checksum += *checkptr++; 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 gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 228 bzero(gci_p, sizeof(*gci_p)); 229 gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 230 pxe_call(PXENV_GET_CACHED_INFO); 231 if (gci_p->Status != 0) { 232 pxe_perror(gci_p->Status); 233 pxe_p = NULL; 234 return (0); 235 } 236 237 free(bootp_response); 238 if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { 239 bootp_response_size = gci_p->BufferSize; 240 bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 241 bootp_response, bootp_response_size); 242 } 243 244 return (1); 245 } 246 247 static int 248 pxe_print(int verbose) 249 { 250 if (pxe_call == NULL) 251 return (0); 252 253 printf("%s devices:", pxedisk.dv_name); 254 if (pager_output("\n") != 0) 255 return (1); 256 printf(" %s0:", pxedisk.dv_name); 257 if (verbose) { 258 printf(" %s:%s", inet_ntoa(rootip), rootpath); 259 } 260 return (pager_output("\n")); 261 } 262 263 static void 264 pxe_cleanup(void) 265 { 266 #ifdef PXE_DEBUG 267 t_PXENV_UNLOAD_STACK *unload_stack_p = 268 (t_PXENV_UNLOAD_STACK *)scratch_buffer; 269 t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 270 (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 271 #endif 272 273 if (pxe_call == NULL) 274 return; 275 276 pxe_call(PXENV_UNDI_SHUTDOWN); 277 278 #ifdef PXE_DEBUG 279 if (pxe_debug && undi_shutdown_p->Status != 0) 280 printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", 281 undi_shutdown_p->Status); 282 #endif 283 284 pxe_call(PXENV_UNLOAD_STACK); 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 } 292 293 void 294 pxe_perror(int err) 295 { 296 return; 297 } 298 299 void 300 pxenv_call(int func) 301 { 302 #ifdef PXE_DEBUG 303 if (pxe_debug) 304 printf("pxenv_call %x\n", func); 305 #endif 306 307 bzero(&v86, sizeof(v86)); 308 bzero(data_buffer, sizeof(data_buffer)); 309 310 __pxenvseg = pxenv_p->RMEntry.segment; 311 __pxenvoff = pxenv_p->RMEntry.offset; 312 313 v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 314 v86.es = VTOPSEG(scratch_buffer); 315 v86.edi = VTOPOFF(scratch_buffer); 316 v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); 317 v86.ebx = func; 318 v86int(); 319 v86.ctl = V86_FLAGS; 320 } 321 322 void 323 bangpxe_call(int func) 324 { 325 #ifdef PXE_DEBUG 326 if (pxe_debug) 327 printf("bangpxe_call %x\n", func); 328 #endif 329 330 bzero(&v86, sizeof(v86)); 331 bzero(data_buffer, sizeof(data_buffer)); 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(scratch_buffer); 338 v86.eax = VTOPOFF(scratch_buffer); 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 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 = (t_PXENV_UNDI_CLOSE *)scratch_buffer; 368 bzero(undi_close_p, sizeof(*undi_close_p)); 369 pxe_call(PXENV_UNDI_CLOSE); 370 if (undi_close_p->Status != 0) 371 printf("undi close failed: %x\n", undi_close_p->Status); 372 } 373 374 static void 375 pxe_netif_init(struct iodesc *desc, void *machdep_hint) 376 { 377 t_PXENV_UNDI_GET_INFORMATION *undi_info_p; 378 t_PXENV_UNDI_OPEN *undi_open_p; 379 uint8_t *mac; 380 int i, len; 381 382 undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer; 383 bzero(undi_info_p, sizeof(*undi_info_p)); 384 pxe_call(PXENV_UNDI_GET_INFORMATION); 385 if (undi_info_p->Status != 0) { 386 printf("undi get info failed: %x\n", undi_info_p->Status); 387 return; 388 } 389 390 /* Make sure the CurrentNodeAddress is valid. */ 391 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 392 if (undi_info_p->CurrentNodeAddress[i] != 0) 393 break; 394 } 395 if (i < undi_info_p->HwAddrLen) { 396 for (i = 0; i < undi_info_p->HwAddrLen; ++i) { 397 if (undi_info_p->CurrentNodeAddress[i] != 0xff) 398 break; 399 } 400 } 401 if (i < undi_info_p->HwAddrLen) 402 mac = undi_info_p->CurrentNodeAddress; 403 else 404 mac = undi_info_p->PermNodeAddress; 405 406 len = min(sizeof (desc->myea), undi_info_p->HwAddrLen); 407 for (i = 0; i < len; ++i) { 408 desc->myea[i] = mac[i]; 409 } 410 411 if (bootp_response != NULL) 412 desc->xid = bootp_response->bp_xid; 413 else 414 desc->xid = 0; 415 416 undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer; 417 bzero(undi_open_p, sizeof(*undi_open_p)); 418 undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; 419 pxe_call(PXENV_UNDI_OPEN); 420 if (undi_open_p->Status != 0) 421 printf("undi open failed: %x\n", undi_open_p->Status); 422 } 423 424 static int 425 pxe_netif_receive(void **pkt) 426 { 427 t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer; 428 char *buf, *ptr, *frame; 429 size_t size, rsize; 430 431 bzero(isr, sizeof(*isr)); 432 isr->FuncFlag = PXENV_UNDI_ISR_IN_START; 433 pxe_call(PXENV_UNDI_ISR); 434 if (isr->Status != 0) 435 return (-1); 436 437 bzero(isr, sizeof(*isr)); 438 isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; 439 pxe_call(PXENV_UNDI_ISR); 440 if (isr->Status != 0) 441 return (-1); 442 443 while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { 444 /* 445 * Wait till transmit is done. 446 */ 447 bzero(isr, sizeof(*isr)); 448 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 449 pxe_call(PXENV_UNDI_ISR); 450 if (isr->Status != 0 || 451 isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) 452 return (-1); 453 } 454 455 while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { 456 if (isr->Status != 0 || 457 isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { 458 return (-1); 459 } 460 bzero(isr, sizeof(*isr)); 461 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 462 pxe_call(PXENV_UNDI_ISR); 463 } 464 465 size = isr->FrameLength; 466 buf = malloc(size + ETHER_ALIGN); 467 if (buf == NULL) 468 return (-1); 469 ptr = buf + ETHER_ALIGN; 470 rsize = 0; 471 472 while (rsize < size) { 473 frame = (char *)((uintptr_t)isr->Frame.segment << 4); 474 frame += isr->Frame.offset; 475 bcopy(PTOV(frame), ptr, isr->BufferLength); 476 ptr += isr->BufferLength; 477 rsize += isr->BufferLength; 478 479 bzero(isr, sizeof(*isr)); 480 isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; 481 pxe_call(PXENV_UNDI_ISR); 482 if (isr->Status != 0) { 483 free(buf); 484 return (-1); 485 } 486 487 /* Did we got another update? */ 488 if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) 489 continue; 490 break; 491 } 492 493 *pkt = buf; 494 return (rsize); 495 } 496 497 static ssize_t 498 pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) 499 { 500 time_t t; 501 void *ptr; 502 ssize_t ret = -1; 503 504 t = getsecs(); 505 while ((getsecs() - t) < timeout) { 506 ret = pxe_netif_receive(&ptr); 507 if (ret != -1) { 508 *pkt = ptr; 509 break; 510 } 511 } 512 return (ret); 513 } 514 515 static ssize_t 516 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 517 { 518 t_PXENV_UNDI_TRANSMIT *trans_p; 519 t_PXENV_UNDI_TBD *tbd_p; 520 char *data; 521 522 trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer; 523 bzero(trans_p, sizeof(*trans_p)); 524 tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p)); 525 bzero(tbd_p, sizeof(*tbd_p)); 526 527 data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p); 528 529 trans_p->TBD.segment = VTOPSEG(tbd_p); 530 trans_p->TBD.offset = VTOPOFF(tbd_p); 531 532 tbd_p->ImmedLength = len; 533 tbd_p->Xmit.segment = VTOPSEG(data); 534 tbd_p->Xmit.offset = VTOPOFF(data); 535 bcopy(pkt, data, len); 536 537 pxe_call(PXENV_UNDI_TRANSMIT); 538 if (trans_p->Status != 0) { 539 return (-1); 540 } 541 542 return (len); 543 } 544