summaryrefslogtreecommitdiffstats
path: root/contrib/baremetal/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/baremetal/main.c')
-rw-r--r--contrib/baremetal/main.c1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/contrib/baremetal/main.c b/contrib/baremetal/main.c
new file mode 100644
index 00000000..7b0de44c
--- /dev/null
+++ b/contrib/baremetal/main.c
@@ -0,0 +1,1119 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Dec/93
+
+Literature dealing with the network protocols:
+ ARP - RFC826
+ RARP - RFC903
+ UDP - RFC768
+ BOOTP - RFC951, RFC2132 (vendor extensions)
+ DHCP - RFC2131, RFC2132 (options)
+ TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+ RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+ NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
+
+**************************************************************************/
+
+/* #define MDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+
+int jmp_bootmenu[10];
+
+struct arptable_t arptable[MAX_ARP];
+
+const char *kernel;
+char kernel_buf[128];
+struct rom_info rom;
+
+#ifdef IMAGE_MENU
+static char *imagelist[RFC1533_VENDOR_NUMOFIMG];
+static int useimagemenu;
+int menutmo,menudefault;
+unsigned char *defparams = NULL;
+int defparams_max = 0;
+#endif
+#ifdef MOTD
+char *motd[RFC1533_VENDOR_NUMOFMOTD];
+#endif
+#ifdef IMAGE_FREEBSD
+int freebsd_howto = 0;
+#endif
+int vendorext_isvalid;
+char config_buffer[TFTP_MAX_PACKET+1]; /* +1 for null byte */
+unsigned long netmask;
+char *hostname = "";
+int hostnamelen = 0;
+#if defined(ETHERBOOT16) || defined(INTERNAL_BOOTP_DATA)
+struct bootpd_t bootp_data;
+#endif
+unsigned long xid;
+unsigned char *end_of_rfc1533 = NULL;
+#ifndef NO_DHCP_SUPPORT
+int dhcp_reply;
+in_addr dhcp_server = { 0L };
+in_addr dhcp_addr = { 0L };
+#endif /* NO_DHCP_SUPPORT */
+
+unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
+#ifdef NO_DHCP_SUPPORT
+char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
+#else
+char rfc1533_cookie[] = { RFC1533_COOKIE};
+char rfc1533_end[]={RFC1533_END };
+static const char dhcpdiscover[]={
+ RFC2132_MSG_TYPE,1,DHCPDISCOVER,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
+ RFC2132_PARAM_LIST,4,RFC1533_NETMASK,RFC1533_GATEWAY,
+ RFC1533_HOSTNAME
+ };
+static const char dhcprequest []={
+ RFC2132_MSG_TYPE,1,DHCPREQUEST,
+ RFC2132_SRV_ID,4,0,0,0,0,
+ RFC2132_REQ_ADDR,4,0,0,0,0,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ sizeof(struct bootpd_t) / 256, sizeof(struct bootpd_t) % 256,
+ /* request parameters */
+ RFC2132_PARAM_LIST,
+#ifdef IMAGE_FREEBSD
+ /* 4 standard + 6 vendortags + 8 motd + 16 menu items */
+ 4 + 6 + 8 + 16,
+#else
+ /* 4 standard + 5 vendortags + 8 motd + 16 menu items */
+ 4 + 5 + 8 + 16,
+#endif
+ /* Standard parameters */
+ RFC1533_NETMASK, RFC1533_GATEWAY,
+ RFC1533_HOSTNAME,
+ RFC1533_ROOTPATH, /* only passed to the booted image */
+ /* Etherboot vendortags */
+ RFC1533_VENDOR_MAGIC,
+ RFC1533_VENDOR_ADDPARM,
+ RFC1533_VENDOR_ETHDEV,
+#ifdef IMAGE_FREEBSD
+ RFC1533_VENDOR_HOWTO,
+#endif
+ RFC1533_VENDOR_MNUOPTS, RFC1533_VENDOR_SELECTION,
+ /* 8 MOTD entries */
+ RFC1533_VENDOR_MOTD,
+ RFC1533_VENDOR_MOTD+1,
+ RFC1533_VENDOR_MOTD+2,
+ RFC1533_VENDOR_MOTD+3,
+ RFC1533_VENDOR_MOTD+4,
+ RFC1533_VENDOR_MOTD+5,
+ RFC1533_VENDOR_MOTD+6,
+ RFC1533_VENDOR_MOTD+7,
+ /* 16 image entries */
+ RFC1533_VENDOR_IMG,
+ RFC1533_VENDOR_IMG+1,
+ RFC1533_VENDOR_IMG+2,
+ RFC1533_VENDOR_IMG+3,
+ RFC1533_VENDOR_IMG+4,
+ RFC1533_VENDOR_IMG+5,
+ RFC1533_VENDOR_IMG+6,
+ RFC1533_VENDOR_IMG+7,
+ RFC1533_VENDOR_IMG+8,
+ RFC1533_VENDOR_IMG+9,
+ RFC1533_VENDOR_IMG+10,
+ RFC1533_VENDOR_IMG+11,
+ RFC1533_VENDOR_IMG+12,
+ RFC1533_VENDOR_IMG+13,
+ RFC1533_VENDOR_IMG+14,
+ RFC1533_VENDOR_IMG+15,
+ };
+
+#endif /* NO_DHCP_SUPPORT */
+static const char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/**************************************************************************
+MAIN - Kick off routine
+**************************************************************************/
+int main(void)
+{
+ char *p;
+ static int card_retries = 0;
+ int i;
+
+ for (p=_edata; p<_end; p++)
+ *p = 0; /* Zero BSS */
+
+#ifdef CONSOLE_SERIAL
+ (void)serial_init();
+#endif
+
+#ifdef DELIMITERLINES
+ for (i=0; i<80; i++) putchar('=');
+#endif
+
+#ifdef ETHERBOOT32
+ rom = *(struct rom_info *)ROM_INFO_LOCATION;
+ printf("ROM segment %#x length %#x reloc %#x\n", rom.rom_segment,
+ rom.rom_length << 1, ((unsigned long)_start) >> 4);
+#endif
+#ifdef ETHERBOOT16
+ fmemcpy(&rom, (Address)ROM_INFO_LOCATION, sizeof(rom));
+ printf("ROM segment %#x length %#x\n", rom.rom_segment,
+ rom.rom_length << 1);
+#endif
+#ifdef ASK_BOOT
+ while (1) {
+ int c;
+ unsigned long time;
+ printf(ASK_PROMPT);
+#if ASK_BOOT > 0
+ for (time = currticks() + ASK_BOOT*TICKS_PER_SEC; !iskey(); )
+ if (currticks() > time) {
+ c = ANS_DEFAULT;
+ goto done;
+ }
+#endif
+ c = getchar();
+ if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
+ if (c == '\n') c = ANS_DEFAULT;
+done:
+ if ((c >= ' ') && (c <= '~')) putchar(c);
+ putchar('\n');
+ if (c == ANS_LOCAL)
+ exit(0);
+ if (c == ANS_NETWORK)
+ break;
+ }
+#endif
+#if (TRY_FLOPPY_FIRST > 0) && defined(FLOPPY)
+ disk_init();
+ printf("Trying floppy");
+ for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
+ putchar('.');
+ if (disk_read(0, 0, 0, 0, ((char *) FLOPPY_BOOT_LOCATION)) != 0x8000) {
+ printf("using floppy\n");
+ exit(0);
+ }
+ }
+ printf("no floppy\n");
+#endif /* TRY_FLOPPY_FIRST && FLOPPY */
+ print_config();
+ gateA20_set();
+#ifdef EMERGENCYDISKBOOT
+ if (!eth_probe()) {
+ printf("No adapter found\n");
+ exit(0);
+ }
+#else
+ while (!eth_probe()) {
+ printf("No adapter found");
+ if (!setjmp(jmp_bootmenu))
+ rfc951_sleep(++card_retries);
+ }
+#endif
+ kernel = DEFAULT_BOOTFILE;
+ while (1) {
+ if ((i = setjmp(jmp_bootmenu)) != 0) {
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+ bootmenu(--i);
+ } else {
+ load();
+ }
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+ }
+}
+
+/**************************************************************************
+LOADKERNEL - Try to load kernel image
+**************************************************************************/
+#ifndef FLOPPY
+#define loadkernel(s) download((s),downloadkernel)
+#else
+static int loadkernel(const char *fname)
+{
+ if (!memcmp(fname,"/dev/",5) && fname[6] == 'd') {
+ int dev, part = 0;
+ if (fname[5] == 'f') {
+ if ((dev = fname[7] - '0') < 0 || dev > 3)
+ goto nodisk; }
+ else if (fname[5] == 'h' || fname[5] == 's') {
+ if ((dev = 0x80 + fname[7] - 'a') < 0x80 || dev > 0x83)
+ goto nodisk;
+ if (fname[8]) {
+ part = fname[8] - '0';
+ if (fname[9])
+ part = 10*part + fname[9] - '0'; }
+ /* bootdisk cannot cope with more than eight partitions */
+ if (part < 0 || part > 8)
+ goto nodisk; }
+ else
+ goto nodisk;
+ return(bootdisk(dev,part)); }
+nodisk:
+ return download(fname, downloadkernel);
+}
+#endif
+
+/**************************************************************************
+LOAD - Try to get booted
+**************************************************************************/
+void load()
+{
+ static int bootp_completed = 0;
+
+ /* Find a server to get BOOTP reply from */
+ if (!bootp_completed ||
+ !arptable[ARP_CLIENT].ipaddr.s_addr || !arptable[ARP_SERVER].ipaddr.s_addr) {
+retry:
+ bootp_completed = 0;
+#ifdef RARP_NOT_BOOTP
+ printf("Searching for server (RARP)...\n");
+#else
+#ifndef NO_DHCP_SUPPORT
+ printf("Searching for server (DHCP)...\n");
+#else
+ printf("Searching for server (BOOTP)...\n");
+#endif
+#endif
+
+#ifdef RARP_NOT_BOOTP
+ if (!rarp()) {
+#else
+ if (!bootp()) {
+#endif
+ printf("No Server found\n");
+#ifdef EMERGENCYDISKBOOT
+ exit(0);
+#else
+ goto retry;
+#endif
+ }
+ bootp_completed++;
+ }
+ printf("Me: %I, Server: %I",
+ arptable[ARP_CLIENT].ipaddr.s_addr,
+ arptable[ARP_SERVER].ipaddr.s_addr);
+ if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr)
+ printf(", Relay: %I",
+ BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr);
+ if (arptable[ARP_GATEWAY].ipaddr.s_addr)
+ printf(", Gateway %I", arptable[ARP_GATEWAY].ipaddr.s_addr);
+ putchar('\n');
+
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+#ifdef MOTD
+ if (vendorext_isvalid)
+ show_motd();
+#endif
+ /* Now use TFTP to load file */
+#ifdef IMAGE_MENU
+ if (vendorext_isvalid && useimagemenu) {
+ selectImage(imagelist);
+ bootp_completed = 0;
+ }
+#endif
+#ifdef DOWNLOAD_PROTO_NFS
+ rpc_init();
+#endif
+ for (;;) {
+ printf("Loading %s ",kernel);
+ while (!loadkernel(kernel)) {
+ printf("Unable to load file.\n");
+ sleep(2); /* lay off server for a while */
+ }
+ }
+}
+
+/**************************************************************************
+DEFAULT_NETMASK - Return default netmask for IP address
+**************************************************************************/
+static inline unsigned long default_netmask(void)
+{
+ int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
+ if (net <= 127)
+ return(htonl(0xff000000));
+ else if (net < 192)
+ return(htonl(0xffff0000));
+ else
+ return(htonl(0xffffff00));
+}
+
+/**************************************************************************
+UDP_TRANSMIT - Send a UDP datagram
+**************************************************************************/
+int udp_transmit(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, int len, const void *buf)
+{
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest arpreq;
+ int arpentry, i;
+ int retry;
+
+ ip = (struct iphdr *)buf;
+ udp = (struct udphdr *)((long)buf + sizeof(struct iphdr));
+ ip->verhdrlen = 0x45;
+ ip->service = 0;
+ ip->len = htons(len);
+ ip->ident = 0;
+ ip->frags = 0;
+ ip->ttl = 60;
+ ip->protocol = IP_UDP;
+ ip->chksum = 0;
+ ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ ip->dest.s_addr = destip;
+ ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
+ udp->src = htons(srcsock);
+ udp->dest = htons(destsock);
+ udp->len = htons(len - sizeof(struct iphdr));
+ udp->chksum = 0;
+ if (destip == IP_BROADCAST) {
+ eth_transmit(broadcast, IP, len, buf);
+ } else {
+ if (((destip & netmask) !=
+ (arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
+ arptable[ARP_GATEWAY].ipaddr.s_addr)
+ destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+ for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
+ if (arptable[arpentry].ipaddr.s_addr == destip) break;
+ if (arpentry == MAX_ARP) {
+ printf("%I is not in my arp table!\n", destip);
+ return(0);
+ }
+ for (i = 0; i<ETHER_ADDR_SIZE; i++)
+ if (arptable[arpentry].node[i]) break;
+ if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
+ arpreq.hwtype = htons(1);
+ arpreq.protocol = htons(IP);
+ arpreq.hwlen = ETHER_ADDR_SIZE;
+ arpreq.protolen = 4;
+ arpreq.opcode = htons(ARP_REQUEST);
+ memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memset(arpreq.thwaddr, 0, ETHER_ADDR_SIZE);
+ memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
+ for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
+ eth_transmit(broadcast, ARP, sizeof(arpreq),
+ &arpreq);
+ if (await_reply(AWAIT_ARP, arpentry,
+ arpreq.tipaddr, TIMEOUT)) goto xmit;
+ rfc951_sleep(retry);
+ /* We have slept for a while - the packet may
+ * have arrived by now. If not, we have at
+ * least some room in the Rx buffer for the
+ * next reply. */
+ if (await_reply(AWAIT_ARP, arpentry,
+ arpreq.tipaddr, 0)) goto xmit;
+ }
+ return(0);
+ }
+xmit:
+ eth_transmit(arptable[arpentry].node, IP, len, buf);
+ }
+ return(1);
+}
+
+/**************************************************************************
+DOWNLOADKERNEL - Try to load file
+**************************************************************************/
+int downloadkernel(data, block, len, eof)
+ unsigned char *data;
+ int block, len, eof;
+{
+#ifdef SIZEINDICATOR
+ static int rlen = 0;
+
+ if (!(block % 4) || eof) {
+ int size;
+ size = ((block-1) * rlen + len) / 1024;
+
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+
+ putchar('0' + (size/1000)%10);
+ putchar('0' + (size/100)%10);
+ putchar('0' + (size/10)%10);
+ putchar('0' + (size/1)%10);
+ }
+#endif
+ if (block == 1)
+ {
+#ifdef SIZEINDICATOR
+ rlen=len;
+#endif
+ if (!eof && (
+#ifdef TAGGED_IMAGE
+ *((unsigned long *)data) == 0x1B031336L ||
+#endif
+#ifdef ELF_IMAGE
+ *((unsigned long *)data) == 0x464C457FL ||
+#endif
+#ifdef AOUT_IMAGE
+ *((unsigned short *)data) == 0x010BL ||
+#endif
+ ((unsigned short *)data)[255] == 0xAA55))
+ {
+ ;
+ }
+ else if (eof)
+ {
+ memcpy(config_buffer, data, len);
+ config_buffer[len] = 0;
+ return (1); /* done */
+ }
+ else
+ {
+ printf("error: not a tagged image\n");
+ return(0); /* error */
+ }
+ }
+ if (len != 0) {
+ if (!os_download(block, data, len))
+ return(0); /* error */
+ }
+ if (eof) {
+ os_download(block+1, data, 0); /* does not return */
+ return(0); /* error */
+ }
+ return(-1); /* there is more data */
+}
+
+#ifdef DOWNLOAD_PROTO_TFTP
+/**************************************************************************
+TFTP - Download extended BOOTP data, or kernel image
+**************************************************************************/
+int tftp(const char *name, int (*fnc)(unsigned char *, int, int, int))
+{
+ int retry = 0;
+ static unsigned short iport = 2000;
+ unsigned short oport;
+ unsigned short len, block = 0, prevblock = 0;
+ int bcounter = 0;
+ struct tftp_t *tr;
+ struct tftp_t tp;
+ int rc;
+ int packetsize = TFTP_DEFAULTSIZE_PACKET;
+
+ /* Clear out the Rx queue first. It contains nothing of interest,
+ * except possibly ARP requests from the DHCP/TFTP server. We use
+ * polling throughout Etherboot, so some time may have passed since we
+ * last polled the receive queue, which may now be filled with
+ * broadcast packets. This will cause the reply to the packets we are
+ * about to send to be lost immediately. Not very clever. */
+ await_reply(AWAIT_QDRAIN, 0, NULL, 0);
+
+ tp.opcode = htons(TFTP_RRQ);
+ len = (sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
+ name, 0, 0, 0, TFTP_MAX_PACKET) - ((char *)&tp)) + 1;
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
+ TFTP_PORT, len, &tp))
+ return (0);
+ for (;;)
+ {
+#ifdef CONGESTED
+ if (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
+#else
+ if (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
+#endif
+ {
+ if (!block && retry++ < MAX_TFTP_RETRIES)
+ { /* maybe initial request was lost */
+ rfc951_sleep(retry);
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ ++iport, TFTP_PORT, len, &tp))
+ return (0);
+ continue;
+ }
+#ifdef CONGESTED
+ if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
+ { /* we resend our last ack */
+#ifdef MDEBUG
+ printf("<REXMT>\n");
+#endif
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ iport, oport,
+ TFTP_MIN_PACKET, &tp);
+ continue;
+ }
+#endif
+ break; /* timeout */
+ }
+ tr = (struct tftp_t *)&nic.packet[ETHER_HDR_SIZE];
+ if (tr->opcode == ntohs(TFTP_ERROR))
+ {
+ printf("TFTP error %d (%s)\n",
+ ntohs(tr->u.err.errcode),
+ tr->u.err.errmsg);
+ break;
+ }
+
+ if (tr->opcode == ntohs(TFTP_OACK)) {
+ char *p = tr->u.oack.data, *e;
+
+ if (prevblock) /* shouldn't happen */
+ continue; /* ignore it */
+ len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
+ if (len > TFTP_MAX_PACKET)
+ goto noak;
+ e = p + len;
+ while (*p != '\000' && p < e) {
+ if (!strcasecmp("blksize", p)) {
+ p += 8;
+ if ((packetsize = getdec(&p)) <
+ TFTP_DEFAULTSIZE_PACKET)
+ goto noak;
+ while (p < e && *p) p++;
+ if (p < e)
+ p++;
+ }
+ else {
+ noak:
+ tp.opcode = htons(TFTP_ERROR);
+ tp.u.err.errcode = 8;
+ len = (sprintf((char *)tp.u.err.errmsg,
+ "RFC1782 error")
+ - ((char *)&tp)) + 1;
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ iport, ntohs(tr->udp.src),
+ len, &tp);
+ return (0);
+ }
+ }
+ if (p > e)
+ goto noak;
+ block = tp.u.ack.block = 0; /* this ensures, that */
+ /* the packet does not get */
+ /* processed as data! */
+ }
+ else if (tr->opcode == ntohs(TFTP_DATA)) {
+ len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
+ if (len > packetsize) /* shouldn't happen */
+ continue; /* ignore it */
+ block = ntohs(tp.u.ack.block = tr->u.data.block); }
+ else /* neither TFTP_OACK nor TFTP_DATA */
+ break;
+
+ if ((block || bcounter) && (block != prevblock+1)) {
+ /* Block order should be continuous */
+ tp.u.ack.block = htons(block = prevblock);
+ }
+ tp.opcode = htons(TFTP_ACK);
+ oport = ntohs(tr->udp.src);
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
+ oport, TFTP_MIN_PACKET, &tp); /* ack */
+ if ((unsigned short)(block-prevblock) != 1) {
+ /* Retransmission or OACK, don't process via callback
+ * and don't change the value of prevblock. */
+ continue;
+ }
+ prevblock = block;
+ retry = 0; /* It's the right place to zero the timer? */
+ if ((rc = fnc(tr->u.data.download,
+ ++bcounter, len, len < packetsize)) >= 0)
+ return(rc);
+ if (len < packetsize) /* End of data */
+ return (1);
+ }
+ return (0);
+}
+#endif /* DOWNLOAD_PROTO_TFTP */
+
+#ifdef RARP_NOT_BOOTP
+/**************************************************************************
+RARP - Get my IP address and load information
+**************************************************************************/
+int rarp()
+{
+ int retry;
+
+ /* arp and rarp requests share the same packet structure. */
+ struct arprequest rarpreq;
+
+ memset(&rarpreq, 0, sizeof(rarpreq));
+
+ rarpreq.hwtype = htons(1);
+ rarpreq.protocol = htons(IP);
+ rarpreq.hwlen = ETHER_ADDR_SIZE;
+ rarpreq.protolen = 4;
+ rarpreq.opcode = htons(RARP_REQUEST);
+ memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ /* sipaddr is already zeroed out */
+ memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ /* tipaddr is already zeroed out */
+
+ for (retry = 0; retry < MAX_ARP_RETRIES; rfc951_sleep(++retry)) {
+ eth_transmit(broadcast, RARP, sizeof(rarpreq), &rarpreq);
+
+ if (await_reply(AWAIT_RARP, 0, rarpreq.shwaddr, TIMEOUT))
+ break;
+ }
+
+ if (retry < MAX_ARP_RETRIES) {
+ sprintf(kernel = kernel_buf, "/tftpboot/kernel.%I", arptable[ARP_CLIENT].ipaddr);
+
+ return (1);
+ }
+ return (0);
+}
+
+#else
+
+/**************************************************************************
+BOOTP - Get my IP address and load information
+**************************************************************************/
+int bootp()
+{
+ int retry;
+#ifndef NO_DHCP_SUPPORT
+ int retry1;
+#endif /* NO_DHCP_SUPPORT */
+ struct bootp_t bp;
+ unsigned long starttime;
+#ifdef T509HACK
+ int flag;
+
+ flag = 1;
+#endif
+ memset(&bp, 0, sizeof(struct bootp_t));
+ bp.bp_op = BOOTP_REQUEST;
+ bp.bp_htype = 1;
+ bp.bp_hlen = ETHER_ADDR_SIZE;
+ bp.bp_xid = xid = starttime = currticks();
+ memcpy(bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+#ifdef NO_DHCP_SUPPORT
+ memcpy(bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
+#else
+ memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcpdiscover, rfc1533_end, sizeof rfc1533_end);
+#endif /* NO_DHCP_SUPPORT */
+
+ for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
+
+ /* Clear out the Rx queue first. It contains nothing of
+ * interest, except possibly ARP requests from the DHCP/TFTP
+ * server. We use polling throughout Etherboot, so some time
+ * may have passed since we last polled the receive queue,
+ * which may now be filled with broadcast packets. This will
+ * cause the reply to the packets we are about to send to be
+ * lost immediately. Not very clever. */
+ await_reply(AWAIT_QDRAIN, 0, NULL, 0);
+
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootp_t), &bp);
+#ifdef T509HACK
+ if (flag) {
+ flag--;
+ } else {
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+ return(1);
+ rfc951_sleep(++retry);
+
+ }
+#else
+#ifdef NO_DHCP_SUPPORT
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+#else
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT)){
+ if (dhcp_reply==DHCPOFFER){
+ dhcp_reply=0;
+ memcpy(bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
+ memcpy(bp.bp_vend+sizeof rfc1533_cookie +sizeof dhcprequest, rfc1533_end, sizeof rfc1533_end);
+ memcpy(bp.bp_vend+9, &dhcp_server, sizeof(in_addr));
+ memcpy(bp.bp_vend+15, &dhcp_addr, sizeof(in_addr));
+ for (retry1 = 0; retry1 < MAX_BOOTP_RETRIES;) {
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootp_t), &bp);
+ dhcp_reply=0;
+ if (await_reply(AWAIT_BOOTP, 0, NULL, TIMEOUT))
+ if (dhcp_reply==DHCPACK)
+ return(1);
+ rfc951_sleep(++retry1);
+ }
+ } else
+#endif /* NO_DHCP_SUPPORT */
+ return(1);
+#ifndef NO_DHCP_SUPPORT
+ }
+ rfc951_sleep(++retry);
+
+#endif /* NO_DHCP_SUPPORT */
+#endif
+ bp.bp_secs = htons((currticks()-starttime)/20);
+ }
+ return(0);
+}
+#endif /* RARP_NOT_BOOTP */
+
+/**************************************************************************
+AWAIT_REPLY - Wait until we get a response for our request
+**************************************************************************/
+int await_reply(int type, int ival, void *ptr, int timeout)
+{
+ unsigned long time;
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest *arpreply;
+ struct bootp_t *bootpreply;
+ struct rpc_t *rpc;
+ unsigned short ptype;
+
+ unsigned int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
+ sizeof(struct udphdr);
+ time = timeout + currticks();
+ /* The timeout check is done below. The timeout is only checked if
+ * there is no packet in the Rx queue. This assumes that eth_poll()
+ * needs a negligible amount of time. */
+ for (;;) {
+ if (eth_poll()) { /* We have something! */
+ /* Check for ARP - No IP hdr */
+ if (nic.packetlen >= ETHER_HDR_SIZE) {
+ ptype = ((unsigned short) nic.packet[12]) << 8
+ | ((unsigned short) nic.packet[13]);
+ } else continue; /* what else could we do with it? */
+ if ((nic.packetlen >= ETHER_HDR_SIZE +
+ sizeof(struct arprequest)) &&
+ (ptype == ARP) ) {
+ unsigned long tmp;
+
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
+ !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
+ (type == AWAIT_ARP)) {
+ memcpy(arptable[ival].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ return(1);
+ }
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ if ((arpreply->opcode == ntohs(ARP_REQUEST)) &&
+ (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
+ arpreply->opcode = htons(ARP_REPLY);
+ memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(arpreply->thwaddr, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETHER_ADDR_SIZE);
+ eth_transmit(arpreply->thwaddr, ARP,
+ sizeof(struct arprequest),
+ arpreply);
+#ifdef MDEBUG
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ printf("Sent ARP reply to: %I\n",tmp);
+#endif MDEBUG
+ }
+ continue;
+ }
+
+ if (type == AWAIT_QDRAIN) {
+ continue;
+ }
+
+ /* Check for RARP - No IP hdr */
+ if ((type == AWAIT_RARP) &&
+ (nic.packetlen >= ETHER_HDR_SIZE +
+ sizeof(struct arprequest)) &&
+ (ptype == RARP)) {
+ arpreply = (struct arprequest *)
+ &nic.packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(RARP_REPLY)) &&
+ !memcmp(arpreply->thwaddr, ptr, ETHER_ADDR_SIZE)) {
+ memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETHER_ADDR_SIZE);
+ memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
+ return(1);
+ }
+ continue;
+ }
+
+ /* Anything else has IP header */
+ if ((nic.packetlen < protohdrlen) ||
+ (ptype != IP) ) continue;
+ ip = (struct iphdr *)&nic.packet[ETHER_HDR_SIZE];
+ if ((ip->verhdrlen != 0x45) ||
+ ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
+ (ip->protocol != IP_UDP)) continue;
+ udp = (struct udphdr *)&nic.packet[ETHER_HDR_SIZE +
+ sizeof(struct iphdr)];
+
+ /* BOOTP ? */
+ bootpreply = (struct bootp_t *)&nic.packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_BOOTP) &&
+ (nic.packetlen >= (ETHER_HDR_SIZE +
+#ifdef NO_DHCP_SUPPORT
+ sizeof(struct bootp_t))) &&
+#else
+ sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
+#endif /* NO_DHCP_SUPPORT */
+ (ntohs(udp->dest) == BOOTP_CLIENT) &&
+ (bootpreply->bp_op == BOOTP_REPLY) &&
+ (bootpreply->bp_xid == xid)) {
+ arptable[ARP_CLIENT].ipaddr.s_addr =
+ bootpreply->bp_yiaddr.s_addr;
+#ifndef NO_DHCP_SUPPORT
+ dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
+#endif /* NO_DHCP_SUPPORT */
+ netmask = default_netmask();
+ arptable[ARP_SERVER].ipaddr.s_addr =
+ bootpreply->bp_siaddr.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+ arptable[ARP_GATEWAY].ipaddr.s_addr =
+ bootpreply->bp_giaddr.s_addr;
+ memset(arptable[ARP_GATEWAY].node, 0, ETHER_ADDR_SIZE); /* Kill arp */
+ if (bootpreply->bp_file[0]) {
+ memcpy(kernel_buf, bootpreply->bp_file, 128);
+ kernel = kernel_buf;
+ }
+ memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
+ decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+#ifdef NO_DHCP_SUPPORT
+ 0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1);
+#else
+ 0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1);
+#endif /* NO_DHCP_SUPPORT */
+ return(1);
+ }
+
+#ifdef DOWNLOAD_PROTO_TFTP
+ /* TFTP ? */
+ if ((type == AWAIT_TFTP) &&
+ (ntohs(udp->dest) == ival)) return(1);
+#endif /* DOWNLOAD_PROTO_TFTP */
+
+#ifdef DOWNLOAD_PROTO_NFS
+ /* RPC ? */
+ rpc = (struct rpc_t *)&nic.packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_RPC) &&
+ (ntohs(udp->dest) == ival) &&
+ (*(unsigned long *)ptr == ntohl(rpc->u.reply.id)) &&
+ (ntohl(rpc->u.reply.type) == MSG_REPLY)) {
+ return (1);
+ }
+#endif /* DOWNLOAD_PROTO_NFS */
+
+ } else {
+ /* Check for abort key only if the Rx queue is empty -
+ * as long as we have something to process, don't
+ * assume that something failed. It is unlikely that
+ * we have no processing time left between packets. */
+ if (iskey() && (getchar() == ESC))
+#ifdef EMERGENCYDISKBOOT
+ exit(0);
+#else
+ longjmp(jmp_bootmenu,1);
+#endif
+ /* Do the timeout after at least a full queue walk. */
+ if ((timeout == 0) || (currticks() > time)) {
+ break;
+ }
+ }
+ }
+ return(0);
+}
+
+/**************************************************************************
+DECODE_RFC1533 - Decodes RFC1533 header
+**************************************************************************/
+int decode_rfc1533(p, block, len, eof)
+ register unsigned char *p;
+ int block, len, eof;
+{
+ static unsigned char *extdata = NULL, *extend = NULL;
+ unsigned char *extpath = NULL;
+ unsigned char *endp;
+
+ if (block == 0) {
+#ifdef IMAGE_MENU
+ memset(imagelist, 0, sizeof(imagelist));
+ menudefault = useimagemenu = 0;
+ menutmo = -1;
+#endif
+#ifdef MOTD
+ memset(motd, 0, sizeof(motd));
+#endif
+ end_of_rfc1533 = NULL;
+ vendorext_isvalid = 0;
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ endp = p + len; }
+ else {
+ if (block == 1) {
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ len -= 4; }
+ if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) {
+ memcpy(extend, p, len);
+ extend += len;
+ } else {
+ printf("Overflow in vendor data buffer! Aborting...\n");
+ *extdata = RFC1533_END;
+ return(0);
+ }
+ p = extdata; endp = extend;
+ }
+ if (eof) {
+ while(p < endp) {
+ unsigned char c = *p;
+ if (c == RFC1533_PAD) {p++; continue;}
+ else if (c == RFC1533_END) {
+ end_of_rfc1533 = endp = p; continue; }
+ else if (c == RFC1533_NETMASK) {memcpy(&netmask, p+2, sizeof(in_addr));}
+
+ else if (c == RFC1533_GATEWAY) {
+ /* This is a little simplistic, but it will
+ usually be sufficient.
+ Take only the first entry */
+ if (TAG_LEN(p) >= sizeof(in_addr))
+ memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr));
+ }
+ else if (c == RFC1533_EXTENSIONPATH)
+ extpath = p;
+#ifndef NO_DHCP_SUPPORT
+ else if (c == RFC2132_MSG_TYPE)
+ { dhcp_reply=*(p+2);
+ }
+ else if (c == RFC2132_SRV_ID)
+ {
+ memcpy(&dhcp_server, p+2, sizeof(in_addr));
+ }
+#endif /* NO_DHCP_SUPPORT */
+ else if (c == RFC1533_HOSTNAME)
+ {
+ hostname = p + 2;
+ hostnamelen = *(p + 1);
+ }
+ else if (c == RFC1533_VENDOR_MAGIC
+#ifndef IMAGE_FREEBSD /* since FreeBSD uses tag 128 for swap definition */
+ && TAG_LEN(p) >= 6 &&
+ !memcmp(p+2,vendorext_magic,4) &&
+ p[6] == RFC1533_VENDOR_MAJOR
+#endif
+ )
+ vendorext_isvalid++;
+#ifdef IMAGE_FREEBSD
+ else if (c == RFC1533_VENDOR_HOWTO) {
+ freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5];
+ }
+#endif
+#ifdef IMAGE_MENU
+ else if (c == RFC1533_VENDOR_MNUOPTS) {
+ parse_menuopts(p+2, TAG_LEN(p));
+ }
+ else if (c >= RFC1533_VENDOR_IMG &&
+ c<RFC1533_VENDOR_IMG+RFC1533_VENDOR_NUMOFIMG){
+ imagelist[c - RFC1533_VENDOR_IMG] = p;
+ useimagemenu++;
+ }
+#endif
+#ifdef MOTD
+ else if (c >= RFC1533_VENDOR_MOTD &&
+ c < RFC1533_VENDOR_MOTD +
+ RFC1533_VENDOR_NUMOFMOTD)
+ motd[c - RFC1533_VENDOR_MOTD] = p;
+#endif
+ else {
+#if 0
+ unsigned char *q;
+ printf("Unknown RFC1533-tag ");
+ for(q=p;q<p+2+TAG_LEN(p);q++)
+ printf("%x ",*q);
+ putchar('\n');
+#endif
+ }
+ p += TAG_LEN(p) + 2;
+ }
+ extdata = extend = endp;
+ if (block == 0 && extpath != NULL) {
+ char fname[64];
+ memcpy(fname, extpath+2, TAG_LEN(extpath));
+ fname[(int)TAG_LEN(extpath)] = '\000';
+ printf("Loading BOOTP-extension file: %s\n",fname);
+ download(fname,decode_rfc1533);
+ }
+ }
+ return(-1); /* proceed with next block */
+}
+
+/**************************************************************************
+IPCHKSUM - Checksum IP Header
+**************************************************************************/
+unsigned short ipchksum(ip, len)
+ register unsigned short *ip;
+ register int len;
+{
+ unsigned long sum = 0;
+ len >>= 1;
+ while (len--) {
+ sum += *(ip++);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+ return((~sum) & 0x0000FFFF);
+}
+
+/**************************************************************************
+RFC951_SLEEP - sleep for expotentially longer times
+**************************************************************************/
+void rfc951_sleep(exp)
+ int exp;
+{
+ static long seed = 0;
+ long q;
+ unsigned long tmo;
+
+#ifdef BACKOFF_LIMIT
+ if (exp > BACKOFF_LIMIT)
+ exp = BACKOFF_LIMIT;
+#endif
+ if (!seed) /* Initialize linear congruential generator */
+ seed = currticks() + *(long *)&arptable[ARP_CLIENT].node
+ + ((short *)arptable[ARP_CLIENT].node)[2];
+ /* simplified version of the LCG given in Bruce Scheier's
+ "Applied Cryptography" */
+ q = seed/53668;
+ if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l;
+ /* compute mask */
+ for (tmo = 63; tmo <= 60*TICKS_PER_SEC && --exp > 0; tmo = 2*tmo+1);
+ /* sleep */
+ printf("<sleep>\n");
+
+ for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
+ if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
+ return;
+}
+
+/**************************************************************************
+CLEANUP_NET - shut down networking
+**************************************************************************/
+void cleanup_net(void)
+{
+#ifdef DOWNLOAD_PROTO_NFS
+ nfs_umountall(ARP_SERVER);
+#endif
+ eth_disable();
+ eth_reset();
+}
+
+/**************************************************************************
+CLEANUP - shut down etherboot so that the OS may be called right away
+**************************************************************************/
+void cleanup(void)
+{
+#if defined(ANSIESC) && defined(CONSOLE_CRT)
+ ansi_reset();
+#endif
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */