1 /* $NetBSD: pxe.c,v 1.18 2013/10/20 19:47:28 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 (at) freebsd.org> 40 * All rights reserved. 41 * Copyright (c) 2000 Paul Saab <ps (at) freebsd.org> 42 * All rights reserved. 43 * Copyright (c) 2000 John Baldwin <jhb (at) 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)(uint16_t); 106 107 void pxecall_bangpxe(uint16_t); /* pxe_call.S */ 108 void pxecall_pxenv(uint16_t); /* pxe_call.S */ 109 110 extern 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, saseconds_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 (void)memset(ur, 0, 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(void) 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 (void)memset(uo, 0, 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 memcpy(desc.myea, bootplayer.CAddr, 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(int sock) 238 { 239 t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf; 240 241 #ifdef NETIF_DEBUG 242 if (sock != 0) 243 printf("pxe_netif_close: sock=%d\n", sock); 244 #endif 245 246 uc->status = 0; 247 248 pxe_call(PXENV_UDP_CLOSE); 249 250 if (uc->status != PXENV_STATUS_SUCCESS) 251 printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n", 252 uc->status); 253 } 254 255 struct iodesc * 256 socktodesc(int sock) 257 { 258 259 #ifdef NETIF_DEBUG 260 if (sock != 0) 261 return (0); 262 else 263 #endif 264 return (&desc); 265 } 266 267 /***************************************************************************** 268 * PXE initialization and support routines 269 *****************************************************************************/ 270 271 uint16_t pxe_command_buf_seg; 272 uint16_t pxe_command_buf_off; 273 274 extern uint16_t bangpxe_off, bangpxe_seg; 275 extern uint16_t pxenv_off, pxenv_seg; 276 277 static struct btinfo_netif bi_netif; 278 279 int 280 pxe_init(void) 281 { 282 t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf; 283 t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf; 284 pxenv_t *pxenv; 285 pxe_t *pxe; 286 char *cp; 287 int i; 288 uint8_t cksum, *ucp; 289 290 /* 291 * Checking for the presence of PXE is a machine-dependent 292 * operation. On the IA-32, this can be done two ways: 293 * 294 * Int 0x1a function 0x5650 295 * 296 * Scan memory for the !PXE or PXENV+ signatures 297 * 298 * We do the latter, since the Int method returns a pointer 299 * to a deprecated structure (PXENV+). 300 */ 301 302 pxenv = NULL; 303 pxe = NULL; 304 305 for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) { 306 if (pxenv == NULL) { 307 pxenv = (pxenv_t *)cp; 308 if (MEMSTRCMP(pxenv->Signature, "PXENV+")) 309 pxenv = NULL; 310 else { 311 for (i = 0, ucp = (uint8_t *)cp, cksum = 0; 312 i < pxenv->Length; i++) 313 cksum += ucp[i]; 314 if (cksum != 0) { 315 printf("pxe_init: bad cksum (0x%x) " 316 "for PXENV+ at 0x%lx\n", cksum, 317 (u_long) cp); 318 pxenv = NULL; 319 } 320 } 321 } 322 323 if (pxe == NULL) { 324 pxe = (pxe_t *)cp; 325 if (MEMSTRCMP(pxe->Signature, "!PXE")) 326 pxe = NULL; 327 else { 328 for (i = 0, ucp = (uint8_t *)cp, cksum = 0; 329 i < pxe->StructLength; i++) 330 cksum += ucp[i]; 331 if (cksum != 0) { 332 printf("pxe_init: bad cksum (0x%x) " 333 "for !PXE at 0x%lx\n", cksum, 334 (u_long) cp); 335 pxe = NULL; 336 } 337 } 338 } 339 340 if (pxe != NULL && pxenv != NULL) 341 break; 342 } 343 344 if (pxe == NULL && pxenv == NULL) { 345 printf("pxe_init: No PXE BIOS found.\n"); 346 return (1); 347 } 348 349 if (pxenv != NULL) { 350 printf("PXE BIOS Version %d.%d\n", 351 (pxenv->Version >> 8) & 0xff, pxenv->Version & 0xff); 352 if (pxenv->Version >= 0x0201 && pxe != NULL) { 353 /* 2.1 or greater -- don't use PXENV+ */ 354 pxenv = NULL; 355 } 356 } 357 358 if (pxe != NULL) { 359 pxe_call = pxecall_bangpxe; 360 bangpxe_off = pxe->EntryPointSP.offset; 361 bangpxe_seg = pxe->EntryPointSP.segment; 362 } else { 363 pxe_call = pxecall_pxenv; 364 pxenv_off = pxenv->RMEntry.offset; 365 pxenv_seg = pxenv->RMEntry.segment; 366 } 367 368 /* 369 * Pre-compute the segment/offset of the pxe_command_buf 370 * to make things nicer in the low-level calling glue. 371 */ 372 pxe_command_buf_seg = VTOPSEG(pxe_command_buf); 373 pxe_command_buf_off = VTOPOFF(pxe_command_buf); 374 375 /* 376 * Get the cached info from the server's Discovery reply packet. 377 */ 378 (void)memset(gci, 0, sizeof(*gci)); 379 gci->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 380 pxe_call(PXENV_GET_CACHED_INFO); 381 if (gci->Status != PXENV_STATUS_SUCCESS) { 382 printf("pxe_init: PXENV_GET_CACHED_INFO failed: 0x%x\n", 383 gci->Status); 384 return (1); 385 } 386 pvbcopy((void *)((gci->Buffer.segment << 4) + gci->Buffer.offset), 387 &bootplayer, gci->BufferSize); 388 389 /* 390 * Get network interface information. 391 */ 392 (void)memset(gnt, 0, sizeof(*gnt)); 393 pxe_call(PXENV_UNDI_GET_NIC_TYPE); 394 395 if (gnt->Status != PXENV_STATUS_SUCCESS) { 396 printf("pxe_init: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n", 397 gnt->Status); 398 return (0); 399 } 400 401 switch (gnt->NicType) { 402 case PCI_NIC: 403 case CardBus_NIC: 404 strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); 405 bi_netif.bus = BI_BUS_PCI; 406 bi_netif.addr.tag = gnt->info.pci.BusDevFunc; 407 408 printf("Using %s device at bus %d device %d function %d\n", 409 gnt->NicType == PCI_NIC ? "PCI" : "CardBus", 410 (gnt->info.pci.BusDevFunc >> 8) & 0xff, 411 (gnt->info.pci.BusDevFunc >> 3) & 0x1f, 412 gnt->info.pci.BusDevFunc & 0x7); 413 break; 414 415 case PnP_NIC: 416 /* XXX Make bootinfo work with this. */ 417 printf("Using PnP device at 0x%x\n", gnt->info.pnp.CardSelNum); 418 } 419 420 printf("Ethernet address %s\n", ether_sprintf(bootplayer.CAddr)); 421 422 return (0); 423 } 424 425 void 426 pxe_fini(void) 427 { 428 t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf; 429 430 if (pxe_call == NULL) 431 return; 432 433 pxe_call(PXENV_UNDI_SHUTDOWN); 434 435 if (shutdown->Status != PXENV_STATUS_SUCCESS) 436 printf("pxe_fini: PXENV_UNDI_SHUTDOWN failed: 0x%x\n", 437 shutdown->Status); 438 } 439