aboutsummaryrefslogtreecommitdiff
path: root/sys/i386/netboot/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/netboot/main.c')
-rw-r--r--sys/i386/netboot/main.c415
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);
+}