1 /* $NetBSD: pxe.c,v 1.4 2002/03/26 08:02:05 kanaoka Exp $ */ 2 3 /* 4 * Copyright 2001 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> 40 * All rights reserved. 41 * Copyright (c) 2000 Paul Saab <ps@freebsd.org> 42 * All rights reserved. 43 * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> 44 * All rights reserved. 45 * 46 * Redistribution and use in source and binary forms, with or without 47 * modification, are permitted provided that the following conditions 48 * are met: 49 * 1. Redistributions of source code must retain the above copyright 50 * notice, this list of conditions and the following disclaimer. 51 * 2. Redistributions in binary form must reproduce the above copyright 52 * notice, this list of conditions and the following disclaimer in the 53 * documentation and/or other materials provided with the distribution. 54 * 55 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65 * SUCH DAMAGE. 66 */ 67 68 /* 69 * Support for the Intel Preboot Execution Environment (PXE). 70 * 71 * PXE provides a UDP implementation as well as a UNDI network device 72 * driver. UNDI is much more complicated to use than PXE UDP, so we 73 * use PXE UDP as a cheap and easy way to get PXE support. 74 */ 75 76 #include <sys/param.h> 77 #include <sys/socket.h> 78 79 #ifdef _STANDALONE 80 #include <lib/libkern/libkern.h> 81 #else 82 #include <string.h> 83 #endif 84 85 #include <netinet/in.h> 86 #include <netinet/in_systm.h> 87 #include <netinet/ip.h> 88 #include <netinet/ip_var.h> 89 #include <netinet/udp.h> 90 #include <netinet/udp_var.h> 91 92 #include <net/if_ether.h> 93 94 #include <lib/libsa/stand.h> 95 #include <lib/libsa/net.h> 96 #include <lib/libsa/netif.h> 97 #include <lib/libsa/bootp.h> 98 99 #include <libi386.h> 100 #include <bootinfo.h> 101 102 #include "pxeboot.h" 103 #include "pxe.h" 104 105 void (*pxe_call)(u_int16_t); 106 107 void pxecall_bangpxe(u_int16_t); /* pxe_call.S */ 108 void pxecall_pxenv(u_int16_t); /* pxe_call.S */ 109 110 char pxe_command_buf[256]; 111 112 BOOTPLAYER bootplayer; 113 114 /***************************************************************************** 115 * This section is a replacement for libsa/udp.c 116 *****************************************************************************/ 117 118 /* Caller must leave room for ethernet, ip, and udp headers in front!! */ 119 ssize_t 120 sendudp(struct iodesc *d, void *pkt, size_t len) 121 { 122 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf; 123 124 uw->status = 0; 125 126 uw->ip = d->destip.s_addr; 127 uw->gw = gateip.s_addr; 128 uw->src_port = d->myport; 129 uw->dst_port = d->destport; 130 uw->buffer_size = len; 131 uw->buffer.segment = VTOPSEG(pkt); 132 uw->buffer.offset = VTOPOFF(pkt); 133 134 pxe_call(PXENV_UDP_WRITE); 135 136 if (uw->status != PXENV_STATUS_SUCCESS) { 137 /* XXX This happens a lot; it shouldn't. */ 138 if (uw->status != PXENV_STATUS_FAILURE) 139 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n", 140 uw->status); 141 return (-1); 142 } 143 144 return (len); 145 } 146 147 /* 148 * Receive a UDP packet and validate it for us. 149 * Caller leaves room for the headers (Ether, IP, UDP). 150 */ 151 ssize_t 152 readudp(struct iodesc *d, void *pkt, size_t len, time_t tleft) 153 { 154 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf; 155 struct udphdr *uh; 156 struct ip *ip; 157 158 uh = (struct udphdr *)pkt - 1; 159 ip = (struct ip *)uh - 1; 160 161 bzero(ur, sizeof(*ur)); 162 163 ur->dest_ip = d->myip.s_addr; 164 ur->d_port = d->myport; 165 ur->buffer_size = len; 166 ur->buffer.segment = VTOPSEG(pkt); 167 ur->buffer.offset = VTOPOFF(pkt); 168 169 /* XXX Timeout unused. */ 170 171 pxe_call(PXENV_UDP_READ); 172 173 if (ur->status != PXENV_STATUS_SUCCESS) { 174 /* XXX This happens a lot; it shouldn't. */ 175 if (ur->status != PXENV_STATUS_FAILURE) 176 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n", 177 ur->status); 178 return (-1); 179 } 180 181 ip->ip_src.s_addr = ur->src_ip; 182 uh->uh_sport = ur->s_port; 183 uh->uh_dport = d->myport; 184 185 return (len); 186 } 187 188 /***************************************************************************** 189 * PXE "netif" back-end 190 *****************************************************************************/ 191 192 int pxe_netif_match(struct netif *, void *); 193 int pxe_netif_probe(struct netif *, void *); 194 void pxe_netif_init(struct iodesc *, void *); 195 int pxe_netif_get(struct iodesc *, void *, size_t, time_t); 196 int pxe_netif_put(struct iodesc *, void *, size_t); 197 void pxe_netif_end(struct netif *); 198 199 struct netif_stats pxe_netif_stats; 200 201 struct netif_dif pxe0_dif = { 202 0, /* unit */ 203 1, /* nsel */ 204 &pxe_netif_stats, 205 0, 206 0, 207 }; 208 209 struct netif_driver pxe_netif_driver = { 210 "pxe", /* netif_bname */ 211 pxe_netif_match, /* match */ 212 pxe_netif_probe, /* probe */ 213 pxe_netif_init, /* init */ 214 pxe_netif_get, /* get */ 215 pxe_netif_put, /* put */ 216 pxe_netif_end, /* end */ 217 &pxe0_dif, /* netif_ifs */ 218 1, /* netif_nifs */ 219 }; 220 221 int 222 pxe_netif_match(struct netif *nif, void *machdep_hint) 223 { 224 225 return (1); 226 } 227 228 int 229 pxe_netif_probe(struct netif *nif, void *machdep_hint) 230 { 231 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf; 232 233 pxe_init(); 234 235 if (pxe_call == NULL) 236 return (1); 237 238 bzero(uo, sizeof(*uo)); 239 240 uo->src_ip = bootplayer.yip; 241 242 pxe_call(PXENV_UDP_OPEN); 243 244 if (uo->status != PXENV_STATUS_SUCCESS) { 245 printf("pxe_netif_probe: PXENV_UDP_OPEN failed: 0x%x\n", 246 uo->status); 247 pxe_fini(); 248 return (1); 249 } 250 251 return (0); 252 } 253 254 void 255 pxe_netif_init(struct iodesc *desc, void *machdep_hint) 256 { 257 struct netif *nif = desc->io_netif; 258 259 bcopy(bootplayer.CAddr, desc->myea, ETHER_ADDR_LEN); 260 261 /* 262 * Since the PXE BIOS has already done DHCP, make sure we 263 * don't reuse any of its transaction IDs. 264 */ 265 desc->xid = bootplayer.ident; 266 267 printf("%s%d: Ethernet address %s\n", nif->nif_driver->netif_bname, 268 nif->nif_unit, ether_sprintf(desc->myea)); 269 } 270 271 int 272 pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 273 { 274 275 return (-1); 276 } 277 278 int 279 pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t tleft) 280 { 281 282 return (-1); 283 } 284 285 void 286 pxe_netif_end(struct netif *nif) 287 { 288 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf; 289 290 uc->status = 0; 291 292 pxe_call(PXENV_UDP_CLOSE); 293 294 if (uc->status != PXENV_STATUS_SUCCESS) 295 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n", 296 uc->status); 297 298 pxe_fini(); 299 } 300 301 /***************************************************************************** 302 * PXE initialization and support routines 303 *****************************************************************************/ 304 305 u_int16_t pxe_command_buf_seg; 306 u_int16_t pxe_command_buf_off; 307 308 extern u_int16_t bangpxe_off, bangpxe_seg; 309 extern u_int16_t pxenv_off, pxenv_seg; 310 311 static struct btinfo_netif bi_netif; 312 313 int 314 pxe_init(void) 315 { 316 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf; 317 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf; 318 pxenv_t *pxenv; 319 pxe_t *pxe; 320 char *cp; 321 int i; 322 u_int8_t cksum, *ucp; 323 324 /* 325 * Checking for the presence of PXE is a machine-dependent 326 * operation. On the IA-32, this can be done two ways: 327 * 328 * Int 0x1a function 0x5650 329 * 330 * Scan memory for the !PXE or PXENV+ signatures 331 * 332 * We do the latter, since the Int method returns a pointer 333 * to a deprecated structure (PXENV+). 334 */ 335 336 pxenv = NULL; 337 pxe = NULL; 338 339 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) { 340 if (pxenv == NULL) { 341 pxenv = (pxenv_t *)cp; 342 if (bcmp(pxenv->Signature, S_SIZE("PXENV+"))) 343 pxenv = NULL; 344 else { 345 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 346 i < pxenv->Length; i++) 347 cksum += ucp[i]; 348 if (cksum != 0) { 349 printf("pxe_init: bad cksum (0x%x) " 350 "for PXENV+ at 0x%lx\n", cksum, 351 (u_long) cp); 352 pxenv = NULL; 353 } 354 } 355 } 356 357 if (pxe == NULL) { 358 pxe = (pxe_t *)cp; 359 if (bcmp(pxe->Signature, S_SIZE("!PXE"))) 360 pxe = NULL; 361 else { 362 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 363 i < pxe->StructLength; i++) 364 cksum += ucp[i]; 365 if (cksum != 0) { 366 printf("pxe_init: bad cksum (0x%x) " 367 "for !PXE at 0x%lx\n", cksum, 368 (u_long) cp); 369 pxe = NULL; 370 } 371 } 372 } 373 374 if (pxe != NULL && pxenv != NULL) 375 break; 376 } 377 378 if (pxe == NULL && pxenv == NULL) { 379 printf("pxe_init: No PXE BIOS found.\n"); 380 return (1); 381 } 382 383 if (pxenv != NULL) { 384 printf("PXE BIOS Version %d.%d\n", 385 (pxenv->Version >> 8) & 0xff, pxenv->Version & 0xff); 386 if (pxenv->Version >= 0x0201 && pxe != NULL) { 387 /* 2.1 or greater -- don't use PXENV+ */ 388 pxenv = NULL; 389 } 390 } 391 392 if (pxe != NULL) { 393 pxe_call = pxecall_bangpxe; 394 bangpxe_off = pxe->EntryPointSP.offset; 395 bangpxe_seg = pxe->EntryPointSP.segment; 396 } else { 397 pxe_call = pxecall_pxenv; 398 pxenv_off = pxenv->RMEntry.offset; 399 pxenv_seg = pxenv->RMEntry.segment; 400 } 401 402 /* 403 * Pre-compute the segment/offset of the pxe_command_buf 404 * to make things nicer in the low-level calling glue. 405 */ 406 pxe_command_buf_seg = VTOPSEG(pxe_command_buf); 407 pxe_command_buf_off = VTOPOFF(pxe_command_buf); 408 409 /* 410 * Get the cached info from the server's Discovery reply packet. 411 */ 412 bzero(gci, sizeof(*gci)); 413 gci->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 414 pxe_call(PXENV_GET_CACHED_INFO); 415 if (gci->Status != PXENV_STATUS_SUCCESS) { 416 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n", 417 gci->Status); 418 return (1); 419 } 420 pvbcopy((void *)((gci->Buffer.segment << 4) + gci->Buffer.offset), 421 &bootplayer, gci->BufferSize); 422 423 /* 424 * Get network interface information. 425 */ 426 bzero(gnt, sizeof(*gnt)); 427 pxe_call(PXENV_UNDI_GET_NIC_TYPE); 428 429 if (gnt->Status != PXENV_STATUS_SUCCESS) { 430 printf("pxe_init: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n", 431 gnt->Status); 432 return (0); 433 } 434 435 switch (gnt->NicType) { 436 case PCI_NIC: 437 case CardBus_NIC: 438 strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); 439 bi_netif.bus = BI_BUS_PCI; 440 bi_netif.addr.tag = gnt->info.pci.BusDevFunc; 441 442 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); 443 444 printf("Using %s device at bus %d device %d function %d\n", 445 gnt->NicType == PCI_NIC ? "PCI" : "CardBus", 446 (gnt->info.pci.BusDevFunc >> 8) & 0xff, 447 (gnt->info.pci.BusDevFunc >> 3) & 0x1f, 448 gnt->info.pci.BusDevFunc & 0x7); 449 break; 450 451 case PnP_NIC: 452 /* XXX Make bootinfo work with this. */ 453 printf("Using PnP device at 0x%x\n", gnt->info.pnp.CardSelNum); 454 } 455 456 return (0); 457 } 458 459 void 460 pxe_fini(void) 461 { 462 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf; 463 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf; 464 465 if (pxe_call == NULL) 466 return; 467 468 pxe_call(PXENV_UNDI_SHUTDOWN); 469 470 if (shutdown->Status != PXENV_STATUS_SUCCESS) 471 printf("pxe_fini: PXENV_UNDI_SHUTDOWN failed: 0x%x\n", 472 shutdown->Status); 473 474 pxe_call(PXENV_UNLOAD_STACK); 475 476 if (unload->Status != PXENV_STATUS_SUCCESS) 477 printf("pxe_fini: PXENV_UNLOAD_STACK failed: 0x%x\n", 478 unload->Status); 479 } 480