diff options
Diffstat (limited to 'sys/i386/netboot/main.c')
| -rw-r--r-- | sys/i386/netboot/main.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/sys/i386/netboot/main.c b/sys/i386/netboot/main.c new file mode 100644 index 000000000000..5118e76336b7 --- /dev/null +++ b/sys/i386/netboot/main.c @@ -0,0 +1,415 @@ +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Dec/93 + +**************************************************************************/ + +#include "netboot.h" + +#define ESC 0x1b /* ESC Key */ + +int jmp_bootmenu[10]; +struct exec head; +int txtoff; +void (*kernelentry)(); +char *loadpoint; +char *bootfile; +char bootname[128]; +char cfg[32]; +char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +struct nfs_diskless nfsdiskless; +extern char packet[]; +extern int packetlen; +char *putdec(); + +/************************************************************************** +MAIN - Kick off routine +**************************************************************************/ +main() +{ + int c; + char *p; + extern char edata[], end[]; + for (p=edata; p<end; p++) *p = 0; /* Zero BSS */ +#ifdef ASK_BOOT + while (1) { + printf("\n\rBoot from Network (Y/N) ? "); + c = getchar(); + if ((c >= 'a') && (c <= 'z')) c &= 0x5F; + if (c == '\r') break; + putchar(c); + if (c == 'N') + exit(0); + if (c == 'Y') + break; + printf(" - bad response\n\r"); + } +#endif + gateA20(); + printf("\r\nBOOTP/TFTP bootstrap loader ESC for menu\n\r"); + printf("\r\nSearching for adapter..."); + if (!eth_probe()) { + printf("No adapter found.\r\n"); + exit(0); + } + bootfile = DEFAULT_BOOTFILE; + while (1) { + if (setjmp(jmp_bootmenu)) + bootmenu(); + else + load(); + } +} + +/************************************************************************** +LOAD - Try to get booted +**************************************************************************/ +load() +{ + char *p; + if (!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr) { + printf("\r\nSearching for server...\r\n"); + if (!bootp()) { + printf("No Server found.\r\n"); + longjmp(jmp_bootmenu,1); + } + } + printf("station IP %I, server IP %I\r\n", + arptable[ARP_CLIENT].ipaddr, + arptable[ARP_SERVER].ipaddr); + p = cfg; + *(p++) = 'c'; + *(p++) = 'f'; + *(p++) = 'g'; + *(p++) = '.'; + p = putdec(p, arptable[ARP_CLIENT].ipaddr>>24); + *(p++) = '.'; + p = putdec(p, arptable[ARP_CLIENT].ipaddr>>16); + *(p++) = '.'; + p = putdec(p, arptable[ARP_CLIENT].ipaddr>>8); + *(p++) = '.'; + p = putdec(p, arptable[ARP_CLIENT].ipaddr); + *p = 0; + printf("Loading %s...\r\n",cfg); + if (!tftp(cfg, TFTP_CODE_CFG)) { + printf("Unable to load config file.\r\n"); + longjmp(jmp_bootmenu,1); + } + printf("Loading %s...\r\n",bootfile); + if (!tftp(bootfile, TFTP_CODE_BOOT)) { + printf("Unable to load boot file.\r\n"); + longjmp(jmp_bootmenu,1); + } + if (!(head.a_entry & 0x00F00000)) { /* < 1MB kernel? */ + printf("<1MB kernel. Relocating\r\n"); + bcopy(0x100000, 0, 0x400); /* Relocate */ + bcopy(0x100500, 0x500, ((int)loadpoint) - 0x100500); + } + kernelentry = (void *)(head.a_entry & 0x00FFFFFF); + (*kernelentry)(0,0,0,0,&nfsdiskless,0,0,0); +} + +/************************************************************************** +POLLKBD - Check for Interrupt from keyboard +**************************************************************************/ +pollkbd() +{ + if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1); +} + +/************************************************************************** +UDP_TRANSMIT - Send a UDP datagram +**************************************************************************/ +udp_transmit(destip, srcsock, destsock, len, buf) + unsigned long destip; + unsigned short srcsock, destsock; + int len; + char *buf; +{ + struct iphdr *ip; + struct udphdr *udp; + struct arprequest arpreq, *arpreply; + int arpentry, i; + unsigned long time; + int retry = MAX_ARP_RETRIES; + ip = (struct iphdr *)buf; + udp = (struct udphdr *)(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; + convert_ipaddr(ip->src, &arptable[ARP_CLIENT].ipaddr); + convert_ipaddr(ip->dest, &destip); + ip->chksum = ipchksum(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 { + for(arpentry = 0; arpentry<MAX_ARP; arpentry++) + if (arptable[arpentry].ipaddr == destip) break; + if (arpentry == MAX_ARP) { + printf("%I is not in my arp table!\n"); + 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); + bcopy(arptable[ARP_CLIENT].node, arpreq.shwaddr, ETHER_ADDR_SIZE); + convert_ipaddr(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr); + bzero(arpreq.thwaddr, ETHER_ADDR_SIZE); + convert_ipaddr(arpreq.tipaddr, &destip); + while (retry--) { + eth_transmit(broadcast, ARP, sizeof(arpreq), &arpreq); + time = currticks() + TIMEOUT; + while (time > currticks()) { + pollkbd(); + if (eth_poll() && (packetlen >= ETHER_HDR_SIZE + sizeof(arpreq)) && + (((packet[12] << 8) | packet[13]) == ARP)) { + arpreply = (struct arprequest *)&packet[ETHER_HDR_SIZE]; + if ((arpreply->opcode == ntohs(ARP_REPLY)) && + bcompare(arpreply->sipaddr,arpreq.tipaddr, 4)) { + bcopy(arpreply->shwaddr, arptable[arpentry].node,ETHER_ADDR_SIZE); + goto xmit; + } + } + } + } + return(0); + } +xmit: eth_transmit(arptable[arpentry].node, IP, len, buf); + } + return(1); +} + +/************************************************************************** +TFTP - Try to load something +**************************************************************************/ +tftp(name, type) + char *name; + int type; +{ + int retry = MAX_TFTP_RETRIES; + static unsigned short isocket = 2000; + unsigned short osocket = TFTP; + unsigned short len, block=1; + struct tftp_t tp; + unsigned long time; + int code; + char *p = tp.u.rrq; + isocket++; + tp.opcode = htons(TFTP_RRQ); + while (*name) *(p++) = *(name++); + *(p++) = 0; + *(p++) = 'o'; + *(p++) = 'c'; + *(p++) = 't'; + *(p++) = 'e'; + *(p++) = 't'; + *(p++) = 0; + len = p - (char *)&tp; + while(retry--) { + if (!udp_transmit(arptable[ARP_SERVER].ipaddr, isocket, osocket, + len, &tp)) return(0); +next: time = currticks() + TIMEOUT; + while(time > currticks()) { + pollkbd(); + if (eth_poll()) { + code = tftp_data(&tp, &block, isocket, &osocket, + type); + if (!code) continue; + if (code == TFTP_CODE_EOF) return(1); + if (code == TFTP_CODE_ERROR) return(0); + len = TFTP_MIN_PACKET_SIZE; + retry = MAX_TFTP_RETRIES; + goto next; + } + } + } + return(0); +} + +/************************************************************************** +TFTP_DATA - Check and handle incoming TFTP packets +**************************************************************************/ +tftp_data(req, block, isocket, osocket, type) + struct tftp_t *req; + unsigned short *block, isocket, *osocket; + int type; +{ + struct tftp_t *tp; + char *p; + int len; + if (!chkpacket(TFTP_MIN_PACKET_SIZE, isocket)) return(0); + tp = (struct tftp_t *)&packet[ETHER_HDR_SIZE]; + if (tp->opcode == ntohs(TFTP_ERROR)) { + printf("TFTP error %d (%s)\r\n", ntohs(tp->u.err.errcode), + tp->u.err.errmsg); + longjmp(jmp_bootmenu, 1); + } + if (tp->opcode != htons(TFTP_DATA)) return(0); + len = ntohs(tp->udp.len) - sizeof(struct udphdr) - + (2*sizeof(unsigned short)); + req->opcode = htons(TFTP_ACK); /* Send ack */ + req->u.ack.block = tp->u.data.block; + udp_transmit(arptable[ARP_SERVER].ipaddr, isocket, *osocket, + TFTP_MIN_PACKET_SIZE, req); + if (*block != ntohs(tp->u.data.block)) return(TFTP_CODE_MORE); + if (*block == 1) { + *osocket = htons(tp->udp.src); + if ((type == TFTP_CODE_CFG) && + (len == sizeof(struct nfs_diskless))) { + bcopy(tp->u.data.download, &nfsdiskless, sizeof(struct nfs_diskless)); + return(TFTP_CODE_EOF); + } + bcopy(tp->u.data.download, &head, sizeof(struct exec)); + if ((type == TFTP_CODE_BOOT) && + ((len < sizeof(struct exec)) || (N_BADMAG(head)))) { + printf("Not an executable.\r\n"); + return(TFTP_CODE_ERROR); + } else { + if (((head.a_entry & 0x00FFFFFF) == 0) && + (head.a_text + head.a_data >= RELOC)) { + printf("Executable too large.\r\n"); + return(TFTP_CODE_ERROR); + } + /* We load above 1 mb so we don't clobber DOS */ + loadpoint = (char *)0x100000; + printf("text=0x%X", head.a_text); + } + txtoff = N_TXTOFF(head); + } + p = tp->u.data.download; + *block +=1; + while (len--) { + if (txtoff) { + txtoff--; + p++; + continue; + } + if (head.a_text) { + *(loadpoint++) = *(p++); + head.a_text--; + if (!head.a_text) { + printf(", data=0x%X",head.a_data); + while (((int)loadpoint) & CLOFSET) + *(loadpoint++) = 0; + } + continue; + } + if (head.a_data) { + *(loadpoint++) = *(p++); + head.a_data--; + if (!head.a_data) { + printf(", bss=0x%X\r\n",head.a_bss); + return(TFTP_CODE_EOF); + } + + } + } + return((head.a_text || head.a_data) ? TFTP_CODE_MORE : TFTP_CODE_EOF); +} + +/************************************************************************** +BOOTP - Get my IP address and load information +**************************************************************************/ +bootp() +{ + int retry = MAX_BOOTP_RETRIES; + struct bootp_t bp; + struct bootp_t *reply; + unsigned long time, starttime; + bzero(&bp, sizeof(struct bootp_t)); + bp.bp_op = BOOTP_REQUEST; + bp.bp_htype = 1; + bp.bp_hlen = ETHER_ADDR_SIZE; + bp.bp_xid = starttime = currticks(); + bcopy(arptable[ARP_CLIENT].node, bp.bp_hwaddr, ETHER_ADDR_SIZE); + while(retry--) { + udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER, + sizeof(struct bootp_t), &bp); + time = currticks() + TIMEOUT; + while(time > currticks()) { + pollkbd(); + if (eth_poll()) { /* We have something! */ + reply = (struct bootp_t *)&packet[ETHER_HDR_SIZE]; + if (((!chkpacket(sizeof(struct bootp_t), + BOOTP_CLIENT))) || (reply->bp_op != BOOTP_REPLY)) + continue; + convert_ipaddr(&arptable[ARP_CLIENT].ipaddr, + reply->bp_yiaddr); + convert_ipaddr(&arptable[ARP_SERVER].ipaddr, + reply->bp_siaddr); + bzero(arptable[ARP_SERVER].node, ETHER_ADDR_SIZE); /* Kill arp */ + if (reply->bp_file[0]) { + bcopy(reply->bp_file, bootname, 128); + bootfile = bootname; + } + return(1); + } + } + bp.bp_secs = htons((currticks()-starttime)/20); + } + return(0); +} + +/************************************************************************** +IPCHKSUM - Checksum IP Header +**************************************************************************/ +ipchksum(ip, len) + unsigned short *ip; + int len; +{ + unsigned long sum = 0; + len >>= 1; + while (len--) { + sum += *(ip++); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + return((~sum) & 0x0000FFFF); +} + +/************************************************************************** +CHKPACKET - Quick check to see if incoming packet is good +**************************************************************************/ +chkpacket(size, type) + int size, type; +{ + struct iphdr *ip; + struct udphdr *udp; + if (packetlen < (ETHER_HDR_SIZE + size)) return(0); + if (((packet[12] << 8) | packet[13]) != IP) return(0); + ip = (struct iphdr *)&packet[ETHER_HDR_SIZE]; + if (ip->verhdrlen != 0x45) return(0); + if (ipchksum(ip, sizeof(struct iphdr))) return(0); + if (ip->protocol != IP_UDP) return(0); + udp = (struct udphdr *)&packet[ETHER_HDR_SIZE + sizeof(struct iphdr)]; + if (ntohs(udp->dest) != type) return(0); + return(1); +} + +/************************************************************************** +CONVERT_IPADDR - Convert IP address from net to machine order +**************************************************************************/ +convert_ipaddr(d, s) + char *d,*s; +{ + *(d+3) = *s; + *(d+2) = *(s+1); + *(d+1) = *(s+2); + *d = *(s+3); +} |
