aboutsummaryrefslogtreecommitdiff
path: root/sys/i386/netboot
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
committersvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
commita16f65c7d117419bd266c28a1901ef129a337569 (patch)
tree2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /sys/i386/netboot
parent8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff)
Diffstat (limited to 'sys/i386/netboot')
-rw-r--r--sys/i386/netboot/Makefile54
-rw-r--r--sys/i386/netboot/bootmenu.c158
-rw-r--r--sys/i386/netboot/main.c415
-rw-r--r--sys/i386/netboot/makerom.c42
-rw-r--r--sys/i386/netboot/misc.c227
-rw-r--r--sys/i386/netboot/netboot.doc245
-rw-r--r--sys/i386/netboot/netboot.h186
-rw-r--r--sys/i386/netboot/start2.S332
-rw-r--r--sys/i386/netboot/wd80x3.c240
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);
+}
+