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