From 96c309c384e99b742c4307ece38772c4841ffad2 Mon Sep 17 00:00:00 2001 From: Tom Cosgrove Date: Fri, 19 Mar 2004 13:48:21 +0000 Subject: Enter pxeboot, derived from the NetBSD implementation. Initially intended to support network installs using bsd.rd over TFTP. Thanks to the many who tested, including Diana Eichert. ok deraadt@ --- sys/arch/i386/stand/libsa/pxe.c | 547 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 sys/arch/i386/stand/libsa/pxe.c (limited to 'sys/arch/i386/stand/libsa/pxe.c') diff --git a/sys/arch/i386/stand/libsa/pxe.c b/sys/arch/i386/stand/libsa/pxe.c new file mode 100644 index 00000000000..b159b51f5dc --- /dev/null +++ b/sys/arch/i386/stand/libsa/pxe.c @@ -0,0 +1,547 @@ +/* $OpenBSD: pxe.c,v 1.1 2004/03/19 13:48:18 tom Exp $ */ +/* $NetBSD: pxe.c,v 1.5 2003/03/11 18:29:00 drochner Exp $ */ + +/* + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2000 Alfred Perlstein + * All rights reserved. + * Copyright (c) 2000 Paul Saab + * All rights reserved. + * Copyright (c) 2000 John Baldwin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Support for the Intel Preboot Execution Environment (PXE). + * + * PXE provides a UDP implementation as well as a UNDI network device + * driver. UNDI is much more complicated to use than PXE UDP, so we + * use PXE UDP as a cheap and easy way to get PXE support. + */ + +#include +#include + +#ifdef _STANDALONE +#include +#else +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pxeboot.h" +#include "pxe.h" +#include "pxe_netif.h" + +void (*pxe_call)(u_int16_t); + +void pxecall_bangpxe(u_int16_t); /* pxe_call.S */ +void pxecall_pxenv(u_int16_t); /* pxe_call.S */ + +char pxe_command_buf[256]; + +BOOTPLAYER bootplayer; + +struct in_addr servip; /* for tftp */ /* XXX init this */ + +/* static struct btinfo_netif bi_netif; */ + +/***************************************************************************** + * This section is a replacement for libsa/udp.c + *****************************************************************************/ + +/* Caller must leave room for ethernet, ip, and udp headers in front!! */ +ssize_t +pxesendudp(struct iodesc *d, void *pkt, size_t len) +{ + t_PXENV_UDP_WRITE *uw = (void *) pxe_command_buf; + + uw->status = 0; + + uw->ip = d->destip.s_addr; + uw->gw = gateip.s_addr; + uw->src_port = d->myport; + uw->dst_port = d->destport; + uw->buffer_size = len; + uw->buffer.segment = VTOPSEG(pkt); + uw->buffer.offset = VTOPOFF(pkt); + + pxe_call(PXENV_UDP_WRITE); + + if (uw->status != PXENV_STATUS_SUCCESS) { + /* XXX This happens a lot; it shouldn't. */ + if (uw->status != PXENV_STATUS_FAILURE) + printf("sendudp: PXENV_UDP_WRITE failed: 0x%x\n", + uw->status); + return -1; + } + + return len; +} + +/* + * Receive a UDP packet and validate it for us. + * Caller leaves room for the headers (Ether, IP, UDP). + */ +ssize_t +pxereadudp(struct iodesc *d, void *pkt, size_t len, time_t tleft) +{ + t_PXENV_UDP_READ *ur = (void *) pxe_command_buf; + struct udphdr *uh; + struct ip *ip; + + uh = (struct udphdr *)pkt - 1; + ip = (struct ip *)uh - 1; + + bzero(ur, sizeof(*ur)); + + ur->dest_ip = d->myip.s_addr; + ur->d_port = d->myport; + ur->buffer_size = len; + ur->buffer.segment = VTOPSEG(pkt); + ur->buffer.offset = VTOPOFF(pkt); + + /* XXX Timeout unused. */ + + pxe_call(PXENV_UDP_READ); + + if (ur->status != PXENV_STATUS_SUCCESS) { + /* XXX This happens a lot; it shouldn't. */ + if (ur->status != PXENV_STATUS_FAILURE) + printf("readudp: PXENV_UDP_READ_failed: 0x%0x\n", + ur->status); + return -1; + } + + ip->ip_src.s_addr = ur->src_ip; + uh->uh_sport = ur->s_port; + uh->uh_dport = d->myport; + + return ur->buffer_size; +} + +/* + * netif layer: + * open, close, shutdown: called from dev_net.c + * socktodesc: called by network protocol modules + * + * We only allow one open socket. + */ + +static int pxe_inited; +static struct iodesc desc; + +int +pxe_netif_open() +{ + t_PXENV_UDP_OPEN *uo = (void *) pxe_command_buf; + +#ifdef NETIF_DEBUG + printf("pxe_netif_open()\n"); +#endif + if (!pxe_inited) { + if (pxe_init(0) != 0) + return -1; + pxe_inited = 1; + } + /* BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif)); */ + + bzero(uo, sizeof(*uo)); + + uo->src_ip = bootplayer.yip; + + pxe_call(PXENV_UDP_OPEN); + + if (uo->status != PXENV_STATUS_SUCCESS) { + printf("pxe_netif_open: PXENV_UDP_OPEN failed: 0x%x\n", + uo->status); + return -1; + } + + bcopy(bootplayer.CAddr, desc.myea, ETHER_ADDR_LEN); + + /* + * Since the PXE BIOS has already done DHCP, make sure we + * don't reuse any of its transaction IDs. + */ + desc.xid = bootplayer.ident; + + return 0; +} + +void +pxe_netif_close(sock) + int sock; +{ + t_PXENV_UDP_CLOSE *uc = (void *) pxe_command_buf; + +#ifdef NETIF_DEBUG + if (sock != 0) + printf("pxe_netif_close: sock=%d\n", sock); +#endif + + uc->status = 0; + + pxe_call(PXENV_UDP_CLOSE); + + if (uc->status != PXENV_STATUS_SUCCESS) + printf("pxe_netif_end: PXENV_UDP_CLOSE failed: 0x%x\n", + uc->status); +} + +void +pxe_netif_shutdown() +{ +#ifdef NETIF_DEBUG + printf("pxe_netif_shutdown()\n"); +#endif + + pxe_shutdown(); +} + +struct iodesc * +pxesocktodesc(sock) + int sock; +{ + +#ifdef NETIF_DEBUG + if (sock != 0) + return 0; + else +#endif + return &desc; +} + +/***************************************************************************** + * PXE initialization and support routines + *****************************************************************************/ + +u_int16_t pxe_command_buf_seg; +u_int16_t pxe_command_buf_off; + +extern u_int16_t bangpxe_off, bangpxe_seg; +extern u_int16_t pxenv_off, pxenv_seg; + +/* static struct btinfo_netif bi_netif; */ + +void +pxeprobe(void) +{ + if (!pxe_inited) { + if (pxe_init(1) == 0) { + pxe_inited = 1; + } + } +} + +int +pxe_init(int quiet) +{ + t_PXENV_GET_CACHED_INFO *gci = (void *) pxe_command_buf; + pxenv_t *pxenv; + pxe_t *pxe; + char *cp; + int i; + u_int8_t cksum, *ucp; + + /* + * Checking for the presence of PXE is a machine-dependent + * operation. On the IA-32, this can be done two ways: + * + * Int 0x1a function 0x5650 + * + * Scan memory for the !PXE or PXENV+ signatures + * + * We do the latter, since the Int method returns a pointer + * to a deprecated structure (PXENV+). + */ + + pxenv = NULL; + pxe = NULL; + + for (cp = (char *)0xa0000; cp > (char *)0x10000; cp -= 2) { + if (pxenv == NULL) { + pxenv = (pxenv_t *)cp; + if (memcmp(pxenv->Signature, S_SIZE("PXENV+")) != 0) + pxenv = NULL; + else { + for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; + i < pxenv->Length; i++) + cksum += ucp[i]; + if (cksum != 0) { + printf("\npxe_init: bad cksum (0x%x) " + "for PXENV+ at 0x%lx\n", cksum, + (u_long) cp); + pxenv = NULL; + } + } + } + + if (pxe == NULL) { + pxe = (pxe_t *)cp; + if (memcmp(pxe->Signature, S_SIZE("!PXE")) != 0) + pxe = NULL; + else { + for (i = 0, ucp = (u_int8_t *)cp, cksum = 0; + i < pxe->StructLength; i++) + cksum += ucp[i]; + if (cksum != 0) { + printf("pxe_init: bad cksum (0x%x) " + "for !PXE at 0x%lx\n", cksum, + (u_long) cp); + pxe = NULL; + } + } + } + + if (pxe != NULL && pxenv != NULL) + break; + } + + if (pxe == NULL && pxenv == NULL) { + if (!quiet) printf("pxe_init: No PXE BIOS found.\n"); + return 1; + } + + if (pxenv == NULL) { + /* assert(pxe != NULL); */ + + printf(quiet ? " pxe!" : "PXE present\n"); + } + else { /* pxenv != NULL */ + int bang = 0; + + if (pxenv->Version >= 0x0201 && pxe != NULL) { + /* 2.1 or greater -- don't use PXENV+ */ + bang = 1; + } + + if (quiet) { + printf(" pxe%c[%d.%d]", + (bang ? '!' : '+'), + (pxenv->Version >> 8) & 0xff, + pxenv->Version & 0xff); + } + else { + printf("PXE BIOS Version %d.%d\n", + (pxenv->Version >> 8) & 0xff, + pxenv->Version & 0xff); + } + + if (bang) { + pxenv = NULL; + } + } + + if (pxe != NULL) { + pxe_call = pxecall_bangpxe; + bangpxe_off = pxe->EntryPointSP.offset; + bangpxe_seg = pxe->EntryPointSP.segment; + } else { + pxe_call = pxecall_pxenv; + pxenv_off = pxenv->RMEntry.offset; + pxenv_seg = pxenv->RMEntry.segment; + } + + /* + * Pre-compute the segment/offset of the pxe_command_buf + * to make things nicer in the low-level calling glue. + */ + pxe_command_buf_seg = VTOPSEG(pxe_command_buf); + pxe_command_buf_off = VTOPOFF(pxe_command_buf); + + /* + * Get the cached info from the server's Discovery reply packet. + */ + bzero(gci, sizeof(*gci)); + gci->PacketType = PXENV_PACKET_TYPE_CACHED_REPLY; + pxe_call(PXENV_GET_CACHED_INFO); + + if (gci->Status != PXENV_STATUS_SUCCESS) { + printf("\npxeinfo: PXENV_GET_CACHED_INFO failed: 0x%x\n", + gci->Status); + return 1; + } + + memcpy(&bootplayer, + SEGOFF2FLAT(gci->Buffer.segment, gci->Buffer.offset), + gci->BufferSize); + + bcopy(&bootplayer.yip, &myip.s_addr, sizeof(myip.s_addr)); + bcopy(&bootplayer.sip, &servip.s_addr, sizeof(servip.s_addr)); + + /* Compute our "natural" netmask. */ + if (IN_CLASSA(myip.s_addr)) + netmask = IN_CLASSA_NET; + else if (IN_CLASSB(myip.s_addr)) + netmask = IN_CLASSB_NET; + else + netmask = IN_CLASSC_NET; + + return 0; +} + +void +pxeinfo(void) +{ + u_int8_t *p; +#ifdef PXE_DEBUG + t_PXENV_UNDI_GET_NIC_TYPE *gnt = (void *) pxe_command_buf; +#endif + + printf(" mac %s", ether_sprintf(bootplayer.CAddr)); + p = (u_int8_t *)&myip.s_addr; + printf(", ip %d.%d.%d.%d", p[0], p[1], p[2], p[3]); + p = (u_int8_t *)&servip.s_addr; + printf(", server %d.%d.%d.%d", p[0], p[1], p[2], p[3]); + +#ifdef PXE_DEBUG + /* + * Get network interface information. + */ + bzero(gnt, sizeof(*gnt)); + pxe_call(PXENV_UNDI_GET_NIC_TYPE); + + if (gnt->Status != PXENV_STATUS_SUCCESS) { + printf("\npxeinfo: PXENV_UNDI_GET_NIC_TYPE failed: 0x%x\n", + gnt->Status); + return; + } + + switch (gnt->NicType) { + case PCI_NIC: + case CardBus_NIC: + /* strncpy(bi_netif.ifname, "pxe", sizeof(bi_netif.ifname)); */ + /* bi_netif.bus = BI_BUS_PCI; */ + /* bi_netif.addr.tag = gnt->info.pci.BusDevFunc; */ + + printf("\nPXE: Using %s device at bus %d device %d function %d\n", + gnt->NicType == PCI_NIC ? "PCI" : "CardBus", + (gnt->info.pci.BusDevFunc >> 8) & 0xff, + (gnt->info.pci.BusDevFunc >> 3) & 0x1f, + gnt->info.pci.BusDevFunc & 0x7); + break; + + case PnP_NIC: + /* XXX Make bootinfo work with this. */ + printf("\nPXE: Using PnP device at 0x%x\n", + gnt->info.pnp.CardSelNum); + } +#endif +} + +void +pxe_shutdown(void) +{ + int try; + t_PXENV_UNLOAD_STACK *unload = (void *) pxe_command_buf; + t_PXENV_UNDI_SHUTDOWN *shutdown = (void *) pxe_command_buf; +#ifdef PXE_DEBUG + t_PXENV_UDP_CLOSE *close = (void *) pxe_command_buf; +#endif + + if (pxe_call == NULL) + return; + + /* Close any open UDP connections. Ignore return value. */ + pxe_call(PXENV_UDP_CLOSE); +#ifdef PXE_DEBUG + printf("pxe_shutdown: PXENV_UDP_CLOSE returned 0x%x\n", close->status); +#endif + + /* Sometimes PXENV_UNDI_SHUTDOWN doesn't work at first */ + for (try = 3; try > 0; try--) { + pxe_call(PXENV_UNDI_SHUTDOWN); + + if (shutdown->Status == PXENV_STATUS_SUCCESS) + break; + + printf("pxe_shutdown: PXENV_UNDI_SHUTDOWN failed: 0x%x\n", + shutdown->Status); + + if (try != 1) + sleep(1); + } + + /* Have multiple attempts at PXENV_UNLOAD_STACK, too */ + for (try = 3; try > 0; try--) { + pxe_call(PXENV_UNLOAD_STACK); + + if (unload->Status == PXENV_STATUS_SUCCESS) + break; + + printf("pxe_shutdown: PXENV_UNLOAD_STACK failed: 0x%x\n", + unload->Status); + + if (try != 1) + sleep(1); + } +} -- cgit v1.2.3