diff --git a/cdrom/tbbootconfig/Makefile b/cdrom/tbbootconfig/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f5ea244a520a65ea2aaa42ece05ba4f26f4727c7
--- /dev/null
+++ b/cdrom/tbbootconfig/Makefile
@@ -0,0 +1,7 @@
+CFLAGS += -I. -g
+LDFLAGS += -lz
+
+tbbootconfig: tbbootconfig.c
+
+clean:
+	rm tbbootconfig
diff --git a/cdrom/tbbootconfig/tbbootconfig.c b/cdrom/tbbootconfig/tbbootconfig.c
new file mode 100644
index 0000000000000000000000000000000000000000..abeda5adc94494b4673221167286373478d8bb44
--- /dev/null
+++ b/cdrom/tbbootconfig/tbbootconfig.c
@@ -0,0 +1,513 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2000-2002 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <zlib.h>
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "testbed_boot.h"
+
+/*
+ * Operate on the testbed boot header.
+ */
+static char *usagestr = 
+ "usage: %s <options> devicefile\n"
+ " -f              Force operation\n"
+ " -i              Info mode only; do not write anything\n"
+ " -d              Turn on debugging.\n"
+ " -v              Verify and exit with status; Do not print anything\n"
+ " -p              Purge the header block (write zeros)\n"
+ " -w filename     Write system config to header block from file\n"
+ " -r filename     Read system config from header block and write to file\n"
+ " -k 0,1          Set the bootfromdisk flag to either 0 or 1\n"
+ " -c 0,1          Set the bootfromcdrom flag to either 0 or 1\n"
+ " -m 0,1          Set the validimage flag to either 0 or 1\n"
+ " -e string       Get/Set the emulab key. Use - to get the key\n"
+ " device          The device special file (ie: /dev/rad0)\n";
+
+/* Locals */
+static int	info, debug, force, verify, purge;
+static char    *progname;
+
+/* Forward decls */
+static void	usage(void);
+static int	readhdr(int devfd, tbboot_t *tbhdr);
+static int	writehdr(int devfd, tbboot_t *tbhdr);
+static int	readconfigfromfile(int devfd, tbboot_t *tbhdr, char *path);
+static int	writeconfigtofile(int devfd, tbboot_t *tbhdr, char *path);
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	int		rval, ch, devfd;
+	tbboot_t	tbboot_hdr;
+	int		bootfromcdrom = -1, bootfromdisk = -1, validimage = -1;
+	char	       *configfile = NULL, *emulabkey = NULL;
+	int		readconfig = 0, writeconfig = 0;
+
+	progname = argv[0];
+
+	if (geteuid()) {
+		fprintf(stderr, "You must be root!\n");
+		exit(-1);
+	}
+
+	if (sizeof(tbboot_hdr) > SECSIZE) {
+		warnx("boot header size is too big!\n");
+		exit(-1);
+	}
+	
+	while ((ch = getopt(argc, argv, "fidk:c:vm:r:w:pe:")) != -1)
+		switch(ch) {
+		case 'i':
+			info++;
+			break;
+		case 'd':
+			debug++;
+			break;
+		case 'f':
+			force++;
+			break;
+		case 'v':
+			verify++;
+			break;
+		case 'p':
+			purge++;
+			break;
+		case 'k':
+			bootfromdisk = atoi(optarg);
+			if (bootfromdisk < 0 || bootfromdisk > 1)
+				usage();
+			break;
+		case 'c':
+			bootfromcdrom = atoi(optarg);
+			if (bootfromcdrom < 0 || bootfromcdrom > 1)
+				usage();
+			break;
+		case 'm':
+			validimage = atoi(optarg);
+			if (validimage < 0 || validimage > 1)
+				usage();
+			break;
+		case 'r':
+			configfile  = optarg;
+			readconfig  = 1;
+			break;
+		case 'w':
+			configfile  = optarg;
+			writeconfig = 1;
+			break;
+		case 'e':
+			emulabkey  = optarg;
+			break;
+		case 'h':
+		case '?':
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 1)
+		usage();
+
+	if ((devfd = open(argv[0], (info ? O_RDONLY : O_RDWR))) < 0) {
+		if (! verify) /* Be silent */
+			perror("opening device");
+		exit(1);
+	}
+	rval = readhdr(devfd, &tbboot_hdr);
+	if (verify)
+		exit(rval);
+
+	if (configfile && readconfig)
+		exit(writeconfigtofile(devfd, &tbboot_hdr, configfile));
+
+	if (emulabkey && emulabkey[0] == '-') {
+		if (! strlen(tbboot_hdr.emulabkey))
+			exit(-1);
+		printf("%s\n", tbboot_hdr.emulabkey);
+		exit(0);
+	}
+
+	if (info)
+		exit(rval);
+
+	if (purge) {
+		char	buf[SECSIZE];
+		
+		bzero(buf, sizeof(buf));
+
+		if (pwrite(devfd, buf, sizeof(buf), (off_t)TBBOOT_OFFSET) < 0){
+			perror("purging header");
+			exit(1);
+		}
+		exit(0);
+	}
+
+	if (bootfromcdrom != -1)
+		tbboot_hdr.bootfromcdrom = bootfromcdrom;
+	if (bootfromdisk != -1)
+		tbboot_hdr.bootfromdisk  = bootfromdisk;
+	if (validimage != -1)
+		tbboot_hdr.validimage    = bootfromdisk;
+
+	if (emulabkey) {
+		if (strlen(emulabkey) > TBBOOT_MAXKEYLEN - 1) {
+			warnx("Key is longer that %d chars!",
+			      TBBOOT_MAXKEYLEN - 1);
+			exit(-1);
+		}
+		strcpy(tbboot_hdr.emulabkey, emulabkey);
+	}
+
+	if (tbboot_hdr.bootfromdisk == tbboot_hdr.bootfromcdrom && !force) {
+		warnx("Attempt to set bootfromcdrom=bootfromdisk! "
+		      "Use -f option.\n");
+		exit(-1);
+	}
+
+	if (configfile && writeconfig &&
+	    readconfigfromfile(devfd, &tbboot_hdr, configfile))
+		exit(-1);
+	
+	writehdr(devfd, &tbboot_hdr);
+
+	close(devfd);
+	exit(0);
+}
+
+/*
+ * Read and validate the header. Returns -1 if the header is invalid.
+ */
+static int
+readhdr(int devfd, tbboot_t *tbhdr)
+{
+	char		buf[SECSIZE];
+	unsigned	oldcrc, newcrc;
+	
+	if (lseek(devfd, (off_t) TBBOOT_OFFSET, SEEK_SET) < 0) {
+		perror("seeking to read header");
+		exit(1);
+	}
+
+	if (read(devfd, buf, sizeof(buf)) < 0) {
+		perror("reading header");
+		exit(1);
+	}
+	memcpy(tbhdr, buf, sizeof(*tbhdr));
+
+	if (tbhdr->magic1 != TBBOOT_MAGIC1 || tbhdr->magic2 != TBBOOT_MAGIC2) {
+		if (verify)
+			return -1;
+		
+		warnx("Bad magic number in header!");
+		if (! force)
+			exit(1);
+		tbhdr->magic1  = TBBOOT_MAGIC1;
+		tbhdr->magic2  = TBBOOT_MAGIC2;
+		tbhdr->version = TBBOOT_VERSION;
+		
+		/* No info to print */
+		return -1;
+	}
+
+	if (debug) {
+		fprintf(stderr, "Reading TB Boot Header:\n");
+		fprintf(stderr, "  bootfromdisk:   %d\n",
+			tbhdr->bootfromdisk);
+		fprintf(stderr, "  bootfromcdrom:  %d\n",
+			tbhdr->bootfromcdrom);
+		fprintf(stderr, "  checksum:       %lx\n",
+			tbhdr->checksum);
+		fprintf(stderr, "  emulabkey:      %s\n",
+			tbhdr->emulabkey);
+		fprintf(stderr, "  validimage:     %d\n",
+			tbhdr->validimage);
+		fprintf(stderr, "  validconfig:    %d\n",
+			tbhdr->validconfig);
+
+		if (tbhdr->validconfig) {
+			fprintf(stderr, "    interface:  %s\n",
+			       tbhdr->sysconfig.interface);
+			fprintf(stderr, "    hostname:   %s\n",
+			       tbhdr->sysconfig.hostname);
+			fprintf(stderr, "    domain:     %s\n",
+			       tbhdr->sysconfig.domain);
+			fprintf(stderr, "    IP:         %s\n",
+			       inet_ntoa(tbhdr->sysconfig.IP));
+			fprintf(stderr, "    netmask:    %s\n",
+			       inet_ntoa(tbhdr->sysconfig.netmask));
+			fprintf(stderr, "    nameserver: %s\n",
+			       inet_ntoa(tbhdr->sysconfig.nameserver));
+			fprintf(stderr, "    gateway:    %s\n",
+			       inet_ntoa(tbhdr->sysconfig.gateway));
+		}
+	}
+
+	/*
+	 * Check the checksum.
+	 */
+	oldcrc = tbhdr->checksum;
+	tbhdr->checksum = 0;
+	newcrc = crc32(0L, Z_NULL, 0);
+	newcrc = crc32(newcrc, (const char *) tbhdr, sizeof(*tbhdr));
+
+	if (newcrc != oldcrc) {
+		if (verify)
+			return -1;
+
+		warnx("Bad crc header! %x != %x\n", newcrc, oldcrc);
+		if (! force)
+			exit(1);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Write the header,
+ */
+static int
+writehdr(int devfd, tbboot_t *tbhdr)
+{
+	char		buf[SECSIZE];
+	unsigned long	newcrc;
+
+	if (tbhdr->magic1 != TBBOOT_MAGIC1 || tbhdr->magic2 != TBBOOT_MAGIC2) {
+		warnx("Bad magic number in header!");
+		if (! force)
+			exit(1);
+	}
+	tbhdr->checksum = 0;
+	newcrc = crc32(0L, Z_NULL, 0);
+	newcrc = crc32(newcrc, (const char *) tbhdr, sizeof(*tbhdr));
+	tbhdr->checksum = newcrc;
+	
+	if (debug) {
+		if (info) 
+			fprintf(stderr, "Would write TB Boot Header:\n");
+		else
+			fprintf(stderr, "Writing TB Boot Header:\n");
+		
+		fprintf(stderr, "  bootfromdisk:   %d\n",
+			tbhdr->bootfromdisk);
+		fprintf(stderr, "  bootfromcdrom:  %d\n",
+			tbhdr->bootfromcdrom);
+		fprintf(stderr, "  checksum:       %lx\n",
+			tbhdr->checksum);
+		fprintf(stderr, "  emulabkey:      %s\n",
+			tbhdr->emulabkey);
+		fprintf(stderr, "  validimage:     %d\n",
+			tbhdr->validimage);
+		fprintf(stderr, "  validconfig:    %d\n",
+			tbhdr->validconfig);
+
+		if (tbhdr->validconfig) {
+			fprintf(stderr, "    interface:  %s\n",
+			       tbhdr->sysconfig.interface);
+			fprintf(stderr, "    hostname:   %s\n",
+			       tbhdr->sysconfig.hostname);
+			fprintf(stderr, "    domain:     %s\n",
+			       tbhdr->sysconfig.domain);
+			fprintf(stderr, "    IP:         %s\n",
+			       inet_ntoa(tbhdr->sysconfig.IP));
+			fprintf(stderr, "    netmask:    %s\n",
+			       inet_ntoa(tbhdr->sysconfig.netmask));
+			fprintf(stderr, "    nameserver: %s\n",
+			       inet_ntoa(tbhdr->sysconfig.nameserver));
+			fprintf(stderr, "    gateway:    %s\n",
+			       inet_ntoa(tbhdr->sysconfig.gateway));
+		}
+	}
+	if (info)
+		return 0;
+
+	bzero(buf, sizeof(buf));
+	memcpy(buf, tbhdr, sizeof(*tbhdr));
+
+	if (pwrite(devfd, buf, sizeof(buf), (off_t) TBBOOT_OFFSET) < 0) {
+		perror("writing header");
+		exit(1);
+	}
+	
+	return 0;
+}
+
+#define CONFIG_STRING	1
+#define CONFIG_INADDR	2
+tbboot_t		foohdr;
+
+struct configval {
+	char	*name;
+	int	type;
+	int	size;
+	int	offset;
+	union {
+		char		*string;
+		struct in_addr  in;
+	} value;
+} configvals[] = {
+	{"IP",		CONFIG_INADDR, sizeof(foohdr.sysconfig.IP),
+	 (char *) &foohdr.sysconfig.IP - (char *) &foohdr },
+	{"netmask",	CONFIG_INADDR, sizeof(foohdr.sysconfig.netmask),
+	 (char *) &foohdr.sysconfig.netmask - (char *) &foohdr },
+	{"nameserver",  CONFIG_INADDR, sizeof(foohdr.sysconfig.nameserver),
+	 (char *) &foohdr.sysconfig.nameserver - (char *) &foohdr },
+	{"gateway",     CONFIG_INADDR, sizeof(foohdr.sysconfig.gateway),
+	 (char *) &foohdr.sysconfig.gateway - (char *) &foohdr },
+	{"interface",   CONFIG_STRING, sizeof(foohdr.sysconfig.interface),
+	 (char *) &foohdr.sysconfig.interface - (char *) &foohdr },
+	{"hostname",    CONFIG_STRING, sizeof(foohdr.sysconfig.hostname),
+	 (char *) &foohdr.sysconfig.hostname - (char *) &foohdr },
+	{"domain",      CONFIG_STRING, sizeof(foohdr.sysconfig.domain),
+	 (char *) &foohdr.sysconfig.domain - (char *) &foohdr },
+};
+int maxconfigs = sizeof(configvals)/sizeof(struct configval);
+
+static int
+readconfigfromfile(int devfd, tbboot_t *tbhdr, char *path)
+{
+	FILE	*fp;
+	int	i, badconfig = 0;
+	char	buf[BUFSIZ];
+
+	if ((fp = fopen(path, "r")) == NULL) {
+		perror("opening configfile for read");
+		exit(-1);
+	}
+
+	while (fgets(buf, sizeof(buf), fp)) {
+		char	*name, *value, *bp;
+
+		if (buf[0] == '\n' || buf[0] == '#')
+			continue;
+		
+		if ((bp = rindex(buf, '\n')))
+			*bp = 0;
+
+		if (! (bp = index(buf, '='))) {
+			warnx("Misformed input line: '%s'\n", buf);
+			return -1;
+		}
+		*bp   = NULL;
+		name  = buf;
+		value = bp + 1;
+		
+		if (!strlen(name) || !strlen(value)) {
+			warnx("Misformed input line: '%s:%s'\n", name, value);
+			return -1;
+		}
+		
+		for (i = 0; i < maxconfigs; i++) {
+			if (! strcmp(configvals[i].name, name))
+				break;
+		}
+		if (i == maxconfigs) {
+			warnx("Invalid input line: '%s'\n", buf);
+			return -1;
+		}
+		switch (configvals[i].type) {
+		case CONFIG_STRING:
+			if (strlen(value) > configvals[i].size - 1) {
+				warnx("Value too long!: %s\n", value);
+				return -1;
+			}
+			configvals[i].value.string = strdup(value);
+			break;
+			
+		case CONFIG_INADDR:
+			inet_aton(value, &configvals[i].value.in);
+			break;
+
+		default:
+			warnx("Bad config type: %d\n", configvals[i].type);
+			return -1;
+		}
+	}
+
+	/*
+	 * Check to make sure all the fields were specified. If so, we can
+	 * set the sysconfig to valid and overwrite the current info.
+	 */
+	for (i = 0; i < maxconfigs; i++) {
+		if (! configvals[i].value.string) {
+			warnx("Config is missing: '%s'\n", configvals[i].name);
+			badconfig++;
+		}
+	}
+	if (badconfig) {
+		warnx("Not writing configuration because its incomplete.\n");
+		return -1;
+	}
+
+	/*
+	 * Valid config. Change the boot header.
+	 */
+	for (i = 0; i < maxconfigs; i++) {
+		char	*foo = ((char *) tbhdr) + configvals[i].offset;
+
+		switch (configvals[i].type) {
+		case CONFIG_STRING:
+			strcpy(foo, configvals[i].value.string);
+			break;
+			
+		case CONFIG_INADDR:
+			memcpy(foo, &configvals[i].value,
+			       sizeof(struct in_addr));
+			break;
+		}
+	}
+	tbhdr->validconfig = 1;
+	
+	return 0;
+}
+
+static int
+writeconfigtofile(int devfd, tbboot_t *tbhdr, char *path)
+{
+	FILE	*fp;
+
+	if (!tbhdr->validconfig) {
+		warnx("There is no system configuration in the header block!");
+		return -1;
+	}
+
+	if ((fp = fopen(path, "w")) == NULL) {
+		perror("opening configfile for write");
+		exit(-1);
+	}
+
+	fprintf(fp, "interface=%s\n", tbhdr->sysconfig.interface);
+	fprintf(fp, "hostname=%s\n",  tbhdr->sysconfig.hostname);
+	fprintf(fp, "domain=%s\n",    tbhdr->sysconfig.domain);
+	fprintf(fp, "IP=%s\n",        inet_ntoa(tbhdr->sysconfig.IP));
+	fprintf(fp, "netmask=%s\n",   inet_ntoa(tbhdr->sysconfig.netmask));
+	fprintf(fp, "nameserver=%s\n",inet_ntoa(tbhdr->sysconfig.nameserver));
+	fprintf(fp, "gateway=%s\n",   inet_ntoa(tbhdr->sysconfig.gateway));
+	
+	fclose(fp);
+	return 0;
+}
+
+static void
+usage()
+{
+	fprintf(stderr, usagestr, progname);
+	exit(1);
+}
+
+
diff --git a/cdrom/tbbootconfig/testbed_boot.h b/cdrom/tbbootconfig/testbed_boot.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fa37a15bd7fc42e6591b250cef3beb5c1baf54a
--- /dev/null
+++ b/cdrom/tbbootconfig/testbed_boot.h
@@ -0,0 +1,111 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2000-2002 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
+
+#include <netinet/in.h>
+
+/*
+ * This structure is placed in the boot block. The loader reads the
+ * structure to determine whether it should boot from CDROM or switch
+ * to the disk.
+ *
+ * The basic idea is this:
+ *
+ * 1. The node always boots from the CD-ROM (first in boot chain).
+ * 2. The loader looks for this magic structure:
+ *     2a: If not present continues to load from the CD-ROM (step 4).
+ *     2b: If present check the contents of the structure. If directed to
+ *         boot from the disk goto step 3. If not, continues to load from
+ *         the CDROM (step 4).
+ * 3. Boot from the disk. Switch the boot device to the disk so that the
+ *    kernel is loaded from the disk. Also reset the flag (written to disk)
+ *    to indicate that next reboot should happen from the CDROM. Also set
+ *    another flag, cleared by the kernel, that indicates the kernel booted
+ *    okay. This avoids reboot loops in the case of a scrogged disk.
+ * 4. Boot from the CD-ROM. The user level code on the CD-ROM will take care
+ *    of the rest, writing the proper flags to the structure on the disk.
+ *    Normally, this means checking in with Emulab, and then setting the flag
+ *    to cause it to boot from the disk.
+ *
+ * This entire structure has to be less than a sector.
+ */
+#define SECSIZE			512
+#define TBBOOT_MAXIFACELEN	12
+#define TBBOOT_MAXHOSTLEN	32
+#define TBBOOT_MAXDOMAINLEN	64
+#define TBBOOT_MAXKEYLEN	64
+#define TBBOOT_MAXDISKDEVLEN	32
+
+typedef struct tbboot_header
+{
+	unsigned long		magic1;
+	short			version;
+
+	/*
+	 * Set bootfromdisk to 1 to force loader to boot from disk.
+	 */
+	char			bootfromdisk;
+
+	/*
+	 * Set bootfromcdrom to 0 when booting from the disk. The kernel
+	 * will set this to 1. If the cdrom boots with bootfromdisk 0
+	 * and bootfromcdrom 0, something went wrong and the kernel did not
+	 * boot properly. Avoids a loop.
+	 */
+	char			bootfromcdrom;
+
+	/*
+	 * Flag to indicate the image is valid. Clear this when writing
+	 * a new image, and set it when done.
+	 */
+	char			validimage;
+
+	/*
+	 * Flag to indicate the system config block is valid (has info).
+	 */
+	char			validconfig;
+
+	/*
+	 * crc32 from the zlib library.
+	 */ 
+	unsigned long		checksum;
+
+	/* Paranoia */
+	unsigned long		magic2;
+
+	/*
+	 * The emulab key. 
+	 */
+	char			emulabkey[TBBOOT_MAXKEYLEN];
+
+	/*
+	 * System configuration.
+	 */
+	struct {
+		char		interface[TBBOOT_MAXIFACELEN];
+		char		hostname[TBBOOT_MAXHOSTLEN];
+		char		domain[TBBOOT_MAXDOMAINLEN];
+		struct in_addr	IP;
+		struct in_addr	netmask;
+		struct in_addr	nameserver;
+		struct in_addr	gateway;
+	} sysconfig;
+} tbboot_t;
+
+/* Magic value identifying the header.  */
+#define TBBOOT_MAGIC1		0x9badbeef
+#define TBBOOT_MAGIC2		0x69ceafd8
+
+/* Current Version */
+#define TBBOOT_VERSION		100
+
+/*
+ * Offset from start of the disk. Hardwired to sector 60 which should be
+ * clear on our images.
+ */
+#define TBBOOT_SECTOR		60
+#define TBBOOT_OFFSET		(TBBOOT_SECTOR * SECSIZE)
+
+