1/* $NetBSD: pxe.c,v 1.17 2009/12/13 23:01:42 jakllsch 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 105void (*pxe_call)(uint16_t); 106 107void pxecall_bangpxe(uint16_t); /* pxe_call.S */ 108void pxecall_pxenv(uint16_t); /* pxe_call.S */ 109 110extern char pxe_command_buf[256]; 111 112BOOTPLAYER bootplayer; 113 114static 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!! */ 121ssize_t 122sendudp(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 */ 153ssize_t 154readudp(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 198static int pxe_inited; 199static struct iodesc desc; 200 201int 202pxe_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 236void 237pxe_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 255struct iodesc * 256socktodesc(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 271uint16_t pxe_command_buf_seg; 272uint16_t pxe_command_buf_off; 273 274extern uint16_t bangpxe_off, bangpxe_seg; 275extern uint16_t pxenv_off, pxenv_seg; 276 277static struct btinfo_netif bi_netif; 278 279int 280pxe_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 425void 426pxe_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