1 /* $NetBSD: pxe.c,v 1.8 2005/12/11 12:17:49 christos 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/bootp.h> 97 98 #include <libi386.h> 99 #include <bootinfo.h> 100 101 #include "pxeboot.h" 102 #include "pxe.h" 103 #include "pxe_netif.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 static struct btinfo_netif bi_netif; 115 116 /***************************************************************************** 117 * This section is a replacement for libsa/udp.c 118 *****************************************************************************/ 119 120 /* Caller must leave room for ethernet, ip, and udp headers in front!! */ 121 ssize_t 122 sendudp(struct iodesc *d, void *pkt, size_t len) 123 { 124 t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf; 125 126 uw->status = 0; 127 128 uw->ip = d->destip.s_addr; 129 uw->gw = gateip.s_addr; 130 uw->src_port = d->myport; 131 uw->dst_port = d->destport; 132 uw->buffer_size = len; 133 uw->buffer.segment = VTOPSEG(pkt); 134 uw->buffer.offset = VTOPOFF(pkt); 135 136 pxe_call(PXENV_UDP_WRITE); 137 138 if (uw->status != PXENV_STATUS_SUCCESS) { 139 /* XXX This happens a lot; it shouldn't. */ 140 if (uw->status != PXENV_STATUS_FAILURE) 141 printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n", 142 uw->status); 143 return (-1); 144 } 145 146 return (len); 147 } 148 149 /* 150 * Receive a UDP packet and validate it for us. 151 * Caller leaves room for the headers (Ether, IP, UDP). 152 */ 153 ssize_t 154 readudp(struct iodesc *d, void *pkt, size_t len, time_t tleft) 155 { 156 t_PXENV_UDP_READ *ur = (void *) pxe_command_buf; 157 struct udphdr *uh; 158 struct ip *ip; 159 160 uh = (struct udphdr *)pkt - 1; 161 ip = (struct ip *)uh - 1; 162 163 bzero(ur, sizeof(*ur)); 164 165 ur->dest_ip = d->myip.s_addr; 166 ur->d_port = d->myport; 167 ur->buffer_size = len; 168 ur->buffer.segment = VTOPSEG(pkt); 169 ur->buffer.offset = VTOPOFF(pkt); 170 171 /* XXX Timeout unused. */ 172 173 pxe_call(PXENV_UDP_READ); 174 175 if (ur->status != PXENV_STATUS_SUCCESS) { 176 /* XXX This happens a lot; it shouldn't. */ 177 if (ur->status != PXENV_STATUS_FAILURE) 178 printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n", 179 ur->status); 180 return (-1); 181 } 182 183 ip->ip_src.s_addr = ur->src_ip; 184 uh->uh_sport = ur->s_port; 185 uh->uh_dport = d->myport; 186 187 return (ur->buffer_size); 188 } 189 190 /* 191 * netif layer: 192 * open, close, shutdown: called from dev_net.c 193 * socktodesc: called by network protocol modules 194 * 195 * We only allow one open socket. 196 */ 197 198 static int pxe_inited; 199 static struct iodesc desc; 200 201 int 202 pxe_netif_open() 203 { 204 t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf; 205 206 if (!pxe_inited) { 207 if (pxe_init() != 0) 208 return (-1); 209 pxe_inited = 1; 210 } 211 BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); 212 213 bzero(uo, sizeof(*uo)); 214 215 uo->src_ip = bootplayer.yip; 216 217 pxe_call(PXENV_UDP_OPEN); 218 219 if (uo->status != PXENV_STATUS_SUCCESS) { 220 printf("pxe_netif_probe: PXENV_UDP_OPEN failed: 0x%x\n", 221 uo->status); 222 return (-1); 223 } 224 225 bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN); 226 227 /* 228 * Since the PXE BIOS has already done DHCP, make sure we 229 * don't reuse any of its transaction IDs. 230 */ 231 desc.xid = bootplayer.ident; 232 233 return (0); 234 } 235 236 void 237 pxe_netif_close(sock) 238 int sock; 239 { 240 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf; 241 242 #ifdef NETIF_DEBUG 243 if (sock != 0) 244 printf("pxe_netif_close: sock=%d\n", sock); 245 #endif 246 247 uc->status = 0; 248 249 pxe_call(PXENV_UDP_CLOSE); 250 251 if (uc->status != PXENV_STATUS_SUCCESS) 252 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n", 253 uc->status); 254 } 255 256 void 257 pxe_netif_shutdown() 258 { 259 260 pxe_fini(); 261 } 262 263 struct iodesc * 264 socktodesc(sock) 265 int sock; 266 { 267 268 #ifdef NETIF_DEBUG 269 if (sock != 0) 270 return (0); 271 else 272 #endif 273 return (&desc); 274 } 275 276 /***************************************************************************** 277 * PXE initialization and support routines 278 *****************************************************************************/ 279 280 u_int16_t pxe_command_buf_seg; 281 u_int16_t pxe_command_buf_off; 282 283 extern u_int16_t bangpxe_off, bangpxe_seg; 284 extern u_int16_t pxenv_off, pxenv_seg; 285 286 static struct btinfo_netif bi_netif; 287 288 int 289 pxe_init(void) 290 { 291 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf; 292 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf; 293 pxenv_t *pxenv; 294 pxe_t *pxe; 295 char *cp; 296 int i; 297 u_int8_t cksum, *ucp; 298 299 /* 300 * Checking for the presence of PXE is a machine-dependent 301 * operation. On the IA-32, this can be done two ways: 302 * 303 * Int 0x1a function 0x5650 304 * 305 * Scan memory for the !PXE or PXENV+ signatures 306 * 307 * We do the latter, since the Int method returns a pointer 308 * to a deprecated structure (PXENV+). 309 */ 310 311 pxenv = NULL; 312 pxe = NULL; 313 314 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) { 315 if (pxenv == NULL) { 316 pxenv = (pxenv_t *)cp; 317 if (MEMSTRCMP(pxenv->Signature, "PXENV+")) 318 pxenv = NULL; 319 else { 320 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 321 i < pxenv->Length; i++) 322 cksum += ucp[i]; 323 if (cksum != 0) { 324 printf("pxe_init: bad cksum (0x%x) " 325 "for PXENV+ at 0x%lx\n", cksum, 326 (u_long) cp); 327 pxenv = NULL; 328 } 329 } 330 } 331 332 if (pxe == NULL) { 333 pxe = (pxe_t *)cp; 334 if (MEMSTRCMP(pxe->Signature, "!PXE")) 335 pxe = NULL; 336 else { 337 for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; 338 i < pxe->StructLength; i++) 339 cksum += ucp[i]; 340 if (cksum != 0) { 341 printf("pxe_init: bad cksum (0x%x) " 342 "for !PXE at 0x%lx\n", cksum, 343 (u_long) cp); 344 pxe = NULL; 345 } 346 } 347 } 348 349 if (pxe != NULL && pxenv != NULL) 350 break; 351 } 352 353 if (pxe == NULL && pxenv == NULL) { 354 printf("pxe_init: No PXE BIOS found.\n"); 355 return (1); 356 } 357 358 if (pxenv != NULL) { 359 printf("PXE BIOS Version %d.%d\n", 360 (pxenv->Version >> 8) & 0xff, pxenv->Version & 0xff); 361 if (pxenv->Version >= 0x0201 && pxe != NULL) { 362 /* 2.1 or greater -- don't use PXENV+ */ 363 pxenv = NULL; 364 } 365 } 366 367 if (pxe != NULL) { 368 pxe_call = pxecall_bangpxe; 369 bangpxe_off = pxe->EntryPointSP.offset; 370 bangpxe_seg = pxe->EntryPointSP.segment; 371 } else { 372 pxe_call = pxecall_pxenv; 373 pxenv_off = pxenv->RMEntry.offset; 374 pxenv_seg = pxenv->RMEntry.segment; 375 } 376 377 /* 378 * Pre-compute the segment/offset of the pxe_command_buf 379 * to make things nicer in the low-level calling glue. 380 */ 381 pxe_command_buf_seg = VTOPSEG(pxe_command_buf); 382 pxe_command_buf_off = VTOPOFF(pxe_command_buf); 383 384 /* 385 * Get the cached info from the server's Discovery reply packet. 386 */ 387 bzero(gci, sizeof(*gci)); 388 gci->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 389 pxe_call(PXENV_GET_CACHED_INFO); 390 if (gci->Status != PXENV_STATUS_SUCCESS) { 391 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n", 392 gci->Status); 393 return (1); 394 } 395 pvbcopy((void *)((gci->Buffer.segment << 4) + gci->Buffer.offset), 396 &bootplayer, gci->BufferSize); 397 398 /* 399 * Get network interface information. 400 */ 401 bzero(gnt, sizeof(*gnt)); 402 pxe_call(PXENV_UNDI_GET_NIC_TYPE); 403 404 if (gnt->Status != PXENV_STATUS_SUCCESS) { 405 printf("pxe_init: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n", 406 gnt->Status); 407 return (0); 408 } 409 410 switch (gnt->NicType) { 411 case PCI_NIC: 412 case CardBus_NIC: 413 strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); 414 bi_netif.bus = BI_BUS_PCI; 415 bi_netif.addr.tag = gnt->info.pci.BusDevFunc; 416 417 printf("Using %s device at bus %d device %d function %d\n", 418 gnt->NicType == PCI_NIC ? "PCI" : "CardBus", 419 (gnt->info.pci.BusDevFunc >> 8) & 0xff, 420 (gnt->info.pci.BusDevFunc >> 3) & 0x1f, 421 gnt->info.pci.BusDevFunc & 0x7); 422 break; 423 424 case PnP_NIC: 425 /* XXX Make bootinfo work with this. */ 426 printf("Using PnP device at 0x%x\n", gnt->info.pnp.CardSelNum); 427 } 428 429 printf("Ethernet address %s\n", ether_sprintf(bootplayer.CAddr)); 430 431 return (0); 432 } 433 434 void 435 pxe_fini(void) 436 { 437 t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf; 438 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf; 439 440 if (pxe_call == NULL) 441 return; 442 443 pxe_call(PXENV_UNDI_SHUTDOWN); 444 445 if (shutdown->Status != PXENV_STATUS_SUCCESS) 446 printf("pxe_fini: PXENV_UNDI_SHUTDOWN failed: 0x%x\n", 447 shutdown->Status); 448 449 pxe_call(PXENV_UNLOAD_STACK); 450 451 if (unload->Status != PXENV_STATUS_SUCCESS) 452 printf("pxe_fini: PXENV_UNLOAD_STACK failed: 0x%x\n", 453 unload->Status); 454 } 455