diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /sys/i386/netboot | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'sys/i386/netboot')
| -rw-r--r-- | sys/i386/netboot/Makefile | 54 | ||||
| -rw-r--r-- | sys/i386/netboot/bootmenu.c | 158 | ||||
| -rw-r--r-- | sys/i386/netboot/main.c | 415 | ||||
| -rw-r--r-- | sys/i386/netboot/makerom.c | 42 | ||||
| -rw-r--r-- | sys/i386/netboot/misc.c | 227 | ||||
| -rw-r--r-- | sys/i386/netboot/netboot.doc | 245 | ||||
| -rw-r--r-- | sys/i386/netboot/netboot.h | 186 | ||||
| -rw-r--r-- | sys/i386/netboot/start2.S | 332 | ||||
| -rw-r--r-- | sys/i386/netboot/wd80x3.c | 240 |
9 files changed, 1899 insertions, 0 deletions
diff --git a/sys/i386/netboot/Makefile b/sys/i386/netboot/Makefile new file mode 100644 index 000000000000..550cd7be44cd --- /dev/null +++ b/sys/i386/netboot/Makefile @@ -0,0 +1,54 @@ +# Makefile for NETBOOT +# +# Options: +# -DASK_BOOT - Ask "Boot from Network (Y/N) ?" at startup +# -DSMALL_ROM - Compile for 8K ROMS +# -DROMSIZE - Size of EPROM - Must be set (even for .COM files) +# -DRELOC - Relocation address (usually 0x90000) +# +ROMSIZE=16384 +RELOCADDR=0x90000 +CFLAGS=-O2 -DNFS -DROMSIZE=$(ROMSIZE) -DRELOC=$(RELOCADDR) + +HDRS=netboot.h +COBJS=main.o misc.o wd80x3.o bootmenu.o +SSRCS=start2.S +SOBJS=start2.o + +.SUFFIXES: .c .S .s .o + +all: netboot.com netboot.rom + +makerom: makerom.c + cc -o makerom -DROMSIZE=$(ROMSIZE) makerom.c + +netboot.com: $(COBJS) $(SSRCS) + cc -c $(CFLAGS) $(SSRCS) + ld -e _start -T $(RELOCADDR) -N $(SOBJS) $(COBJS) + strip a.out + size a.out + dd ibs=32 skip=1 <a.out >netboot.com + +netboot.rom: $(COBJS) $(SSRCS) makerom + cc -c $(CFLAGS) -DBOOTROM $(SSRCS) + ld -e _start -T $(RELOCADDR) -N $(SOBJS) $(COBJS) + strip a.out + size a.out + dd ibs=32 skip=1 <a.out >netboot.rom + ./makerom netboot.rom + +test: netboot.com + mount -t pcfs /dev/fd0a /msdos + cp netboot.com /msdos/netboot.com + cp netboot.rom /msdos/netboot.rom + umount /msdos +clean: + rm -f $(COBJS) $(SOBJS) *.s netboot.com netboot.rom a.out makerom + +.c.o: Makefile $(HDRS) + cc $(CFLAGS) -c $< + +.c.s: Makefile $(HDRS) + cc $(CFLAGS) -S $< + + diff --git a/sys/i386/netboot/bootmenu.c b/sys/i386/netboot/bootmenu.c new file mode 100644 index 000000000000..c6b7e79d99b4 --- /dev/null +++ b/sys/i386/netboot/bootmenu.c @@ -0,0 +1,158 @@ +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Dec/93 + +**************************************************************************/ +#include "netboot.h" + +int cmd_ip(), cmd_server(), cmd_bootfile(), cmd_help(), exit(); + +#ifdef SMALL_ROM +struct bootcmds_t { + char *name; + int (*func)(); +} bootcmds[] = { + {"ip", cmd_ip}, + {"server", cmd_server}, + {"bootfile", cmd_bootfile}, + {"diskboot", exit}, + {"autoboot", NULL}, + {NULL, NULL} +}; + +#else /* !SMALL ROM */ + +struct bootcmds_t { + char *name; + int (*func)(); + char *help; +} bootcmds[] = { + {"ip", cmd_ip, "<addr> set my IP address"}, + {"server", cmd_server, "<addr> set server IP address"}, + {"bootfile", cmd_bootfile, "<file> set boot filename"}, + {"help", cmd_help, " this list"}, + {"diskboot", exit, " boot from disk"}, + {"autoboot", NULL, " continue"}, + {NULL, NULL, NULL} +}; + +/************************************************************************** +CMD_HELP - Display help screen - NOT FOR SMALL ROMS +**************************************************************************/ +cmd_help() +{ + struct bootcmds_t *cmd = bootcmds; + printf("\r\n"); + while (cmd->name) { + printf("%s %s\n\r",cmd->name,cmd->help); + cmd++; + } +} +#endif /* SMALL ROM */ + +/************************************************************************** +CMD_IP - Set my IP address +**************************************************************************/ +cmd_ip(p) + char *p; +{ + int i; + if (!setip(p, &arptable[ARP_CLIENT].ipaddr)) { + printf("IP address is %I\r\n", + arptable[ARP_CLIENT].ipaddr); + } +} + +/************************************************************************** +CMD_SERVER - Set server's IP address +**************************************************************************/ +cmd_server(p) + char *p; +{ + int i; + if (!setip(p, &arptable[ARP_SERVER].ipaddr)) { + printf("Server IP address is %I\r\n", + arptable[ARP_SERVER].ipaddr); + } else /* Need to clear arp entry if we change IP address */ + for (i=0; i<6; i++) arptable[ARP_SERVER].node[i] = 0; +} + +extern char bootname[], *bootfile; +/************************************************************************** +CMD_BOOTFILE - set boot filename +**************************************************************************/ +cmd_bootfile(p) + char *p; +{ + char *q = bootname; + if (*p) { + while(*p) + *(q++) = *(p++); + *q = 0; + } else + printf("Bootfile is %s\r\n", bootfile); +} + + + +/************************************************************************** +EXECUTE - Decode command +**************************************************************************/ +execute(buf) + char *buf; +{ + char *p, *q; + struct bootcmds_t *cmd = bootcmds; + if (!(*buf)) return(0); + while(cmd->name) { + p = buf; + q = cmd->name; + while (*q && (*(q++) == *(p++))) ; + if ((!(*q)) && ((*p == ' ') || (!(*p)))) { + if (!cmd->func) return(1); + while (*p == ' ') p++; + (cmd->func)(p); + return(0); + } else + cmd++; + } +#ifdef SMALL_ROM + printf("invalid command\n\r"); +#else + printf("bad command - type 'help' for list\n\r"); +#endif + return(0); +} + +/************************************************************************** +BOOTMENU - Present boot options +**************************************************************************/ +bootmenu() +{ + char cmd[80]; + int ptr, c; + while (1) { + ptr = 0; + printf("\r\nboot> "); + while (ptr < 80) { + c = getchar(); + if (c == '\r') + break; + else if (c == '\b') { + if (ptr > 0) { + ptr--; + printf("\b \b"); + } + } else { + cmd[ptr++] = c; + putchar(c); + } + } + cmd[ptr] = 0; + printf("\r\n"); + if (execute(cmd)) break; + } + eth_reset(); +} 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); +} diff --git a/sys/i386/netboot/makerom.c b/sys/i386/netboot/makerom.c new file mode 100644 index 000000000000..98cc65130ec4 --- /dev/null +++ b/sys/i386/netboot/makerom.c @@ -0,0 +1,42 @@ +#include <stdio.h> +#include <fcntl.h> + +unsigned char rom[ROMSIZE]; +unsigned int sum; + +main(argc,argv) + int argc; char *argv[]; +{ + int i, fd; + if (argc < 1) { + fprintf(stderr,"usage: %s rom-file\n",argv[0]); + exit(2); + } + if ((fd = open(argv[1], O_RDWR)) < 0) { + perror("unable to open file"); + exit(2); + } + bzero(rom, ROMSIZE); + if (read(fd, rom, ROMSIZE) < 0) { + perror("read error"); + exit(2); + } + rom[5] = 0; + for (i=0,sum=0; i<ROMSIZE; i++) + sum += rom[i]; + rom[5] = -sum; + for (i=0,sum=0; i<ROMSIZE; i++); + sum += rom[i]; + if (sum) + printf("checksum fails.\n"); + if (lseek(fd, 0L, SEEK_SET) < 0) { + perror("unable to seek"); + exit(2); + } + if (write(fd, rom, ROMSIZE) < 0) { + perror("unable to write"); + exit(2); + } + close(fd); + exit(0); +} diff --git a/sys/i386/netboot/misc.c b/sys/i386/netboot/misc.c new file mode 100644 index 000000000000..65977add7874 --- /dev/null +++ b/sys/i386/netboot/misc.c @@ -0,0 +1,227 @@ +/************************************************************************** +MISC Support Routines +**************************************************************************/ + +#include "netboot.h" + +#define NO_SWITCH /* saves space */ + +/************************************************************************** +TWIDDLE +**************************************************************************/ +twiddle() +{ + static int count=0; + char tiddles[]="-\\|/"; + putchar(tiddles[(count++)&3]); + putchar('\b'); +} + +/************************************************************************** +BCOPY +**************************************************************************/ +bcopy(s,d,n) + char *s, *d; + int n; +{ + while ((n--) > 0) { + *(d++) = *(s++); + } +} + +/************************************************************************** +BZERO +**************************************************************************/ +bzero(d,n) + char *d; + int n; +{ + while ((n--) > 0) { + *(d++) = 0; + } +} + +/************************************************************************** +BCOMPARE +**************************************************************************/ +bcompare(d,s,n) + char *d,*s; + int n; +{ + while ((n--) > 0) { + if (*(d++) != *(s++)) return(0); + } + return(1); +} + + +/************************************************************************** +PRINTF +**************************************************************************/ +printf(fmt, data) + char *fmt; + int data; +{ + char *p; + int *dp = &data; + while (*fmt) { + if (*fmt == '%') { /* switch() uses more space */ + fmt++; + if (*fmt == 'X') + printhl(*dp++); + if (*fmt == 'x') + printhw(*dp++); + if (*fmt == 'd') + printdec(*dp++); + if (*fmt == 'I') + printip(*dp++); + if (*fmt == 'c') + putchar(*(dp++) & 0x00FF); + if (*fmt == 's') { + p = (char *)*dp++; + while (*p) putchar(*p++); + } + } else putchar(*fmt); + fmt++; + } + return(0); +} + + +/************************************************************************** +PRINTDEC - Print a number in decimal +**************************************************************************/ +printdec(n) + int n; +{ + char buf[16], *p; + p = buf; + if (n < 0) { + putchar('-'); + n = -n; + } + do { + *(p++) = '0' + (n%10); + n = n/10; + } while(n); + while ((--p) >= buf) putchar(*p); +} + +char *putdec(p, n) + char *p; + int n; +{ + n = n & 0x00FF; + if (n/100) *(p++) = '0' + (n/100); + if (n/10) *(p++) = '0' + ((n/10) % 10); + *(p++) = '0' + (n%10); + return(p); +} + +/************************************************************************** +PRINTDEC - Print a number in decimal +**************************************************************************/ +static char hex[]="0123456789ABCDEF"; +printhl(h) { + printhw(h>>16); + printhw(h); +} +printhw(h) { + printhb(h>>8); + printhb(h); +} +printhb(h) { + putchar(hex[(h>>4)& 0x0F]); + putchar(hex[h &0x0F]); +} + +/************************************************************************** +PRINTIP - Print an IP address in x.x.x.x notation +**************************************************************************/ +printip(i) + unsigned i; +{ + printdec((i>>24) & 0x0FF); + putchar('.'); + printdec((i>>16) & 0x0FF); + putchar('.'); + printdec((i>>8) & 0x0FF); + putchar('.'); + printdec(i & 0x0FF); +} + +/************************************************************************** +SETIP - Convert an ascii x.x.x.x to binary form +**************************************************************************/ +setip(p, i) + char *p; + unsigned *i; +{ + unsigned ip = 0; + int val; + if (((val = getdec(&p)) < 0) || (val > 255)) return(0); + if (*p != '.') return(0); + p++; + ip = val; + if (((val = getdec(&p)) < 0) || (val > 255)) return(0); + if (*p != '.') return(0); + p++; + ip = (ip << 8) | val; + if (((val = getdec(&p)) < 0) || (val > 255)) return(0); + if (*p != '.') return(0); + p++; + ip = (ip << 8) | val; + if (((val = getdec(&p)) < 0) || (val > 255)) return(0); + *i = (ip << 8) | val; + return(1); +} + +getdec(ptr) + char **ptr; +{ + char *p = *ptr; + int ret=0; + if ((*p < '0') || (*p > '9')) return(-1); + while ((*p >= '0') && (*p <= '9')) { + ret = ret*10 + (*p - '0'); + p++; + } + *ptr = p; + return(ret); +} + + +#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ +#define K_STATUS 0x64 /* keyboard status */ +#define K_CMD 0x64 /* keybd ctlr command (write-only) */ + +#define K_OBUF_FUL 0x01 /* output buffer full */ +#define K_IBUF_FUL 0x02 /* input buffer full */ + +#define KC_CMD_WIN 0xd0 /* read output port */ +#define KC_CMD_WOUT 0xd1 /* write output port */ +#define KB_A20 0x9f /* enable A20, + enable output buffer full interrupt + enable data line + disable clock line */ + +/* + * Gate A20 for high memory + */ +unsigned char x_20 = KB_A20; +gateA20() +{ +#ifdef IBM_L40 + outb(0x92, 0x2); +#else IBM_L40 + while (inb(K_STATUS) & K_IBUF_FUL); + while (inb(K_STATUS) & K_OBUF_FUL) + (void)inb(K_RDWR); + + outb(K_CMD, KC_CMD_WOUT); + while (inb(K_STATUS) & K_IBUF_FUL); + outb(K_RDWR, x_20); + while (inb(K_STATUS) & K_IBUF_FUL); +#endif IBM_L40 +} + diff --git a/sys/i386/netboot/netboot.doc b/sys/i386/netboot/netboot.doc new file mode 100644 index 000000000000..3576a56349be --- /dev/null +++ b/sys/i386/netboot/netboot.doc @@ -0,0 +1,245 @@ + +Configuring a Diskless FreeBSD Client and Boot Server +----------------------------------------------------- + +Prepared by: Todd Pfaff <todd@flex.eng.mcmaster.ca> + Martin Renters <martin@innovus.com> + +Last update: Jan. 31, 1994 + + +Notes +----- + +Martin Renters developed the diskless boot code for FreeBSD and has tested +it with an HPUX server. + +Todd Pfaff has used a SunOS 4.1.2 Sun 4 server to support a diskless FreeBSD +client. The following procedure applies to that environment so your mileage +may vary. + +The following Ethernet cards have been tested with netboot: + + WD8003E + WD8013EPC + +Currently, diskless clients must be on the same subnet as the NFS server. +Code has been added to the diskless_cfg program to allow for booting clients +across a gateway, but the kernel code (in /usr/src/sys/nfs/nfs_vfsops.c) to +add a default route at boot time is not yet working. + + +Notation +-------- + +<server> - host name of root/swap NFS server +<client> - host name of diskless FreeBSD client +<tftp_root> - path to TFTP root directory on TFTP server + (usually /tftpboot on SunOS 4.1.x) +<client_root> - path to client root filesystem on NFS server + (eg. /export/root/<client>) +<client_swap> - path to client swap file on NFS server + (eg. /export/swap/<client>) +<client_usr> - path to client usr filesystem on NFS server +<swap_size> - size of swap file in 512 byte blocks + + + +Procedure +--------- + +1) Configure a bootp server. + +CMU bootp-2.1 has been verified to do the job on a Sun 4/SunOS 4.1.2. + +Create a bootptab entry for the diskless client. For example: + +client:\ + :ht=ethernet:\ + :ha=0123456789ab:\ + :sm=255.255.255.0:\ + :ip=1.2.3.4:\ + :hn:\ + :vm=rfc1048:\ + :ds=1.2.3.1 1.2.3.2:\ + :gw=1.2.3.1:\ + :hd=/:\ + :bf=386bsd: + + +2) Configure a tftp server. + +I already had this set up for diskless SunOS clients so I didn't have to do +anything here. + +You will later place the client kernel and diskless configuration file in +the tftp root directory. + + +3) Build a diskless kernel. + +You must have a standalone FreeBSD system running to create a kernel +for the diskless client. + +Make sure you are running a current config. If not, rebuild and install +/usr/src/usr.sbin/config. + +Create a kernel configuration with: + + option DISKLESS + config "386bsd" swap on nfs size <swap_size> + +and config and make this kernel. + +Install the kernel in <tftp_root> and <server>:<client_root>. + +Here's a minimal diskless kernel config file: + +machine "i386" +cpu "I486_CPU" +ident "NODISK" +timezone 8 dst +maxusers 10 +maxfdescs 2048 +options INET +options NFS +options "COMPAT_43" +options "TCP_COMPAT_42" +options XSERVER +options UCONSOLE +options DISKLESS +config "386bsd" swap on nfs size 50000 +controller isa0 +controller fd0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fd0 drive 0 +disk fd1 at fd0 drive 1 +device pc0 at isa? port "IO_KBD" tty irq 1 vector pcrint +device npx0 at isa? port "IO_NPX" irq 13 vector npxintr +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device lpt0 at isa? port "IO_LPT1" tty irq 7 vector lptintr +device ed0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector edintr +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device pty 8 +pseudo-device speaker +pseudo-device swappager +pseudo-device vnodepager +pseudo-device devpager + + +4) Create a client root filesystem and swap file on the NFS server. + +Make a new directory which will be the client's root filesystem. +This should include /bin, /dev, /etc, /sbin, /tmp, /var, a mountpoint +for /usr, and the client kernel (386bsd or whatever is given in the +bf field of the bootptab entry). + +For example, you can copy the root filesystem from a standalone FreeBSD +system to your server: + + <server># mkdir <client_root> + <server># mount freebsdhost:/ /mnt + <server># cd /mnt + <server># find * -print | cpio -pm <client_root> + +To create a swap file: + + <server># touch <client_swap> + +or under SunOS 4.1.x: + + <server># mkfile -n <swap_size>b <client_swap> + +Export this root and swap to <client>. + +A few things to modify in the client root directory: + +/dev/... + Make sure all the devices you need are there. + If not you will have to mount this directory on a running + FreeBSD system and use MAKEDEV. + [This one bit me because I used tar to copy an root filesystem and + none of the device files were copied. Use cpio if you want to copy + a root filesystem and preserve device files. -TP] + +/etc/exports + Delete this. You probably don't want your diskless client to be an + NFS server. + +/etc/fstab + You need at least the following entries: + + <server>:<client_root> / nfs rw 0 0 + <server>:<client_usr> /usr nfs rw 0 0 + +/etc/myname + Client hostname. + +/etc/netstart + Set interface config and default route. + +/etc/rc + change: + mount -a -t nonfs + to: + mount -a + + and remove the final 'mount -a -t nfs' at the end of rc. + +/etc/rc.local +/etc/sendmail.cf + + +5) Create a diskless configuration file. + +The program /sbin/diskless_cfg is used to create a configuration file +which is loaded via tftp from the server by the netboot program. + +This program must be run on the NFS server from which root and swap +will be mounted because it stores the server hostname and file handles +of the root path and swap file in a file. + +Build /usr/src/usr.sbin/diskless_cfg on your NFS server. +The supplied Makefile is meant to build diskless_cfg under a FreeBSD +source tree, but you can compile diskless_cfg manually with: + + cc -DNFS diskless_cfg.c -o diskless_cfg + +Run the program as follows: + + diskless_cfg -rootfs <client_root> -swap <client_swap> -hostname <client> + +and this will create a file cfg.X.X.X.X where X.X.X.X is the decimal IP +address of <client>. + +Copy cfg.X.X.X.X to <tftp_root>. + + +6) Compile the netboot.com program. + +cd /sys/i386/netboot; make + +*** NOTE *** + +At the top of wd80x3.c in /sys/i386/netboot/wd80x3.c is a line: + + #define WE_DEFAULT_MEM 0xD0000 + +which defines the Ethernet adapter RAM buffer address. Make sure you edit +this to reflect the address given in your diskless kernel config file: + + device ed0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector edintr + +Copy netboot.com to a DOS disk or put netboot.rom into a boot PROM for +your Ethernet adapter. + +Run netboot.com from DOS to initiate the diskless client boot. + +If your bootp server is not responding you can set the IP addresses of the +client and server manually and continue. Press escape during the boot to +to the netboot monitor. Type help in the netboot monitor for a list of +commands. + + diff --git a/sys/i386/netboot/netboot.h b/sys/i386/netboot/netboot.h new file mode 100644 index 000000000000..4a62e80c726e --- /dev/null +++ b/sys/i386/netboot/netboot.h @@ -0,0 +1,186 @@ + +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Dec/93 + +**************************************************************************/ + +#include <a.out.h> +#include <netdb.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <net/if.h> +#include <nfs/nfsv2.h> +#include <nfs/nfsdiskless.h> + +#ifndef DEFAULT_BOOTFILE +#define DEFAULT_BOOTFILE "386bsd" +#endif + +#ifndef MAX_TFTP_RETRIES +#define MAX_TFTP_RETRIES 20 +#endif + +#ifndef MAX_BOOTP_RETRIES +#define MAX_BOOTP_RETRIES 20 +#endif + +#ifndef MAX_ARP_RETRIES +#define MAX_ARP_RETRIES 20 +#endif + +#ifndef TIMEOUT /* Inter-packet retry in ticks 18/sec */ +#define TIMEOUT 20 +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define ETHER_ADDR_SIZE 6 /* Size of Ethernet address */ +#define ETHER_HDR_SIZE 14 /* Size of ethernet header */ + +#define ARP_CLIENT 0 +#define ARP_SERVER 1 +#define ARP_GATEWAY 2 +#define ARP_NS 3 +#define MAX_ARP ARP_NS+1 + +#define IP 0x0800 +#define ARP 0x0806 + +#define BOOTP_SERVER 67 +#define BOOTP_CLIENT 68 +#define TFTP 69 + +#define IP_UDP 17 +#define IP_BROADCAST 0xFFFFFFFF + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +#define TFTP_CODE_EOF 1 +#define TFTP_CODE_MORE 2 +#define TFTP_CODE_ERROR 3 +#define TFTP_CODE_BOOT 4 +#define TFTP_CODE_CFG 5 + +struct arptable_t { + unsigned long ipaddr; + unsigned char node[6]; +} arptable[MAX_ARP]; + +struct arprequest { + unsigned short hwtype; + unsigned short protocol; + char hwlen; + char protolen; + unsigned short opcode; + char shwaddr[6]; + char sipaddr[4]; + char thwaddr[6]; + char tipaddr[4]; +}; + +struct iphdr { + char verhdrlen; + char service; + unsigned short len; + unsigned short ident; + unsigned short frags; + char ttl; + char protocol; + unsigned short chksum; + char src[4]; + char dest[4]; +}; + +struct udphdr { + unsigned short src; + unsigned short dest; + unsigned short len; + unsigned short chksum; +}; + +struct bootp_t { + struct iphdr ip; + struct udphdr udp; + char bp_op; + char bp_htype; + char bp_hlen; + char bp_hops; + unsigned long bp_xid; + unsigned short bp_secs; + unsigned short unused; + char bp_ciaddr[4]; + char bp_yiaddr[4]; + char bp_siaddr[4]; + char bp_giaddr[4]; + char bp_hwaddr[16]; + char bp_sname[64]; + char bp_file[128]; + char bp_vend[64]; +}; + +struct tftp_t { + struct iphdr ip; + struct udphdr udp; + unsigned short opcode; + union { + char rrq[512]; + struct { + unsigned short block; + char download[512]; + } data; + struct { + unsigned short block; + } ack; + struct { + unsigned short errcode; + char errmsg[512]; + } err; + } u; +}; +#define TFTP_MIN_PACKET_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr) + 4) + +#ifdef oldstuff +static inline unsigned short htons(unsigned short in) +{ + return((in >> 8) | (in << 8)); +} +#define ntohs htons + +static inline unsigned long htonl(unsigned long in) +{ + return((in >> 24) | ((in >> 16) & 0x0000FF00) | + ((in << 16) & 0x00FF0000) | (in << 24)); +} +#define ntohl htonl +#endif + +static inline unsigned char inb(a) + unsigned short a; +{ + unsigned char d; + asm volatile( "inb %1, %0" : "=a" (d) : "d" (a)); + return d; +} + +static inline void outb(a,d) + unsigned short a; + unsigned char d; +{ + asm volatile( "outb %0, %1" : : "a" (d), "d" (a)); +} diff --git a/sys/i386/netboot/start2.S b/sys/i386/netboot/start2.S new file mode 100644 index 000000000000..dd249c742d16 --- /dev/null +++ b/sys/i386/netboot/start2.S @@ -0,0 +1,332 @@ + +#define STACKADDR 0xe000 /* Needs to be end of bss + stacksize */ +#define KERN_CODE_SEG 0x08 +#define KERN_DATA_SEG 0x10 +#define REAL_MODE_SEG 0x18 +#define CR0_PE 1 + +#define opsize .byte 0x66 +#define addrsize .byte 0x67 + +/* At entry, the processor is in 16 bit real mode and the code is being + * executed from an address it was not linked to. Code must be pic and + * 32 bit sensitive until things are fixed up. + */ +#ifdef BOOTROM + .word 0xaa55 /* bios extension signature */ + .byte (ROMSIZE>>9) /* no. of 512B blocks */ + jmp 1f /* enter from bios here */ + .byte 0 /* checksum */ +1: push %eax + push %ds + xor %eax,%eax + mov %ax,%ds + .byte 0xa1 /* MOV 0x304,%ax */ + .word 0x304 + .byte 0x3d /* CMP $0x4d52, %ax == 'MR' */ + .word 0x4d52 + jz 2f + .byte 0xa1 /* MOV 0x64, %ax */ + .word 0x64 + .byte 0xa3 /* MOV %ax, 0x300 */ + .word 0x300 + .byte 0xa1 /* MOV 0x66, %ax */ + .word 0x66 + .byte 0xa3 /* MOV %ax, 0x302 */ + .word 0x302 + .byte 0xb8 /* MOV $_start-RELOCADDR, %ax */ + .word (_start-RELOC) + .byte 0xa3 /* MOV %ax, 0x64 */ + .word 0x64 + mov %cs,%ax + .byte 0xa3 /* MOV %ax, 0x66 */ + .word 0x66 + .byte 0xb8 /* MOV 'MR',%ax */ + .word 0x4d52 + .byte 0xa3 /* MOV %ax, 0x304 */ + .word 0x304 +2: pop %ds + pop %eax + lret +#endif + +/************************************************************************** +START - Where all the fun begins.... +**************************************************************************/ + .globl _start +_start: + cli + cld +#ifdef BOOTROM /* relocate ourselves */ + xor %esi, %esi /* zero for ROMs */ +#else + .byte 0xbe /* MOV $0x100,%si -- 100h for .COM */ + .word 0x100 +#endif + xor %edi,%edi + .byte 0xb8 /* MOV $RELOCADDR>>4, %ax */ + .word (RELOC>>4) + mov %ax, %es + .byte 0xb9 /* MOV $ROMSIZE, %cx */ + .word ROMSIZE + cs + rep + movsb + opsize + ljmp $(RELOC>>4),$1f-RELOC /* Jmp to RELOC:1f */ +1: + nop + mov %cs,%ax + mov %ax,%ds + mov %ax,%es + mov %ax,%ss + .byte 0xb8 /* MOV $STACKADDR, %ax */ + .word STACKADDR + mov %eax,%esp + opsize + call _real_to_prot + call _main + .globl _exit +_exit: + call _prot_to_real +#ifdef BOOTROM + xor %eax,%eax + mov %ax,%ds + .byte 0xa1 /* MOV 0x302, %ax */ + .word 0x302 + push %eax + .byte 0xa1 /* MOV 0x300, %ax */ + .word 0x300 + push %eax + lret +#else + int $0x19 +#endif + +/************************************************************************** +CURRTICKS - Get Time +**************************************************************************/ + .globl _currticks +_currticks: + push %ebp + mov %esp,%ebp + push %ecx + push %edx + xor %edx,%edx + call _prot_to_real + xor %eax,%eax + int $0x1a + opsize + call _real_to_prot + xor %eax,%eax + shl $16,%ecx + mov %edx,%eax + or %ecx,%eax + pop %edx + pop %ecx + pop %ebp + ret + +/************************************************************************** +PUTCHAR - Print a character +**************************************************************************/ + .globl _putchar +_putchar: + push %ebp + mov %esp,%ebp + push %ecx + push %ebx + movb 8(%ebp),%cl + call _prot_to_real + opsize + mov $1,%ebx + movb $0x0e,%ah + movb %cl,%al + int $0x10 + opsize + call _real_to_prot + pop %ebx + pop %ecx + pop %ebp + ret + +/************************************************************************** +GETCHAR - Get a character +**************************************************************************/ + .globl _getchar +_getchar: + push %ebp + mov %esp,%ebp + push %ebx + call _prot_to_real + movb $0x0,%ah + int $0x16 + movb %al,%bl + opsize + call _real_to_prot + xor %eax,%eax + movb %bl,%al + pop %ebx + pop %ebp + ret + +/************************************************************************** +ISKEY - Check for keyboard interrupt +**************************************************************************/ + .globl _iskey +_iskey: + push %ebp + mov %esp,%ebp + push %ebx + call _prot_to_real + xor %ebx,%ebx + movb $0x1,%ah + int $0x16 + opsize + jz 1f + movb %al,%bl +1: + opsize + call _real_to_prot + xor %eax,%eax + movb %bl,%al + pop %ebx + pop %ebp + ret + + +/* + * C library -- _setjmp, _longjmp + * + * longjmp(a,v) + * will generate a "return(v)" from the last call to + * setjmp(a) + * by restoring registers from the stack. + * The previous signal state is restored. + */ + + .globl _setjmp +_setjmp: + movl 4(%esp),%ecx + movl 0(%esp),%edx + movl %edx, 0(%ecx) + movl %ebx, 4(%ecx) + movl %esp, 8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + movl %eax,24(%ecx) + movl $0,%eax + ret + + .globl _longjmp +_longjmp: + movl 4(%esp),%edx + movl 8(%esp),%eax + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret + +/************************************************************************** +___MAIN - Dummy to keep GCC happy +**************************************************************************/ + .globl ___main +___main: + ret + +/************************************************************************** +REAL_TO_PROT - Go from REAL mode to Protected Mode +**************************************************************************/ + .globl _real_to_prot +_real_to_prot: + cli + cs + addrsize + lgdt gdtarg-RELOC + mov %cr0, %eax + opsize + or $CR0_PE, %eax + mov %eax, %cr0 /* turn on protected mode */ + + /* jump to relocation, flush prefetch queue, and reload %cs */ + opsize + ljmp $KERN_CODE_SEG, $1f +1: + /* reload other segment registers */ + movl $KERN_DATA_SEG, %eax + movl %ax, %ds + movl %ax, %es + movl %ax, %ss + add $RELOC,%esp /* Fix up stack pointer */ + pop %eax /* Fix up return Address */ + add $RELOC,%eax + push %eax + ret + + +/************************************************************************** +PROT_TO_REAL - Go from Protected Mode to REAL Mode +**************************************************************************/ + .globl _prot_to_real +_prot_to_real: + pop %eax + sub $RELOC,%eax /* Adjust return address */ + push %eax + sub $RELOC,%esp /* Adjust stack pointer */ + ljmp $REAL_MODE_SEG, $1f /* jump to a 16 bit segment */ +1: + /* clear the PE bit of CR0 */ + mov %cr0, %eax + opsize + andl $0!CR0_PE, %eax + mov %eax, %cr0 + + /* make intersegment jmp to flush the processor pipeline + * and reload CS register + */ + opsize + ljmp $(RELOC)>>4, $2f-RELOC +2: + /* we are in real mode now + * set up the real mode segment registers : DS, SS, ES + */ + mov %cs, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %ss + sti + opsize + ret + +/************************************************************************** +GLOBAL DESCRIPTOR TABLE +**************************************************************************/ + .align 4 +gdt: + .word 0, 0 + .byte 0, 0x00, 0x00, 0 + + /* code segment */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + /* data segment */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + + /* 16 bit real mode */ + .word 0xffff, 0 + .byte 0, 0x9b, 0x0f, 0 + + .align 4 +gdtarg: + .word 0x1f /* limit */ + .long gdt /* addr */ diff --git a/sys/i386/netboot/wd80x3.c b/sys/i386/netboot/wd80x3.c new file mode 100644 index 000000000000..a68dc44c1f18 --- /dev/null +++ b/sys/i386/netboot/wd80x3.c @@ -0,0 +1,240 @@ + +/************************************************************************** +NETBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Dec/93 + +**************************************************************************/ + +#include "netboot.h" + +unsigned short we_base; +unsigned char *we_bmem; + +#define WE_LOW_BASE 0x200 +#define WE_HIGH_BASE 0x3e0 +#define WE_DEFAULT_MEM 0xD0000 +#define WE_MIN_PACKET 64 +#define WE_TXBUF_SIZE 6 +#define WE_RXBUF_END 32 + +#define WE_MSR 0x00 +#define WE_ICR 0x01 +#define WE_IAR 0x02 +#define WE_BIO 0x03 +#define WE_IRR 0x04 +#define WE_LAAR 0x05 +#define WE_IJR 0x06 +#define WE_GP2 0x07 +#define WE_LAR 0x08 +#define WE_BID 0x0E +#define WE_P0_COMMAND 0x10 +#define WE_P0_PSTART 0x11 +#define WE_P0_PSTOP 0x12 +#define WE_P0_BOUND 0x13 +#define WE_P0_TSR 0x14 +#define WE_P0_TPSR 0x14 +#define WE_P0_TBCR0 0x15 +#define WE_P0_TBCR1 0x16 +#define WE_P0_ISR 0x17 +#define WE_P0_RBCR0 0x1A +#define WE_P0_RBCR1 0x1B +#define WE_P0_RSR 0x1C +#define WE_P0_RCR 0x1C +#define WE_P0_TCR 0x1D +#define WE_P0_DCR 0x1E +#define WE_P0_IMR 0x1F +#define WE_P1_COMMAND 0x10 +#define WE_P1_PAR0 0x11 +#define WE_P1_PAR1 0x12 +#define WE_P1_PAR2 0x13 +#define WE_P1_PAR3 0x14 +#define WE_P1_PAR4 0x15 +#define WE_P1_PAR5 0x16 +#define WE_P1_CURR 0x17 +#define WE_P1_MAR0 0x18 + +#define WE_COMMAND_PS0 0x0 /* Page 0 select */ +#define WE_COMMAND_PS1 0x40 /* Page 1 select */ +#define WE_COMMAND_PS2 0x80 /* Page 2 select */ +#define WE_COMMAND_TXP 0x04 /* transmit packet */ +#define WE_COMMAND_STA 0x02 /* start */ +#define WE_COMMAND_STP 0x01 /* stop */ + +#define WE_ISR_PRX 0x01 /* successful recv */ +#define WE_ISR_PTX 0x02 /* successful xmit */ +#define WE_ISR_RXE 0x04 /* receive error */ +#define WE_ISR_TXE 0x08 /* transmit error */ +#define WE_ISR_OVW 0x10 /* Overflow */ +#define WE_ISR_CNT 0x20 /* Counter overflow */ +#define WE_ISR_RST 0x80 /* reset */ + +#define WE_RSTAT_PRX 0x01 /* successful recv */ +#define WE_RSTAT_CRC 0x02 /* CRC error */ +#define WE_RSTAT_FAE 0x04 /* Frame alignment error */ +#define WE_RSTAT_OVER 0x08 /* overflow */ + +char packet[1600]; +int packetlen; +int bit16; + +/************************************************************************** +ETH_PROBE - Look for an adapter +**************************************************************************/ +eth_probe() +{ + unsigned short base; + unsigned short chksum; + unsigned char c; + int i; + for (we_base = WE_LOW_BASE; we_base <= WE_HIGH_BASE; we_base += 0x20) { + chksum = 0; + for (i=8; i<16; i++) + chksum += inb(i+we_base); + if ((chksum & 0x00FF) == 0x00FF) + break; + } + if (we_base > WE_HIGH_BASE) return(0); /* No adapter found */ + + for (i = 1; i<6; i++) /* Look for aliased registers */ + if (inb(we_base+i) != inb(we_base+i+WE_LAR)) break; + if (i == 6) { /* Aliased */ + we_bmem = (char *)WE_DEFAULT_MEM; + bit16 = 0; + } else { + we_bmem = (char *)(0x80000 | ((inb(we_base+WE_MSR) & 0x3F) << 13)); + bit16 = 1; + } + outb(we_base+WE_MSR, 0x80); /* Reset */ + printf("\r\nSMC80x3 base 0x%x, memory 0x%X, etheraddr ",we_base, we_bmem); + for (i=0; i<6; i++) + printhb((int)(arptable[ARP_CLIENT].node[i] = inb(i+we_base+WE_LAR))); + printf("\r\n"); + outb(we_base+WE_MSR,(((unsigned)we_bmem >> 13) & 0x3F) | 0x40); + iskey(); /* Kill some time while device resets */ + eth_reset(); +} + +/************************************************************************** +ETH_RESET - Reset adapter +**************************************************************************/ +eth_reset() +{ + int i; + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STP); + outb(we_base+WE_P0_DCR, 0x48); + outb(we_base+WE_P0_RBCR0, 0); + outb(we_base+WE_P0_RBCR1, 0); + outb(we_base+WE_P0_RCR, 4); /* allow broadcast frames */ + outb(we_base+WE_P0_TCR, 0); + outb(we_base+WE_P0_TPSR, 0); + outb(we_base+WE_P0_PSTART, WE_TXBUF_SIZE); + outb(we_base+WE_P0_PSTOP, WE_RXBUF_END); /* All cards have 8K */ + outb(we_base+WE_P0_BOUND, WE_TXBUF_SIZE); + outb(we_base+WE_P0_IMR, 0); + outb(we_base+WE_P0_ISR, 0xFF); + if (bit16) outb(we_base+WE_LAAR, 1); /* Turn off 16bit mode */ + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1); + for (i=0; i<6; i++) + outb(we_base+WE_P1_PAR0+i, inb(we_base+WE_LAR+i)); + for (i=0; i<6; i++) + outb(we_base+WE_P1_MAR0+i, 0xFF); + outb(we_base+WE_P1_CURR, WE_TXBUF_SIZE+1); + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_STA); + return(1); +} + +/************************************************************************** +ETH_TRANSMIT - Transmit a frame +**************************************************************************/ +eth_transmit(d,t,s,p) + char *d; /* Destination */ + unsigned short t; /* Type */ + unsigned short s; /* size */ + char *p; /* Packet */ +{ + unsigned char c; + bcopy(d, we_bmem, 6); /* Copy destination */ + bcopy(arptable[ARP_CLIENT].node, we_bmem+6, ETHER_ADDR_SIZE); /* My ether addr */ + *(we_bmem+12) = t>>8; /* Type field */ + *(we_bmem+13) = t; + bcopy(p, we_bmem+14, s); + s += 14; + while (s < WE_MIN_PACKET) *(we_bmem+(s++)) = 0; + twiddle(); + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0); + outb(we_base+WE_P0_TPSR, 0); + outb(we_base+WE_P0_TBCR0, s); + outb(we_base+WE_P0_TBCR1, s>>8); + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0 | WE_COMMAND_TXP); + return(0); +} + +/************************************************************************** +ETH_POLL - Wait for a frame +**************************************************************************/ +eth_poll() +{ + int ret = 0; + unsigned short type = 0; + unsigned char bound,curr,rstat; + unsigned short len; + unsigned char *pkt, *p; + rstat = inb(we_base+WE_P0_RSR); + if (rstat & WE_RSTAT_OVER) { + eth_reset(); + return(0); + } + if (!(rstat & WE_RSTAT_PRX)) return(0); + bound = inb(we_base+WE_P0_BOUND)+1; + if (bound == WE_RXBUF_END) bound = WE_TXBUF_SIZE; + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS1); + curr = inb(we_base+WE_P1_CURR); + outb(we_base+WE_P0_COMMAND, WE_COMMAND_PS0); + if (curr == WE_RXBUF_END) curr=WE_TXBUF_SIZE; + if (curr == bound) return(0); + pkt = we_bmem + (bound << 8); + len = *((unsigned short *)(pkt+2)) - 4; /* sub CRC */ + if (len > 1514) len = 1514; +#ifdef DEBUG +printf("[R%dS%dC%dB%dN%d]",len, rstat, curr,bound,*(pkt+1)); +#endif + bound = *(pkt+1); /* New bound ptr */ + p = packet; + if ( (*pkt & WE_RSTAT_PRX) && (len > 14) && (len < 1518)) { + pkt += 4; + packetlen = len; + while (len) { + while (len && (pkt < (we_bmem + 8192))) { + *(p++) = *(pkt++); + len--; + } + pkt = we_bmem + (WE_TXBUF_SIZE << 8); + } + type = (packet[12]<<8) | packet[13]; + ret = 1; + } + if (bound == WE_TXBUF_SIZE) + bound = WE_RXBUF_END; + outb(we_base+WE_P0_BOUND, bound-1); + if (ret && (type == ARP)) { + struct arprequest *arpreq; + unsigned long reqip; + arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE]; + convert_ipaddr(&reqip, arpreq->tipaddr); + if ((ntohs(arpreq->opcode) == ARP_REQUEST) && + (reqip == arptable[ARP_CLIENT].ipaddr)) { + arpreq->opcode = htons(ARP_REPLY); + bcopy(arpreq->sipaddr, arpreq->tipaddr, 4); + bcopy(arpreq->shwaddr, arpreq->thwaddr, 6); + bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6); + convert_ipaddr(arpreq->sipaddr, &reqip); + eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest), + arpreq); + return(0); + } + } + return(ret); +} + |
