changeset 485:a5a03e983d49

Merge branch 'loader'
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Wed, 20 Apr 2011 20:50:32 -0400
parents 440c330d23ab (current diff) 25abe8a2c816 (diff)
children b08396f8a1d0
files cp/Makefile cp/include/list.h cp/include/loader.h cp/ipl/ipl_rdr.S cp/ipl/ipl_tape.S_in cp/ipl/linker.script cp/ipl/loader.c cp/ipl/loader_asm.S cp/ipl/setmode.S cp/scripts/gen_rdr_ccws.sh cp/scripts/gen_tape_ipl_s.sh
diffstat 45 files changed, 3636 insertions(+), 1473 deletions(-) [+]
line wrap: on
line diff
--- a/.gitignore	Sat Feb 26 17:58:57 2011 -0500
+++ b/.gitignore	Wed Apr 20 20:50:32 2011 -0400
@@ -1,3 +1,5 @@
 *.swp
 *.orig
 *.rej
+
+installer.bin
--- a/Makefile	Sat Feb 26 17:58:57 2011 -0500
+++ b/Makefile	Wed Apr 20 20:50:32 2011 -0400
@@ -7,17 +7,33 @@
 #
 
 all:
+	make -C build
 	make -C lib
 	make -C cp
 	make -C nss/8ball
+	make -C loader
+	make -C installer
+	./build/mkarchive \
+		cp/hvf.directory text 80 \
+		cp/hvf bin \
+		loader/eckd.rto bin \
+		loader/loader.rto bin \
+		doc/installed_files.txt text 80 \
+		> installer/archive.cpio
+	./build/padcat installer/rdr.rto installer/loader.rto installer/archive.cpio > installer.bin
 
 doc:
 	make -C doc/manual
 
 clean:
+	make -C build clean
 	make -C lib clean
 	make -C cp clean
 	make -C nss/8ball clean
+	make -C loader clean
+	make -C installer clean
 	make -C doc/manual clean
+	rm -f installer/archive.cpio
+	rm -f installer.bin
 
 .PHONY: all doc clean
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build/.gitignore	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,3 @@
+ccw_gen
+padcat
+mkarchive
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build/Makefile	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,16 @@
+HOSTCC=gcc
+CFLAGS=-Wall -O2 -g
+
+all: ccw_gen padcat mkarchive
+
+clean:
+	rm -f ccw_gen padcat mkarchive
+
+ccw_gen: ccw_gen.c
+	$(HOSTCC) $(CFLAGS) -o $@ $<
+
+padcat: padcat.c
+	$(HOSTCC) $(CFLAGS) -o $@ $<
+
+mkarchive: mkarchive.c
+	$(HOSTCC) $(CFLAGS) -o $@ $<
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build/ccw_gen.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,252 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *deck_head =
+"#\n"
+"# NOTE: This file was automatically generated by " __FILE__ "\n"
+"#\n"
+"# During the system IPL, 24 bytes are read from the device.\n"
+"#\n"
+"# NOTE: zArch IPLs in ESA/390 mode.\n"
+"#\n";
+
+static char *card_head =
+"\n"
+"#######################################################################\n"
+"# CARD %-3d                                                            #\n"
+"#######################################################################\n";
+
+static char *psw_head =
+"#\n"
+"# Bytes 0-7 contain PSW to be loaded after IO operation completes\n"
+"#\n";
+
+static char *psw =
+"	.byte	0x%c%c\n"
+"		#   bits  value   name                        desc\n"
+"		#      0      0   <zero>\n"
+"		#      1      0   PER Mask (R)                disabled\n"
+"		#    2-4      0   <zero>\n"
+"		#      5      0   DAT Mode (T)                disabled\n"
+"		#      6      0   I/O Mask (IO)               disabled\n"
+"		#      7      0   External Mask (EX)          disabled\n"
+"\n"
+"	.byte	0x%c%c\n"
+"		#   bits  value   name                        desc\n"
+"		#   8-11      0   Key\n"
+"		#     12      1   <one>\n"
+"		#     13      0   Machine-Check Mask (M)      disabled\n"
+"		#     14      0   Wait State (W)              executing\n"
+"		#     15      0   Problem State (P)           supervisor state\n"
+"\n"
+"	.byte	0x%c%c\n"
+"		#   bits  value   name                        desc\n"
+"		#  16-17      0   Address-Space Control (AS)  disabled\n"
+"		#  18-19      0   Condition Code (CC)\n"
+"		#  20-23      0   Program Mask                exceptions disabled\n"
+"\n"
+"	.byte	0x%c%c\n"
+"		#   bits  value   name                        desc\n"
+"		#  24-30      0   <zero>\n"
+"		#     31      0   Extended Addressing (EA)    ! 64 mode\n"
+"\n"
+"	.byte	0x%c%c	# bits 32-39\n"
+"	.byte	0x%c%c	# bits 40-47\n"
+"	.byte	0x%c%c	# bits 48-55\n"
+"	.byte	0x%c%c	# bits 56-63\n"
+"		#   bits  value   name                        desc\n"
+"		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24\n"
+"		#  33-63   addr   Instruction Address         Address to exec\n";
+
+static char *psw_tail =
+"\n"
+"#\n"
+"# The remaining 16 bytes should contain CCW to read data from device\n"
+"#\n"
+"\n"
+"# CCW format-0:\n"
+"#   bits  name\n"
+"#    0-7  Cmd Code\n"
+"#   8-31  Data Address\n"
+"#     32  Chain-Data (CD)\n"
+"#     33  Chain-Command (CC)\n"
+"#     34  Sup.-Len.-Inditcation (SLI)\n"
+"#     35  Skip (SKP)\n"
+"#     36  Prog.-Contr.-Inter. (PCI)\n"
+"#     37  Indir.-Data-Addr. (IDA)\n"
+"#     38  Suspend (S)\n"
+"#     39  Modified I.D.A. (MIDA)\n"
+"#  40-47  <ignored>\n"
+"#  48-63  number of bytes to read\n"
+"\n"
+"# CP-CCW 1\n"
+"	# READ 80 bytes of CCWs to 0x%06X\n"
+"	.byte	0x02, 0x%02X, 0x%02X, 0x%02X\n"
+"	.byte	0x%02X, 0x00, 0x00, 0x50\n"
+"\n"
+"# CP-CCW 2\n"
+"	# TIC to 0x%06X\n"
+"	.byte	0x08, 0x%02X, 0x%02X, 0x%02X\n"
+"	.byte	0x00, 0x00, 0x00, 0x00\n";
+
+static char *pad =
+"\n"
+"#\n"
+"# Pad to 80 bytes\n"
+"#\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n"
+"	.byte 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40\n";
+
+static char *cp_ccw =
+"\n"
+"# CP-CCW %d\n"
+"	.byte	0x02, 0x%02X, 0x%02X, 0x%02X\n"
+"	.byte	0x40, 0x00, 0x00, 0x50\n";
+
+#define CHAIN		0x40
+
+static char *data_ccw =
+"\n"
+"# LOADER-CCW %d\n"
+"	.byte	0x02, 0x%02X, 0x%02X, 0x%02X\n"
+"	.byte	0x%02X, 0x00, 0x00, 0x50\n";
+
+static char *nop_ccw =
+"\n"
+"# NOP-CCW %d\n"
+"	.byte	0x03, 0x00, 0x00, 0x08\n"
+"	.byte	0x60, 0x00, 0x00, 0x01\n";
+
+#define ADDR(x)		(((x)[0]<<16) | \
+			 ((x)[1]<<8)  | \
+			 ((x)[2]))
+
+#define ADDRS(x)	(x)[0], (x)[1], (x)[2]
+
+static void die(char *pgm, char *s)
+{
+	fprintf(stderr, "Error: %s\n", s);
+	fprintf(stderr, "Usage: %s <psw word 1> <psw word 2> <file size> "
+		"<address> <channel pgm addr>\n", pgm);
+	exit(1);
+}
+
+static unsigned int unhex(char *pgm, char c)
+{
+	if ((c >= '0') && (c <= '9'))
+		return c - '0';
+	if ((c >= 'A') && (c <= 'F'))
+		return c - 'A' + 10;
+	if ((c >= 'a') && (c <= 'f'))
+		return c - 'a' + 10;
+	die(pgm, "Argument not hexadecimal");
+	return 0;
+}
+
+static void __add(unsigned int *addr, int a)
+{
+	int i;
+
+	addr[2] += a;
+	for(i=2; i>=0; i--) {
+		if (addr[i] > 255) {
+			addr[i-1] += addr[i] / 256;
+			addr[i] %= 256;
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int i = 1, j;
+	unsigned int card;
+	unsigned int ccw_addr[3];
+	unsigned int addr[3];
+	unsigned int size;
+	unsigned int data_cards;
+
+	if (argc != 6)
+		die(argv[0], "Invalid number of args");
+
+	if ((strlen(argv[1]) != 8) ||
+	    (strlen(argv[2]) != 8))
+		die(argv[0], "Invalid PSW argument length");
+
+	size = atol(argv[3]);
+	if (!size)
+		die(argv[0], "Invalid file size");
+
+	if ((strlen(argv[4]) != 6) ||
+	    (strlen(argv[5]) != 6))
+		die(argv[0], "Invalid addr argument length");
+
+	addr[0] = (unhex(argv[0], argv[4][0]) << 4) |
+		   unhex(argv[0], argv[4][1]);
+	addr[1] = (unhex(argv[0], argv[4][2]) << 4) |
+		   unhex(argv[0], argv[4][3]);
+	addr[2] = (unhex(argv[0], argv[4][4]) << 4) |
+		   unhex(argv[0], argv[4][5]);
+
+	ccw_addr[0] = (unhex(argv[0], argv[5][0]) << 4) |
+		       unhex(argv[0], argv[5][1]);
+	ccw_addr[1] = (unhex(argv[0], argv[5][2]) << 4) |
+		       unhex(argv[0], argv[5][3]);
+	ccw_addr[2] = (unhex(argv[0], argv[5][4]) << 4) |
+		       unhex(argv[0], argv[5][5]);
+
+	/* round up */
+	if (size % 80)
+		size = size - (size % 80) + 80;
+
+	data_cards = size / 80;
+
+	printf(deck_head);
+
+	printf(card_head, i);
+	printf(psw_head);
+	printf(psw,
+	       argv[1][0], argv[1][1], argv[1][2], argv[1][3],
+	       argv[1][4], argv[1][5], argv[1][6], argv[1][7],
+	       argv[2][0], argv[2][1], argv[2][2], argv[2][3],
+	       argv[2][4], argv[2][5], argv[2][6], argv[2][7]);
+
+	if (data_cards == 1)
+		die(argv[0], "Cannot handle the case where size <= 80");
+
+	card = 2;
+	i = 0;
+	j = 1;
+
+	printf(psw_tail, ADDR(ccw_addr), ADDRS(ccw_addr), CHAIN,
+	       ADDR(ccw_addr), ADDRS(ccw_addr));
+	printf(pad);
+	__add(ccw_addr, 80);
+
+	for(i=0; i<=((data_cards+9)/10) + (((data_cards+9)/10)+9)/10 - 2; i++) {
+		printf(cp_ccw, i+3, ADDRS(ccw_addr));
+		__add(ccw_addr, 80);
+	}
+
+	while (i % 10) {
+		printf(nop_ccw, i);
+		i++;
+	}
+
+	for(i=1; i<=data_cards; i++) {
+		printf(data_ccw, i, ADDRS(addr),
+		       (i == data_cards) ? 0 : CHAIN);
+
+		__add(addr, 80);
+	}
+
+	return 0;
+
+	printf(card_head, card++);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build/mkarchive.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2011 Josef 'Jeff' Sipek
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <libgen.h>
+
+typedef unsigned char u8;
+
+struct cpio_hdr {
+	u8	magic[6];
+	u8	dev[6];
+	u8	ino[6];
+	u8	mode[6];
+	u8	uid[6];
+	u8	gid[6];
+	u8	nlink[6];
+	u8	rdev[6];
+	u8	mtime[11];
+	u8	namesize[6];
+	u8	filesize[11];
+	u8	data[0];
+};
+
+struct buffer {
+	struct buffer *next;
+	int	len;
+	u8	data[0];
+};
+
+static void storeoct(u8 *buf, int len, unsigned val)
+{
+	len--;
+
+	while(val) {
+		buf[len] = '0' + (val % 8);
+		val /= 8;
+		len--;
+	}
+}
+
+static void mkhdr(struct cpio_hdr *hdr, char *fn, unsigned filesize)
+{
+	memset(hdr, '0', sizeof(struct cpio_hdr));
+
+	hdr->magic[1] = '7';
+	hdr->magic[3] = '7';
+	hdr->magic[5] = '7';
+
+	hdr->nlink[5] = '1';
+
+	storeoct(hdr->namesize, 6, strlen(fn)+1);
+	storeoct(hdr->filesize, 11, filesize);
+}
+
+static unsigned __text(char *fn, int lrecl)
+{
+	struct cpio_hdr hdr;
+	struct buffer *first, *last, *cur, *tmp;
+	FILE *f;
+	int l;
+	unsigned flen;
+
+	if (!lrecl) {
+		fprintf(stderr, "Error: zero lrecl is bogus\n");
+		exit(1);
+	}
+
+	f = fopen(fn, "rb");
+	if (!f) {
+		fprintf(stderr, "Error: could not open file '%s'\n",
+			fn);
+		exit(1);
+	}
+
+	flen  = 0;
+	first = NULL;
+	last  = NULL;
+	for(;;) {
+		cur = malloc(sizeof(struct buffer) + lrecl + 1);
+		if (!cur) {
+			fprintf(stderr, "Error: OOM\n");
+			exit(2);
+		}
+
+		if (!fgets((char*)cur->data, lrecl+1, f))
+			break;
+
+		l = strlen((char*)cur->data);
+
+		if (l && cur->data[l-1])
+			l--;
+
+		assert(l <= lrecl);
+
+		memset(cur->data+l, ' ', lrecl-l);
+
+		cur->next = NULL;
+		if (!first)
+			first = cur;
+		if (last)
+			last->next = cur;
+		last = cur;
+
+		flen += lrecl;
+	}
+
+	fclose(f);
+
+	mkhdr(&hdr, basename(fn), flen);
+	fwrite(&hdr, sizeof(hdr), 1, stdout);
+	fwrite(basename(fn), strlen(basename(fn))+1, 1, stdout);
+
+	for(cur=first; cur; cur=tmp) {
+		fwrite(cur->data, 1, lrecl, stdout);
+		tmp = cur->next;
+		free(cur);
+	}
+
+	return flen + strlen(basename(fn)) + 1 + sizeof(hdr);
+}
+
+#define BUF_LEN 4096
+static unsigned __bin(char *fn)
+{
+	struct cpio_hdr hdr;
+	struct buffer *first, *last, *cur, *tmp;
+	FILE *f;
+	unsigned flen;
+	size_t s;
+
+	f = fopen(fn, "rb");
+	if (!f) {
+		fprintf(stderr, "Error: could not open file '%s'\n",
+			fn);
+		exit(1);
+	}
+
+	flen  = 0;
+	first = NULL;
+	last  = NULL;
+	for(;;) {
+		cur = malloc(sizeof(struct buffer) + BUF_LEN);
+		if (!cur) {
+			fprintf(stderr, "Error: OOM\n");
+			exit(2);
+		}
+
+		memset(cur->data, 0, BUF_LEN);
+		if (!(s = fread(cur->data, 1, BUF_LEN, f)))
+			break;
+
+		cur->next = NULL;
+		cur->len  = s;
+		if (!first)
+			first = cur;
+		if (last)
+			last->next = cur;
+		last = cur;
+
+		flen += s;
+	}
+
+	fclose(f);
+
+	mkhdr(&hdr, basename(fn), flen);
+	fwrite(&hdr, sizeof(hdr), 1, stdout);
+	fwrite(basename(fn), strlen(basename(fn))+1, 1, stdout);
+
+	for(cur=first; cur; cur=tmp) {
+		fwrite(cur->data, 1, cur->len, stdout);
+		tmp = cur->next;
+		free(cur);
+	}
+
+	return flen + strlen(basename(fn)) + 1 + sizeof(hdr);
+}
+
+static void mktail(unsigned len)
+{
+	struct cpio_hdr hdr;
+
+	mkhdr(&hdr, "TRAILER!!!", 0);
+	fwrite(&hdr, sizeof(hdr), 1, stdout);
+	printf("TRAILER!!!");
+
+	while(len % 512) {
+		fwrite("", 1, 1, stdout); // write a nul char
+		len++;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int lrecl;
+	char *fn;
+	int i;
+	unsigned len;
+
+	i=1;
+	len=0;
+	while((i+1) < argc) {
+		fn = argv[i];
+		i++;
+
+		if (!strcmp(argv[i], "text")) {
+			lrecl = atoi(argv[i+1]);
+			i += 2;
+
+			len += __text(fn, lrecl);
+		} else if (!strcmp(argv[i], "bin")) {
+			len += __bin(fn);
+			i++;
+		} else {
+			fprintf(stderr, "Error: cannot understand type '%s'\n",
+				argv[i]);
+			return 1;
+		}
+	}
+
+	mktail(len);
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build/padcat.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+void cat(char *fn)
+{
+	FILE *f;
+	char buf[80];
+	size_t n;
+
+	f = fopen(fn, "rb");
+	if (!f) {
+		fprintf(stderr, "Could not open file \"%s\".\n",
+			fn);
+		exit(1);
+	}
+
+	do {
+		memset(buf, 0, 80);
+		n = fread(buf, 1, 80, f);
+		if (n)
+			assert(fwrite(buf, 1, 80, stdout) == 80);
+	} while(n == 80);
+
+	fclose(f);
+}
+
+int main(int argc, char **argv)
+{
+	int i;
+
+	for(i=1; i<argc; i++)
+		cat(argv[i]);
+
+	return 0;
+}
--- a/cp/Makefile	Sat Feb 26 17:58:57 2011 -0500
+++ b/cp/Makefile	Wed Apr 20 20:50:32 2011 -0400
@@ -32,13 +32,12 @@
 TOP_DIRS=nucleus/ mm/ fs/ drivers/ shell/
 
 .PHONY: all build clean mrproper cleanup tags
-.PHONY: ipl/ $(TOP_DIRS)
+.PHONY: $(TOP_DIRS)
 
 include scripts/Makefile.commands
 
 all: build hvf
 	@echo "Image is `stat -c %s hvf` bytes"
-	@$(MAKE) ipl/ V=$V
 
 hvf: $(patsubst %/,%/built-in.o,$(TOP_DIRS))
 	$(call link-hvf,$^ $(patsubst %,../lib/%.a,$(LIBS)),$@)
@@ -53,7 +52,6 @@
 	@$(MAKE) DIR=drivers/ cleanup V=$V
 	@$(MAKE) DIR=shell/ cleanup V=$V
 	$(call clean,hvf)
-	$(call clean,loader_*.bin ipl/*.o ipl/*.rto ipl/.*.o ipl/ipl_tape.S ipl/ipl_rdr_ccws.S)
 	$(call clean,shell/directory_structs.c)
 	$(call rclean,doc/commands)
 
@@ -75,48 +73,3 @@
 # Include Makefiles from all the top level directories
 #
 include $(patsubst %/,%/Makefile,$(TOP_DIRS))
-
-#
-# IPL specific bits
-#
-
-.PRECIOUS: ipl/%.o
-
-ipl/: loader_rdr.bin loader_tape.bin
-	@echo -n
-
-loader_rdr.bin: ipl/ipl_rdr.rto ipl/ipl_rdr_ccws.rto ipl/setmode.rto ipl/loader_rdr.rto
-	$(call concat,$^,$@)
-	$(call pad,$@,80)
-	@echo "Card reader loader is `stat -c %s $@` bytes"
-
-loader_tape.bin: ipl/ipl_tape.rto ipl/setmode.rto ipl/loader_tape.rto
-	$(call concat,$^,$@)
-	@echo "Tape loader is `stat -c %s $@` bytes"
-
-ipl/loader_asm.o: ipl/loader_asm.S
-	$(call s-to-o,$<,$@)
-
-ipl/ipl_tape.S: ipl/ipl_tape.S_in ipl/setmode.rto ipl/loader_tape.rto scripts/gen_tape_ipl_s.sh
-	$(call genipl-tape,ipl/ipl_tape.S_in,$@)
-
-ipl/ipl_rdr_ccws.S: ipl/setmode.rto ipl/loader_rdr.rto
-	$(call genipl-rdr,$@)
-
-ipl/loader_%.rto: ipl/loader_%.o
-	$(call objcopy-tdr,$<,$@)
-
-ipl/loader_tape.o: ipl/loader.c ipl/loader_asm.o
-	$(call c-to-o-ipl,ipl/loader.c,ipl/loader_c_tape.o,4096,-DTAPE_SEEK)
-	$(call link-ipl,ipl/loader_c_tape.o ipl/loader_asm.o,$@)
-
-ipl/loader_rdr.o: ipl/loader.c ipl/loader_asm.o
-	$(call c-to-o-ipl,ipl/loader.c,ipl/loader_c_rdr.o,80,)
-	$(call link-ipl,ipl/loader_c_rdr.o ipl/loader_asm.o,$@)
-
-ipl/%.rto: ipl/%.o
-	$(call objcopy-t,$<,$@)
-
-ipl/%.o: ipl/%.S
-	$(call s-to-o,$<,$@)
-
--- a/cp/include/list.h	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,421 +0,0 @@
-/*
- * Based on list.h from Linux Kernel
- */
-
-#ifndef _LINUX_LIST_H
-#define _LINUX_LIST_H
-
-#define LIST_POISON1	((void*) 0x8585858585858585ULL)
-#define LIST_POISON2	((void*) 0x8686868686868686ULL)
-
-/*
- * Simple doubly linked list implementation.
- *
- * Some of the internal functions ("__xxx") are useful when
- * manipulating whole lists rather than single entries, as
- * sometimes we already know the next/prev entries and we can
- * generate better code by using them directly rather than
- * using the generic single-entry routines.
- */
-
-struct list_head {
-	struct list_head *next, *prev;
-};
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
-	struct list_head name = LIST_HEAD_INIT(name)
-
-static inline void INIT_LIST_HEAD(struct list_head *list)
-{
-	list->next = list;
-	list->prev = list;
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *new,
-			      struct list_head *prev,
-			      struct list_head *next)
-{
-	next->prev = new;
-	new->next = next;
-	new->prev = prev;
-	prev->next = new;
-}
-
-/**
- * list_add - add a new entry
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- */
-static inline void list_add(struct list_head *new, struct list_head *head)
-{
-	__list_add(new, head, head->next);
-}
-
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
-{
-	__list_add(new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
-	next->prev = prev;
-	prev->next = next;
-}
-
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty() on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-	entry->next = LIST_POISON1;
-	entry->prev = LIST_POISON2;
-}
-
-/**
- * list_replace - replace old entry by new one
- * @old : the element to be replaced
- * @new : the new element to insert
- *
- * If @old was empty, it will be overwritten.
- */
-static inline void list_replace(struct list_head *old,
-				struct list_head *new)
-{
-	new->next = old->next;
-	new->next->prev = new;
-	new->prev = old->prev;
-	new->prev->next = new;
-}
-
-static inline void list_replace_init(struct list_head *old,
-					struct list_head *new)
-{
-	list_replace(old, new);
-	INIT_LIST_HEAD(old);
-}
-
-/**
- * list_del_init - deletes entry from list and reinitialize it.
- * @entry: the element to delete from the list.
- */
-static inline void list_del_init(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-	INIT_LIST_HEAD(entry);
-}
-
-/**
- * list_move - delete from one list and add as another's head
- * @list: the entry to move
- * @head: the head that will precede our entry
- */
-static inline void list_move(struct list_head *list, struct list_head *head)
-{
-	__list_del(list->prev, list->next);
-	list_add(list, head);
-}
-
-/**
- * list_move_tail - delete from one list and add as another's tail
- * @list: the entry to move
- * @head: the head that will follow our entry
- */
-static inline void list_move_tail(struct list_head *list,
-				  struct list_head *head)
-{
-	__list_del(list->prev, list->next);
-	list_add_tail(list, head);
-}
-
-/**
- * list_is_last - tests whether @list is the last entry in list @head
- * @list: the entry to test
- * @head: the head of the list
- */
-static inline int list_is_last(const struct list_head *list,
-				const struct list_head *head)
-{
-	return list->next == head;
-}
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(const struct list_head *head)
-{
-	return head->next == head;
-}
-
-/**
- * list_empty_careful - tests whether a list is empty and not being modified
- * @head: the list to test
- *
- * Description:
- * tests whether a list is empty _and_ checks that no other CPU might be
- * in the process of modifying either member (next or prev)
- *
- * NOTE: using list_empty_careful() without synchronization
- * can only be safe if the only activity that can happen
- * to the list entry is list_del_init(). Eg. it cannot be used
- * if another CPU could re-list_add() it.
- */
-static inline int list_empty_careful(const struct list_head *head)
-{
-	struct list_head *next = head->next;
-	return (next == head) && (next == head->prev);
-}
-
-static inline void __list_splice(struct list_head *list,
-				 struct list_head *head)
-{
-	struct list_head *first = list->next;
-	struct list_head *last = list->prev;
-	struct list_head *at = head->next;
-
-	first->prev = head;
-	head->next = first;
-
-	last->next = at;
-	at->prev = last;
-}
-
-/**
- * list_splice - join two lists
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- */
-static inline void list_splice(struct list_head *list, struct list_head *head)
-{
-	if (!list_empty(list))
-		__list_splice(list, head);
-}
-
-/**
- * list_splice_init - join two lists and reinitialise the emptied list.
- * @list: the new list to add.
- * @head: the place to add it in the first list.
- *
- * The list at @list is reinitialised
- */
-static inline void list_splice_init(struct list_head *list,
-				    struct list_head *head)
-{
-	if (!list_empty(list)) {
-		__list_splice(list, head);
-		INIT_LIST_HEAD(list);
-	}
-}
-
-/**
- * list_entry - get the struct for this entry
- * @ptr:	the &struct list_head pointer.
- * @type:	the type of the struct this is embedded in.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_entry(ptr, type, member) \
-	container_of(ptr, type, member)
-
-/**
- * list_first_entry - get the first element from a list
- * @ptr:	the list head to take the element from.
- * @type:	the type of the struct this is embedded in.
- * @member:	the name of the list_struct within the struct.
- *
- * Note, that list is expected to be not empty.
- */
-#define list_first_entry(ptr, type, member) \
-	list_entry((ptr)->next, type, member)
-
-/**
- * list_for_each	-	iterate over a list
- * @pos:	the &struct list_head to use as a loop cursor.
- * @head:	the head for your list.
- */
-#define list_for_each(pos, head) \
-	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
-		pos = pos->next)
-
-/**
- * __list_for_each	-	iterate over a list
- * @pos:	the &struct list_head to use as a loop cursor.
- * @head:	the head for your list.
- *
- * This variant differs from list_for_each() in that it's the
- * simplest possible list iteration code, no prefetching is done.
- * Use this for code that knows the list to be very short (empty
- * or 1 entry) most of the time.
- */
-#define __list_for_each(pos, head) \
-	for (pos = (head)->next; pos != (head); pos = pos->next)
-
-/**
- * list_for_each_prev	-	iterate over a list backwards
- * @pos:	the &struct list_head to use as a loop cursor.
- * @head:	the head for your list.
- */
-#define list_for_each_prev(pos, head) \
-	for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
-		pos = pos->prev)
-
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos:	the &struct list_head to use as a loop cursor.
- * @n:		another &struct list_head to use as temporary storage
- * @head:	the head for your list.
- */
-#define list_for_each_safe(pos, n, head) \
-	for (pos = (head)->next, n = pos->next; pos != (head); \
-		pos = n, n = pos->next)
-
-/**
- * list_for_each_entry	-	iterate over list of given type
- * @pos:	the type * to use as a loop cursor.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_for_each_entry(pos, head, member)				\
-	for (pos = list_entry((head)->next, typeof(*pos), member);	\
-	     &pos->member != (head);					\
-	     pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_reverse - iterate backwards over list of given type.
- * @pos:	the type * to use as a loop cursor.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_for_each_entry_reverse(pos, head, member)			\
-	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
-	     prefetch(pos->member.prev), &pos->member != (head);	\
-	     pos = list_entry(pos->member.prev, typeof(*pos), member))
-
-/**
- * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
- * @pos:	the type * to use as a start point
- * @head:	the head of the list
- * @member:	the name of the list_struct within the struct.
- *
- * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
- */
-#define list_prepare_entry(pos, head, member) \
-	((pos) ? : list_entry(head, typeof(*pos), member))
-
-/**
- * list_for_each_entry_continue - continue iteration over list of given type
- * @pos:	the type * to use as a loop cursor.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * Continue to iterate over list of given type, continuing after
- * the current position.
- */
-#define list_for_each_entry_continue(pos, head, member)			\
-	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
-	     prefetch(pos->member.next), &pos->member != (head);	\
-	     pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_from - iterate over list of given type from the current point
- * @pos:	the type * to use as a loop cursor.
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * Iterate over list of given type, continuing from current position.
- */
-#define list_for_each_entry_from(pos, head, member)			\
-	for (; prefetch(pos->member.next), &pos->member != (head);	\
-	     pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos:	the type * to use as a loop cursor.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member)			\
-	for (pos = list_entry((head)->next, typeof(*pos), member),	\
-		n = list_entry(pos->member.next, typeof(*pos), member);	\
-	     &pos->member != (head);					\
-	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_continue
- * @pos:	the type * to use as a loop cursor.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * Iterate over list of given type, continuing after current point,
- * safe against removal of list entry.
- */
-#define list_for_each_entry_safe_continue(pos, n, head, member)			\
-	for (pos = list_entry(pos->member.next, typeof(*pos), member),		\
-		n = list_entry(pos->member.next, typeof(*pos), member);		\
-	     &pos->member != (head);						\
-	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_from
- * @pos:	the type * to use as a loop cursor.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * Iterate over list of given type from current point, safe against
- * removal of list entry.
- */
-#define list_for_each_entry_safe_from(pos, n, head, member)			\
-	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
-	     &pos->member != (head);						\
-	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_for_each_entry_safe_reverse
- * @pos:	the type * to use as a loop cursor.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_struct within the struct.
- *
- * Iterate backwards over list of given type, safe against removal
- * of list entry.
- */
-#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
-	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
-		n = list_entry(pos->member.prev, typeof(*pos), member);	\
-	     &pos->member != (head);					\
-	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
-
-#endif
--- a/cp/include/loader.h	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-#ifndef __LOADER_H
-#define __LOADER_H
-
-#define TEMP_BASE	((unsigned char*) 0x400000) /* 4MB */
-
-extern unsigned char ORB[32];
-
-#define memcpy(d,s,l)	__builtin_memcpy((d), (s), (l))
-#define memset(s,c,n)	__builtin_memset((s),(c),(n))
-
-/*
- * It is easier to write this thing in assembly...
- */
-extern int __do_io();
-extern void PGMHANDLER();
-
-extern void load_nucleus(void);
-
-#endif
--- a/cp/ipl/ipl_rdr.S	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-# During the system IPL, 24 bytes are read from the device.
-#
-# NOTE: zArch IPLs in ESA/390 mode.
-#
-
-#
-# Bytes 0-7 contain PSW to be loaded after IO operation completes
-#
-	.byte	0x00
-		#   bits  value   name                        desc
-		#      0      0   <zero>
-		#      1      0   PER Mask (R)                disabled
-		#    2-4      0   <zero>
-		#      5      0   DAT Mode (T)                disabled
-		#      6      0   I/O Mask (IO)               disabled
-		#      7      0   External Mask (EX)          disabled
-
-	.byte	0x08
-		#   bits  value   name                        desc
-		#   8-11      0   Key
-		#     12      1   <one>
-		#     13      0   Machine-Check Mask (M)      disabled
-		#     14      0   Wait State (W)              executing
-		#     15      0   Problem State (P)           supervisor state
-		
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  16-17      0   Address-Space Control (AS)  disabled
-		#  18-19      0   Condition Code (CC)
-		#  20-23      0   Program Mask                exceptions disabled
-
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  24-30      0   <zero>
-		#     31      0   Extended Addressing (EA)    ! 64 mode
-
-	.byte	0x80	# bits 32-39
-	.byte	0x80	# bits 40-47
-	.byte	0x00	# bits 48-55
-	.byte	0x00	# bits 56-63
-		#   bits  value   name                        desc
-		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
-		#  33-63   addr   Instruction Address         Address to exec
-
-#
-# The remaining 16 bytes should contain CCW to read data from device
-#
-
-# CCW format-0:
-#   bits  name
-#    0-7  Cmd Code
-#   8-31  Data Address
-#     32  Chain-Data (CD)
-#     33  Chain-Command (CC)
-#     34  Sup.-Len.-Inditcation (SLI)
-#     35  Skip (SKP)
-#     36  Prog.-Contr.-Inter. (PCI)
-#     37  Indir.-Data-Addr. (IDA)
-#     38  Suspend (S)
-#     39  Modified I.D.A. (MIDA)
-#  40-47  <ignored>
-#  48-63  number of bytes to read
-
-#
-# CCW 1 (bytes 8-15): format-0
-#
-	# read 80 bytes to 0x18
-	.byte	0x02, 0x00, 0x00, 0x18
-	.byte	0x60, 0x00, 0x00, 0x50
-
-#
-# CCW 2 (bytes 16-23): format-0
-#
-	# read 24 bytes to 0x68
-	.byte	0x02, 0x00, 0x00, 0x68
-	.byte	0x60, 0x00, 0x00, 0x18
-
-#
-# Pad to fill up a whole card
-#
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 24-31
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 32-39
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 40-47
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 48-55
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 56-63
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 64-71
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 72-79
-
--- a/cp/ipl/ipl_tape.S_in	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-# During the system IPL, 24 bytes are read from the device.
-#
-# NOTE: zArch IPLs in ESA/390 mode.
-#
-
-#
-# Bytes 0-7 contain PSW to be loaded after IO operation completes
-#
-	.byte	0x00
-		#   bits  value   name                        desc
-		#      0      0   <zero>
-		#      1      0   PER Mask (R)                disabled
-		#    2-4      0   <zero>
-		#      5      0   DAT Mode (T)                disabled
-		#      6      0   I/O Mask (IO)               disabled
-		#      7      0   External Mask (EX)          disabled
-
-	.byte	0x08
-		#   bits  value   name                        desc
-		#   8-11      0   Key
-		#     12      1   <one>
-		#     13      0   Machine-Check Mask (M)      disabled
-		#     14      0   Wait State (W)              executing
-		#     15      0   Problem State (P)           supervisor state
-		
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  16-17      0   Address-Space Control (AS)  disabled
-		#  18-19      0   Condition Code (CC)
-		#  20-23      0   Program Mask                exceptions disabled
-
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  24-30      0   <zero>
-		#     31      0   Extended Addressing (EA)    ! 64 mode
-
-	.byte	0x80	# bits 32-39
-	.byte	0x80	# bits 40-47
-	.byte	0x00	# bits 48-55
-	.byte	0x00	# bits 56-63
-		#   bits  value   name                        desc
-		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
-		#  33-63   addr   Instruction Address         Address to exec
-
-#
-# The remaining 16 bytes should contain CCW to read data from device
-#
-
-#
-# The following is a little bit subtle...
-#
-#    CCW 1     loads (( CCW1H << 16 ) + CCW1L ) bytes to 0x800000 (command chain)
-#    CCW 2     loads (( CCW2H << 16 ) + CCW2L ) bytes to
-#    		   0x800000 + (( CCW1H << 16 ) + CCW1L ) (no command chain)
-#
-
-#
-# CCW 1 (bytes 8-15): format-0
-#
-	.byte	0x27
-		#   bits  value   name                        desc
-		#    0-7      2   Cmd Code                    ?, modifiers
-
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-		#   bits  value   name                        desc
-		#   8-31   addr   Data Address                dest of the read
-
-	.byte	0x60
-		#   bits  value   name                        desc
-		#     32      0   Chain-Data (CD)             don't chain
-		#     33      1   Chain-Command (CC)          read more
-		#     34      1   Sup.-Len.-Inditcation (SLI) suppress
-		#     35      0   Skip (SKP)                  issue read
-		#     36      0   Prog.-Contr.-Inter. (PCI)   don't interrupt
-		#     37      0   Indir.-Data-Addr. (IDA)     real addr
-		#     38      0   Suspend (S)                 don't suspend
-		#     39      0   Modified I.D.A. (MIDA)      real addr
-
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  40-47      0   <ignored>
-
-	.byte	0x00
-	.byte	0x01
-		#   bits  value   name                        desc
-		#  48-63    len   number of bytes to read
-
-#
-# CCW 2 (bytes 16-23): format-0
-#
-	.byte	0x02
-		#   bits  value   name                        desc
-		#    0-7      2   Cmd Code                    read, no modifiers
-
-	.byte	0x7f
-	.byte	0xff	
-	.byte	0xb0
-		#   bits  value   name                        desc
-		#   8-31   addr   Data Address                dest of the read
-
-	.byte	0x20
-		#   bits  value   name                        desc
-		#     32      0   Chain-Data (CD)             don't chain
-		#     33      0   Chain-Command (CC)          don't chain
-		#     34      1   Sup.-Len.-Inditcation (SLI) suppress
-		#     35      0   Skip (SKP)                  issue read
-		#     36      0   Prog.-Contr.-Inter. (PCI)   don't interrupt
-		#     37      0   Indir.-Data-Addr. (IDA)     real addr
-		#     38      0   Suspend (S)                 don't suspend
-		#     39      0   Modified I.D.A. (MIDA)      real addr
-
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  40-47      0   <ignored>
-
-	.byte	CCW2H
-	.byte	CCW2L
-		#   bits  value   name                        desc
-		#  48-63    len   number of bytes to read
-
-#
-# For whatever reason, it would seem that we need to pad until byte 0x50 = 80
-#
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 24-31
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 32-39
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 40-47
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 48-55
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 56-63
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 64-71
-	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 # bytes 72-79
-
--- a/cp/ipl/linker.script	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-SECTIONS
-{
-  ENTRY(load_nucleus)
-  . = 0x800020;
-  .text : { *(.text) }
-  .data : { *(.data) }
-  .rodata : { *(.rodata) }
-  .bss : { *(.bss) }
-}
--- a/cp/ipl/loader.c	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,352 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-#ifndef BLOCK_SIZE
-#error missing BLOCK_SIZE
-#endif
-
-#include <loader.h>
-#include <binfmt_elf.h>
-
-static unsigned char seek_ccw[8] __attribute__ ((aligned (8))) = {
-	/*
-	 * CCW1; seek to the right place
-	 */
-
-	0x3F,
-	/*   bits  value   name                        desc             */
-	/*    0-7      2   Cmd Code                    ?, modifiers     */
-
-	0x00,0x00,0x00,
-	/*   bits  value   name                        desc             */
-	/*   8-31   addr   Data Address                dest of the read */
-
-	0x20,
-	/*   bits  value   name                        desc             */
-	/*     32      0   Chain-Data (CD)             don't chain      */
-	/*     33      0   Chain-Command (CC)          don't chain      */
-	/*     34      1   Sup.-Len.-Inditcation (SLI) suppress         */
-	/*     35      0   Skip (SKP)                  issue read       */
-	/*     36      0   Prog.-Contr.-Inter. (PCI)   don't interrupt  */
-	/*     37      0   Indir.-Data-Addr. (IDA)     real addr        */
-	/*     38      0   Suspend (S)                 don't suspend    */
-	/*     39      0   Modified I.D.A. (MIDA)      real addr        */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*  40-47      0   <ignored>                                    */
-
-	0x00,0x01,
-	/*   bits  value   name                        desc             */
-	/*  48-63    len   number of bytes to read                      */
-};
-
-static unsigned char read_ccw[8] __attribute__ ((aligned (8))) = {
-	/*
-	 * CCW2; read the entire nucleus ELF
-	 */
-
-	0x02,
-	/*   bits  value   name                        desc             */
-	/*    0-7      2   Cmd Code                    read, no modifiers*/
-
-	0xff, 0xff, 0xff,
-	/*   bits  value   name                        desc             */
-	/*   8-31   addr   Data Address                dest of the read */
-
-	0x20,
-	/*   bits  value   name                        desc             */
-	/*     32      0   Chain-Data (CD)             don't chain      */
-	/*     33      0   Chain-Command (CC)          don't chain      */
-	/*     34      1   Sup.-Len.-Inditcation (SLI) suppress         */
-	/*     35      0   Skip (SKP)                  issue read       */
-	/*     36      0   Prog.-Contr.-Inter. (PCI)   don't interrupt  */
-	/*     37      0   Indir.-Data-Addr. (IDA)     real addr        */
-	/*     38      0   Suspend (S)                 don't suspend    */
-	/*     39      0   Modified I.D.A. (MIDA)      real addr        */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*  40-47      0   <ignored>                                    */
-
-	0xff, 0xff,
-	/*   bits  value   name                        desc             */
-	/*  48-63    len   number of bytes to read                      */
-};
-
-unsigned char ORB[32] __attribute__ ((aligned (16))) = {
-	/* Word 0 */
-	0x12,0x34,0x56,0x78,
-	/*   bits  value   name                        desc             */
-	/*   0-31  magic   Interrupt Parameter                          */
-
-	/* Word 1 */
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*    0-3      0   Subchannel Key                               */
-	/*      4      0   Suspend Control                              */
-	/*      5      0   Streaming-Mode Control                       */
-	/*      6      0   Modification Control                         */
-	/*      7      0   Synchronization Control                      */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*      8      0   Format Control              format-0 CCWs    */
-	/*      9      0   Prefetch Control                             */
-	/*     10      0   Initial-Status-Interruption Control          */
-	/*     11      0   Address-Limit-Checking Control               */
-	/*     12      0   Suppress-Suspended-Interruption Control      */
-	/*     13      0   <zero>                                       */
-	/*     14      0   Format-2-IDAW Control                        */
-	/*     15      0   2K-IDAW Control                              */
-
-	0xff,
-	/*   bits  value   name                        desc             */
-	/*  16-23   0xff   Logical-Path Mask           All paths        */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*     24      0   Incorrect-Length-Suppression Mode            */
-	/*     25      0   Modified-CCW-Indirect-Data-Addressing Control*/
-	/*  26-30      0   <zero>                                       */
-	/*     31      0   ORB-Extension Control                        */
-
-	/* Word 2 */
-	0xff,0xff,0xff,0xff,
-	/*   bits  value   name                        desc             */
-	/*   0-31   addr   Channel-Program Address                      */
-
-	/* Word 3 */
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*    0-7      0   Channel-Subsystem Priority                   */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*   8-15      0   <zero/reserved>                              */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*  15-23      0   Control-Unit Priority                        */
-
-	0x00,
-	/*   bits  value   name                        desc             */
-	/*  24-31      0   <zero/reserved>                              */
-
-	/* Word 4 */
-	0x00,0x00,0x00,0x00,
-	/*   bits  value   name                        desc             */
-	/*   0-31      0   <zero/reserved>                              */
-
-	/* Word 5 */
-	0x00,0x00,0x00,0x00,
-	/*   bits  value   name                        desc             */
-	/*   0-31      0   <zero/reserved>                              */
-
-	/* Word 6 */
-	0x00,0x00,0x00,0x00,
-	/*   bits  value   name                        desc             */
-	/*   0-31      0   <zero/reserved>                              */
-
-	/* Word 7 */
-	0x00,0x00,0x00,0x00,
-	/*   bits  value   name                        desc             */
-	/*   0-31      0   <zero/reserved>                              */
-};
-
-/*
- * halt the cpu
- *
- * NOTE: we don't care about not clobbering registers as when this
- * code executes, the CPU will be stopped.
- */
-static inline void die(void)
-{
-	asm volatile(
-		"SR	%r1, %r1	# not used, but should be zero\n"
-		"SR	%r3, %r3 	# CPU Address\n"
-		"SIGP	%r1, %r3, 0x05	# Signal, order 0x05\n"
-	);
-
-	/*
-	 * Just in case SIGP fails
-	 */
-	for(;;);
-}
-
-static u64 pgm_new_psw[2] = {
-	0x0000000180000000ULL, (u64) &PGMHANDLER,
-};
-
-/*
- * determine amount of storage
- */
-static u64 sense_memsize(void)
-{
-	u64 size;
-	int cc;
-
-#define SKIP_SIZE	(1024*1024ULL)
-
-	/* set new PGM psw */
-	memcpy((void*)0x1d0, pgm_new_psw, 16);
-
-	for(size = 0; size < ((u64)~SKIP_SIZE)-1; size += SKIP_SIZE) {
-		asm volatile(
-			"lg	%%r1,%1\n"
-			"tprot	0(%%r1),0\n"
-			"ipm	%0\n"
-			"srl    %0,28\n"
-		: /* output */
-		  "=d" (cc)
-		: /* input */
-		  "m" (size)
-		: /* clobber */
-		  "cc", "r1"
-		);
-
-		/*
-		 * we cheat here a little...if we try to tprot a location
-		 * that isn't part of the configuration, a program exception
-		 * fires off, but our handler sets the CC to 3, and resumes
-		 * execution
-		 */
-		if (cc == 3)
-			break;
-	}
-
-	/* invalidate new PGM psw */
-	memset((void*)0x1d0, 0, 16);
-
-	return size;
-}
-
-/*
- * read the entire nucleus into into TEMP_BASE
- */
-static inline void readnucleus(void)
-{
-	register unsigned long base;
-
-	/*
-	 * First, seek past the tape mark
-	 */
-
-	/* set the CCW address in the ORB */
-	*((u32 *) &ORB[8]) = (u32) (u64) seek_ccw;
-
-#ifdef TAPE_SEEK
-	/*
-	 * Seek to the next tape mark
-	 */
-	if (__do_io())
-		die();
-#endif
-
-	/*
-	 * Second, read in BLOCK_SIZE chunks of nucleus
-	 */
-
-	/* set the CCW address in the ORB */
-	*((u32 *) &ORB[8]) = (u32) (u64) read_ccw;
-
-	read_ccw[6] = ((unsigned char) (BLOCK_SIZE >> 8) & 0xff);
-	read_ccw[7] = ((unsigned char) (BLOCK_SIZE & 0xff));
-
-	base = (unsigned long) TEMP_BASE;
-	for(;; base += BLOCK_SIZE) {
-		read_ccw[1] = ((unsigned char) (base >> 16));
-		read_ccw[2] = ((unsigned char) (base >> 8) & 0xff);
-		read_ccw[3] = ((unsigned char) (base & 0xff));
-		if (__do_io())
-			break;
-	}
-}
-
-void load_nucleus(void)
-{
-	/*
-	 * These are all stored in registers
-	 */
-	register u32 iplsch;
-	register int i;
-	register Elf64_Ehdr *nucleus_elf;
-	register Elf64_Shdr *section;
-	register void (*start_sym)(u64,u32);
-
-	/* Save the IPL device subchannel id */
-	iplsch = *((u32*) 0xb8);
-
-	/*
-	 * Read entire ELF to temporary location
-	 */
-	readnucleus();
-
-	nucleus_elf = (Elf64_Ehdr*) TEMP_BASE;
-
-	/*
-	 * Check that this looks like a valid ELF
-	 */
-	if (nucleus_elf->e_ident[0] != '\x7f' ||
-	    nucleus_elf->e_ident[1] != 'E' ||
-	    nucleus_elf->e_ident[2] != 'L' ||
-	    nucleus_elf->e_ident[3] != 'F' ||
-	    nucleus_elf->e_ident[EI_CLASS] != ELFCLASS64 ||
-	    nucleus_elf->e_ident[EI_DATA] != ELFDATA2MSB ||
-	    nucleus_elf->e_ident[EI_VERSION] != EV_CURRENT ||
-	    nucleus_elf->e_type != ET_EXEC ||
-	    nucleus_elf->e_machine != 0x16 || // FIXME: find the name for the #define
-	    nucleus_elf->e_version != EV_CURRENT)
-		die();
-
-	/*
-	 * Iterate through each section, and copy it to the final
-	 * destination as necessary
-	 */
-	for (i=0; i<nucleus_elf->e_shnum; i++) {
-		section = (Elf64_Shdr*) (TEMP_BASE +
-					 nucleus_elf->e_shoff +
-					 nucleus_elf->e_shentsize * i);
-
-		switch (section->sh_type) {
-			case SHT_PROGBITS:
-				if (!section->sh_addr)
-					break;
-
-				/*
-				 * just copy the data from TEMP_BASE to
-				 * where it wants to be
-				 */
-				memcpy((void*) section->sh_addr,
-					TEMP_BASE + section->sh_offset,
-					section->sh_size);
-				break;
-			case SHT_NOBITS:
-				/*
-				 * No action needed as there's no data to
-				 * copy, and we assume that the ELF sections
-				 * don't overlap
-				 */
-				memset((void*) section->sh_addr,
-				       0, section->sh_size);
-				break;
-			case SHT_SYMTAB:
-			case SHT_STRTAB:
-				/* TODO: relocate */
-				break;
-			default:
-				/* Ignoring */
-				break;
-		}
-	}
-
-	/*
-	 * Now, jump to the nucleus entry point
-	 */
-	start_sym = (void*) nucleus_elf->e_entry;
-	start_sym(sense_memsize(), iplsch);
-}
--- a/cp/ipl/loader_asm.S	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,252 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-#include <loader.h>
-
-.text
-	.align	4
-.globl __do_io
-	.type	__do_io, @function
-__do_io:
-	#
-	# r4 = 0x80000000
-	#
-	XGR	%r4, %r4
-	LHI	%r4, 0x8
-	SLL	%r4, 20
-	#
-	# r14 = r14 & 0x7fffffff don't ask, it's strangely retarded
-	#
-	L	%r1,ADDRMASK(%r4)
-	NR	%r14, %r1  # mask out the bit
-
-	# set up the interrupt handler
-	MVC	0x1f0(16),IOPSW(%r4)	# SET NEW IO PSW
-	LA	%r1,IOHANDLER(%r4)	# GET HANDLER ADDRESS
-	STG	%r1,0x1f0+8		# SAVE IN NEW PSW
-
-	L	%r1, 0xb8		# load subsystem ID
-	
-	SSCH	ORB(%r4)		# issue IO
-
-	# Load Control Register 6 with I/O interrupt subclass mask
-	STCTG	6,6,TMPVAR(%r4)		# GET CR6
-	OI	TMPVAR+4(%r4),0xFF	# enable all
-	LCTLG	6,6,TMPVAR(%r4)		# RELOAD MODIFIED CR6
-
-/*
-7) Enable the PSW for I/O interrupts and go into wait state (you need bits 6, 12 & 14 set to 1 in the PSW : X'020A000000000000' is a good example)
-*/
-	LPSWE	WAITPSW(%r4)
-
-#
-# The IO interrupt handler
-#
-.globl IOHANDLER
-IOHANDLER:
-	# is this for us?
-	L	%r1, MAGICVAL(%r4)
-	C	%r1, 0xbc
-	BNE	IONOTDONE(%r4)
-
-	# it is!
-
-	L	%r1, 0xb8		# load subsystem ID
-
-	TSCH	IRB(%r4)
-
-/*
-FIXME: we should do more checking!
-
-11) If Unit check or Channel Status|=0 : An I/O error occurred and act accordingly
-12) If unit exception : End of media (for tape & cards) and act accordingly
-13) If device end : I/O Completed.. Perform post I/O stuff (like advancing your pointers) and back to step 3
-*/
-
-	# unit check? (end of media?)
-	L	%r1,IRB+5(%r4)
-	LA	%r0,0x02
-	NR	%r0,%r1
-	LA	%r2,1			# return 1 - end of medium
-	BCR	4,%r14			# unit chk => return
-
-	# check the SCSW.. If CE Only : LPSW Old I/O PSW
-	LA	%r0,0x04
-	NR	%r0,%r1
-	LA	%r2,0			# means IO done
-	BCR	4,%r14			# device end => return
-
-IONOTDONE:
-	LPSWE	0x170
-
-#
-# The PGM interrupt handler
-#
-.globl PGMHANDLER
-PGMHANDLER:
-	STMG	%r1,%r3,0x200
-
-	# r3 = 0x80000000
-	XGR	%r3, %r3
-	LHI	%r3, 0x8
-	SLL	%r3, 20
-
-	# is ILC == 3?
-	LGH	%r2,0x8C
-	CGHI	%r2,0x0006
-	BNE	ERR
-
-	# grab the old PSW address, subtract length of TPROT, and compare it
-	# with the TPROT opcode (0xe501)
-	LG	%r1,0x158
-	AGHI	%r1,-6
-	LLGH	%r2,TPROTOP(%r3)
-	LLGH	%r1,0(%r1)
-	CGR	%r2,%r1
-	BNE	ERR(%r3)
-
-	# set CC=3
-	OI	0x152,0x30
-
-	LMG	%r1,%r3,0x200
-
-	LPSWE	0x150
-
-ERR:
-.byte	0x00, 0x00
-
-
-#
-# Useful data
-#
-.data
-.globl TPROTOP
-TPROTOP:
-.byte	0xe5, 0x01
-
-	.align 8
-.globl IOPSW
-IOPSW:
-	.byte	0x00
-		#   bits  value   name                        desc
-		#      0      0   <zero>
-		#      1      0   PER Mask (R)                disabled
-		#    2-4      0   <zero>
-		#      5      0   DAT Mode (T)                disabled
-		#      6      0   I/O Mask (IO)               enabled
-		#      7      0   External Mask (EX)          disabled
-
-	.byte	0x00
-		#   bits  value   name                        desc
-		#   8-11      0   Key
-		#     12      0   <one>
-		#     13      0   Machine-Check Mask (M)      disabled
-		#     14      0   Wait State (W)              executing
-		#     15      0   Problem State (P)           supervisor state
-		
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  16-17      0   Address-Space Control (AS)  disabled
-		#  18-19      0   Condition Code (CC)
-		#  20-23      0   Program Mask                exceptions disabled
-
-	.byte	0x01
-		#   bits  value   name                        desc
-		#  24-30      0   <zero>
-		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
-
-	.byte	0x80
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-		#   bits  value   name                        desc
-		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
-		#  33-63      0   <zero>
-		# 64-127   addr   Instruction Address         Address to exec
-
-.globl WAITPSW
-WAITPSW:
-	.byte	0x02
-		#   bits  value   name                        desc
-		#      0      0   <zero>
-		#      1      0   PER Mask (R)                disabled
-		#    2-4      0   <zero>
-		#      5      0   DAT Mode (T)                disabled
-		#      6      1   I/O Mask (IO)               enabled
-		#      7      0   External Mask (EX)          disabled
-
-	.byte	0x02
-		#   bits  value   name                        desc
-		#   8-11      0   Key
-		#     12      0   <zero>
-		#     13      0   Machine-Check Mask (M)      disabled
-		#     14      1   Wait State (W)              not executing
-		#     15      0   Problem State (P)           supervisor state
-		
-	.byte	0x00
-		#   bits  value   name                        desc
-		#  16-17      0   Address-Space Control (AS)  disabled
-		#  18-19      0   Condition Code (CC)
-		#  20-23      0   Program Mask                exceptions disabled
-
-	.byte	0x01
-		#   bits  value   name                        desc
-		#  24-30      0   <zero>
-		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
-
-	.byte	0x80
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-	.byte	0x00
-		#   bits  value   name                        desc
-		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
-		#  33-63      0   <zero>
-		# 64-127   addr   Instruction Address         Address to exec
-
-.globl TMPVAR
-TMPVAR:
-	.8byte 0x0
-
-.globl ADDRMASK
-ADDRMASK:
-	.4byte 0x7fffffff
-
-.globl MAGICVAL
-MAGICVAL:
-	.4byte 0x12345678
-
-.globl IRB
-IRB:
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-	.8byte 0x00
-
--- a/cp/ipl/setmode.S	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-#
-# At this point, the machine is running in ESA390 mode. Let's load a new
-# PSW, making it switch to 64-bit mode
-#
-
-	# switch to 64-bit mode
-	#
-	# Signal Processor
-	#   Order 0x12: Set Architecture
-	#   R1 bits 56-63 = 0x01 (switch all CPUs to z/Arch)
-	SR	%r1, %r1
-	LHI	%r1, 0x1	# switch all to z/Arch
-	SR	%r3, %r3	# CPU Address (CPU0000)
-	SIGP	%r1, %r3, 0x12	# Signal, order 0x12
-	SAM64
-	# On error:
-	#   Bit 55 = 1, cc1 (inval param)
-	#   Bit 54 = 1, cc1 (incorrect state)
-
-	# FIXME: check for errors?
-
-#
-# At this point, we should be in 64-bit mode
-#
-
-	#
-	# It is unfortunate that the below code is required.
-	#
-	# Let's set the stack pointer to make gcc happy
-	#
-	# A standard stack frame is 160 bytes.
-	#
-	# NOTE: Once this thread of execution turns into the idle thread,
-	# the stack can be reused for something else. Since it's allocated
-	# in the 128th processor's PSA, we have to make sure that it won't
-	# get initialized until after the idle thread kicks in.
-	#
-
-	# r15 = 0x100000
-	#     = (1 << 20)
-	#
-	SR	%r15, %r15
-	LHI	%r15, 0x1
-	SLL	%r15, 20
-	AHI	%r15, -160
-
-#
-# Padding to make the entire file 0x20 bytes
-#
-	BCR	0, %r7
-	BCR	0, %r7
--- a/cp/mm/buddy.c	Sat Feb 26 17:58:57 2011 -0500
+++ b/cp/mm/buddy.c	Wed Apr 20 20:50:32 2011 -0400
@@ -29,7 +29,7 @@
 		INIT_LIST_HEAD(&orders[order]);
 
 		if (pages & (1 << order)) {
-			p = addr_to_page(base);
+			p = addr_to_page((void*) base);
 
 			list_add(&p->buddy, &orders[order]);
 
--- a/cp/scripts/Makefile.commands	Sat Feb 26 17:58:57 2011 -0500
+++ b/cp/scripts/Makefile.commands	Wed Apr 20 20:50:32 2011 -0400
@@ -7,13 +7,12 @@
 #
 cmd-c-to-o=$(CC) $(CFLAGS) $(NUCLEUSCFLAGS) -c -o $(2) $(1)
 cmd-c-to-s=$(CC) $(CFLAGS) $(NUCLEUSCFLAGS) -S -o $(2) $(1)
-cmd-c-to-o-ipl=$(CC) $(CFLAGS) -DBLOCK_SIZE=$(3) $(4) -c -o $(2) $(1)
 cmd-s-to-o=$(AS) -m64 -o $(2) $(1)
 cmd-o-to-builtin=$(LD) $(LDFLAGS) -r -o $(2) $(1)
 cmd-link-hvf=$(LD) $(LDFLAGS) -T scripts/linker.script -o $(2) $(1)
-cmd-link-ipl=$(LD) -melf64_s390 -T ipl/linker.script -o $(2) $(1)
+cmd-link-ipl=$(LD) -melf64_s390 -T linker.script -o $(2) $(1)
 cmd-objcopy-t=$(OBJCOPY) -O binary -j .text $(1) $(2)
-cmd-objcopy-tdr=$(OBJCOPY) -O binary -j .text -j .data -j .rodata $(1) $(2)
+cmd-objcopy-tdr=$(OBJCOPY) -O binary -j .text -j .data -j .rodata -j .rodata.str1.2 $(1) $(2)
 cmd-genipl-rdr=bash scripts/gen_rdr_ccws.sh $(1)
 cmd-genipl-tape=bash scripts/gen_tape_ipl_s.sh $(1) $(2)
 cmd-concat=cat $(1) > $(2)
--- a/cp/scripts/gen_rdr_ccws.sh	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2008 Josef 'Jeff' Sipek
-#
-
-len=`stat -c %s ipl/setmode.rto`
-len=`expr $len + $(stat -c %s ipl/loader_rdr.rto)`
-dif=`expr $len % 80`
-
-if [ $dif -ne 0 ]; then
-	len=`expr $len - $dif`
-	len=`expr $len + 80`
-fi
-
-len=`expr $len \/ 80`
-
-# 13 CCWs * 80 bytes/ccw = 1040 bytes
-if [ $len -gt 13 ]; then
-	echo "ERROR: loader code is too long" >&2
-	exit 1
-fi
-
-echo -n "" > "$1"
-
-addr=8388608 # == 0x800000
-flags="0x60"
-for x in `seq 1 $len`; do
-	high_addr=`expr $addr \/ 65536`
-	mid_addr=`expr $(expr $addr \/ 256) % 256`
-	low_addr=`expr $addr % 256`
-
-	[ $x -eq $len ] && flags="0x20"
-
-	echo "# CCW $x" >> "$1"
-	echo "	.byte	0x02, $high_addr, $mid_addr, $low_addr" >> "$1"
-	echo "	.byte	$flags, 0x00, 0x00, 0x50" >> "$1"
-	echo "" >> "$1"
-
-	addr=`expr $addr + 80`
-done
-
-echo "# pad" >> "$1"
-for x in `seq $len 19`; do
-	echo "	.byte	0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40" >> "$1"
-done
--- a/cp/scripts/gen_tape_ipl_s.sh	Sat Feb 26 17:58:57 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2007 Josef 'Jeff' Sipek
-#
-
-len=80 # this is going the be the size of ipl.rto
-len=$(expr $len + `stat -c %s ipl/setmode.rto`)
-len=$(expr $len + `stat -c %s ipl/loader_tape.rto`)
-dif=`expr $len % 80`
-
-if [ $dif -ne 0 ]; then
-	len=`expr $len - $dif`
-	len=`expr $len + 80`
-fi
-
-if [ $len -gt `expr 65536` ]; then
-	echo "ERROR: loader code is too long" >&2
-	exit 1
-fi
-
-ccw2=$len
-
-ccw2h=`expr $ccw2 / 256`
-ccw2l=`expr $ccw2 % 256`
-
-sed -e "s/CCW2H/$ccw2h/g" \
-    -e "s/CCW2L/$ccw2l/g" \
-    "$1" > "$2"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/installed_files.txt	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,17 @@
+HVF installer creates the following files:
+
+This file (listing everything installed):
+  HVF      TEXT
+
+The directory:
+  HVF      DIRECT
+
+The system configuration file:
+  SYSTEM   CONFIG
+
+The nucleus:
+  HVF      ELF
+
+The nucleus loader:
+  DASDLOAD BIN
+  ECKDLOAD BIN
--- a/hercules/hvf.cnf	Sat Feb 26 17:58:57 2011 -0500
+++ b/hercules/hvf.cnf	Wed Apr 20 20:50:32 2011 -0400
@@ -31,7 +31,7 @@
 #---    ----    --------------------
 
 # Card Reader
-000C    3505    ../cp/loader_rdr.bin ../cp/hvf ebcdic multifile intrq
+000C    3505    ../installer.bin ebcdic autopad eof
 
 # Card Punch
 000D    3525    punch00d.txt ascii
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/list.h	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,421 @@
+/*
+ * Based on list.h from Linux Kernel
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#define LIST_POISON1	((void*) 0x8585858585858585ULL)
+#define LIST_POISON2	((void*) 0x8686868686868686ULL)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+		pos = pos->next)
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+		pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     prefetch(pos->member.prev), &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member)			\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member)			\
+	for (; prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member)			\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member),		\
+		n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member)			\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+#endif
--- a/include/string.h	Sat Feb 26 17:58:57 2011 -0500
+++ b/include/string.h	Wed Apr 20 20:50:32 2011 -0400
@@ -8,12 +8,15 @@
 #ifndef __STRING_H
 #define __STRING_H
 
+#include <vsprintf.h>
+
 #define memset(d,s,l)	__builtin_memset((d),(s),(l))
 #define memcpy(d,s,l)	__builtin_memcpy((d),(s),(l))
+extern void *memmove(void *dest, const void *src, size_t count);
 #define memcmp(d,s,l)	__builtin_memcmp((d),(s),(l))
 extern size_t strnlen(const char *s, size_t count);
 extern int strcmp(const char *cs, const char *ct);
-#define strncmp(a,b,l)	__builtin_strncmp((a),(b),(l))
+extern int strncmp(const char *cs, const char *ct, int len);
 extern int strcasecmp(const char *s1, const char *s2);
 extern char *strncpy(char *dest, const char *src, size_t count);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/.gitignore	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,6 @@
+*.o
+*.rto
+
+rdr.S
+
+archive.cpio
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/Makefile	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,33 @@
+AS=$(CROSS_COMPILE)as
+OBJCOPY=$(CROSS_COMPILE)objcopy
+CC=$(CROSS_COMPILE)gcc
+LD=$(CROSS_COMPILE)ld
+CFLAGS=-g -fno-strict-aliasing -fno-builtin -nostdlib -Wall -m64 -I ../include/ -O2 -include ../include/types.h
+NUCLEUSCFLAGS=
+LIBS=string ebcdic
+
+include ../cp/scripts/Makefile.commands
+
+all: rdr.rto loader.rto
+
+clean:
+	rm -f *.o
+	rm -f *.rto
+	rm -f rdr.S
+
+loader.o: setmode.o loader_c.o loader_asm.o cpio.o malloc.o edf.o
+	$(call link-ipl,$^ $(patsubst %,../lib/%.a,$(LIBS)),$@)
+
+rdr.S: loader.rto
+	../build/ccw_gen 00080000 80100000 `stat -c %s $<` 100000 080000 > $@
+
+%.o: %.c
+	$(call c-to-o,$<,$@)
+
+%.rto: %.o
+	$(call objcopy-tdr,$<,$@)
+
+%.o: %.S
+	$(call s-to-o,$<,$@)
+
+.PRECIOUS: %.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/cpio.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2011 Josef 'Jeff' Sipek
+ */
+#include "loader.h"
+#include <string.h>
+#include <ebcdic.h>
+
+struct cpio_hdr {
+	u8	magic[6];
+	u8	dev[6];
+	u8	ino[6];
+	u8	mode[6];
+	u8	uid[6];
+	u8	gid[6];
+	u8	nlink[6];
+	u8	rdev[6];
+	u8	mtime[11];
+	u8	namesize[6];
+	u8	filesize[11];
+	u8	data[0];
+};
+
+struct table {
+	char	*arch;
+	char	fn[8];
+	char	ft[8];
+	int	lrecl;
+	int	text;
+	u32	lba; /* 0 means undef */
+};
+
+static struct table table[] = {
+	{"hvf.directory",	"HVF     ", "DIRECT  ", 80,   1, 0},
+	{"hvf.config",		"SYSTEM  ", "CONFIG  ", 80,   1, 0},
+//	{"hvf",			"HVF     ", "ELF     ", 4096, 0, 0},
+	{"eckd.rto",		"ECKDLOAD", "BIN     ", 4096, 0, 1},
+	{"loader.rto",		"DASDLOAD", "BIN     ", 4096, 0, 2},
+	{"installed_files.txt",	"HVF     ", "TEXT    ", 80,   1, 0},
+	{"",			""        , ""        , -1,  -1, 0},
+};
+
+static void save_file(struct table *te, int filesize, u8 *buf)
+{
+	char pbuf[100];
+	struct FST fst;
+	int ret;
+	int rec;
+
+	ret = find_file(te->fn, te->ft, &fst);
+	if (!ret) {
+		wto("File '");
+		wto(te->fn);
+		wto("' already exists on the device.\n");
+		wto("The device has been left unmodified.\n");
+		die();
+	}
+
+	ret = create_file(te->fn, te->ft, te->lrecl, &fst);
+	if (ret) {
+		wto("Could not create file '");
+		wto(te->fn);
+		wto("'.\n");
+		die();
+	}
+
+	if (te->text)
+		ascii2ebcdic(buf, filesize);
+
+	if (te->lba) {
+		if (filesize > te->lrecl)
+			die();
+		snprintf(pbuf, 100, "special file, writing copy of data to LBA %d\n",
+			 te->lba);
+		wto(pbuf);
+		write_blk(buf, te->lba);
+	}
+
+	for(rec=0; rec<(filesize/te->lrecl); rec++) {
+		snprintf(pbuf, 100, "rec %03x at %p\n", rec,
+			 buf + (rec * te->lrecl));
+		wto(pbuf);
+		append_record(&fst, buf + (rec * te->lrecl));
+	}
+
+	if (filesize % te->lrecl) {
+		u8 buf2[te->lrecl];
+
+		memset(buf2, 0, te->lrecl);
+		memcpy(buf2, buf + (rec * te->lrecl), filesize % te->lrecl);
+
+		snprintf(pbuf, 100, "rec %03x at %p\n", rec,
+			 buf + (rec * te->lrecl));
+		wto(pbuf);
+		append_record(&fst, buf2);
+	}
+}
+
+static u32 getnumber(u8 *data, int digits)
+{
+	u32 ret = 0;
+
+	for(;digits; digits--, data++)
+		ret = (ret * 8) + (*data - '0');
+
+	return ret;
+}
+
+static void readcard(u8 *buf)
+{
+	static int eof;
+	int ret;
+	struct ccw ccw;
+
+	if (eof)
+		return;
+
+	ccw.cmd   = 0x02;
+	ccw.flags = 0;
+	ccw.count = 80;
+	ccw.addr  = ADDR31(buf);
+
+	ORB.param = 0x12345678,
+	ORB.f     = 1,
+	ORB.lpm   = 0xff,
+	ORB.addr  = ADDR31(&ccw);
+
+	ret = __do_io(ipl_sch);
+	if (ret == 0x01) {
+		eof = 1;
+		return;  // end of media
+	}
+
+	if (ret)
+		die();
+}
+
+void unload_archive(void)
+{
+	char printbuf[132];
+	struct cpio_hdr *hdr;
+	u8 *dasd_buf;
+	int save;
+	int fill;
+	int i;
+
+	u32 filesize;
+	u32 namesize;
+
+	dasd_buf = malloc(2*1024*1024);
+	hdr = (void*) dasd_buf;
+
+	wto("\n");
+
+	fill = 0;
+	while(1) {
+		/* read a file header */
+		if (fill < sizeof(struct cpio_hdr)) {
+			readcard(dasd_buf + fill);
+			fill += 80;
+		}
+
+		namesize = getnumber(hdr->namesize, 6);
+		filesize = getnumber(hdr->filesize, 11);
+
+		while(namesize + sizeof(struct cpio_hdr) > fill) {
+			readcard(dasd_buf + fill);
+			fill += 80;
+		}
+
+		if ((namesize == 11) &&
+		    !strncmp("TRAILER!!!", (char*) hdr->data, 10))
+			break;
+
+		save = 0;
+		for(i=0; table[i].lrecl != -1; i++) {
+			if (!strcmp((char*) hdr->data, table[i].arch)) {
+				save = 1;
+				break;
+			}
+		}
+
+		if (save) {
+			snprintf(printbuf, 132, "processing '%.8s' '%.8s' => '%s'\n",
+				 table[i].fn, table[i].fn + 8, (char*) hdr->data);
+			wto(printbuf);
+		} else {
+			snprintf(printbuf, 132, "skipping   '%s'\n",
+				 (char*) hdr->data);
+			wto(printbuf);
+		}
+
+		fill -= (sizeof(struct cpio_hdr) + namesize);
+		memmove(hdr, hdr->data + namesize, fill);
+
+		/* read the entire file into storage (assuming it's <= 1MB) */
+		while(fill < filesize) {
+			readcard(dasd_buf + fill);
+			fill += 80;
+		}
+
+		if (save)
+			save_file(&table[i], filesize, dasd_buf);
+
+		fill -= filesize;
+		memmove(dasd_buf, dasd_buf + filesize, fill);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/edf.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2011 Josef 'Jeff' Sipek
+ */
+#include "loader.h"
+#include <string.h>
+#include <ebcdic.h>
+#include <list.h>
+
+union adt_u {
+	struct ADT adt;
+	char buf[4096];
+};
+
+struct block_map {
+	struct list_head list;
+
+	/* key */
+	u8 fn[8]; /* EBCDIC */
+	u8 ft[8]; /* EBCDIC */
+	u8 level; /* 0 = data */
+	u32 blk_no;
+
+	/* value */
+	u32 lba;
+	void *buf;
+
+	/* dirty list */
+	int dirty;
+};
+
+static union adt_u *adt;
+static struct FST *directory;
+static struct FST *allocmap;
+
+static LIST_HEAD(block_map);
+
+/* borrowed from Linux */
+#define container_of(ptr, type, member) ({                      \
+         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+         (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/* borrowed from Linux */
+#define offsetof(type, member) __builtin_offsetof(type,member)
+
+#define DIRECTOR_FN ((u8*) "\x00\x00\x00\x01\x00\x00\x00\x00")
+#define DIRECTOR_FT ((u8*) "\xc4\xc9\xd9\xc5\xc3\xe3\xd6\xd9")
+#define ALLOCMAP_FN ((u8*) "\x00\x00\x00\x02\x00\x00\x00\x00")
+#define ALLOCMAP_FT ((u8*) "\xc1\xd3\xd3\xd6\xc3\xd4\xc1\xd7")
+
+static struct block_map *block_map_find(u8 *fn, u8 *ft, u8 level, u32 blk_no)
+{
+	struct block_map *cur;
+
+	list_for_each_entry(cur, &block_map, list) {
+		if (!memcmp(fn, cur->fn, 8) &&
+		    !memcmp(ft, cur->ft, 8) &&
+		    (level == cur->level) &&
+		    (blk_no == cur->blk_no))
+			return cur;
+	}
+
+	return NULL;
+}
+
+static void block_map_add(u8 *fn, u8 *ft, u8 level, u32 blk_no, u32 lba)
+{
+	struct block_map *map;
+
+	map = block_map_find(fn, ft, level, blk_no);
+	if (map) {
+		if (map->lba != lba)
+			die();
+
+		return;
+	}
+
+	map = malloc(sizeof(struct block_map));
+	if (!map)
+		die();
+
+	memcpy(map->fn, fn, 8);
+	memcpy(map->ft, ft, 8);
+	map->level = level;
+	map->blk_no = blk_no;
+
+	map->lba = lba;
+	map->buf = NULL;
+	map->dirty = 0;
+
+	list_add(&map->list, &block_map);
+}
+
+static void *read_file_blk(u8 *fn, u8 *ft, u8 level, u32 blk)
+{
+	struct block_map *map;
+
+	map = block_map_find(fn, ft, level, blk);
+	if (!map) {
+		char buf[128];
+		snprintf(buf,128,"%s could not find block %d at level %d\n",
+			 __func__, blk, level);
+		wto(buf);
+		die();
+	}
+
+	if (map->buf)
+		return map->buf;
+
+	map->buf = malloc(adt->adt.DBSIZ);
+	if (!map->buf)
+		die();
+
+	read_blk(map->buf, map->lba);
+
+	return map->buf;
+}
+
+static void blk_set_dirty(u8 *fn, u8 *ft, u8 level, u32 blk)
+{
+	struct block_map *map;
+
+	map = block_map_find(fn, ft, level, blk);
+	if (!map || !map->buf)
+		die();
+
+	map->dirty = 1;
+}
+
+void writeback_buffers()
+{
+	struct block_map *cur;
+	char buf[100];
+
+	list_for_each_entry(cur, &block_map, list) {
+		if (!cur->dirty)
+			continue;
+
+		if (!cur->buf)
+			die();
+
+		snprintf(buf, 100, "wb: %10d %p\n", cur->lba, cur->buf);
+		wto(buf);
+		write_blk(cur->buf, cur->lba);
+	}
+}
+
+/*
+ * fills in *fst with existing file info and returns 0, or if file doesn't
+ * exist, returns -1
+ */
+int find_file(char *fn, char *ft, struct FST *fst)
+{
+	struct FST *FST;
+	u8 FN[8];
+	u8 FT[8];
+	int rec;
+	int blk;
+
+	memcpy(FN, fn, 8);
+	memcpy(FT, ft, 8);
+	ascii2ebcdic(FN, 8);
+	ascii2ebcdic(FT, 8);
+
+	for(blk=0; blk<directory->ADBC; blk++) {
+		FST = read_file_blk(DIRECTOR_FN, DIRECTOR_FT, 0, blk);
+
+		for(rec=0; rec<adt->adt.NFST; rec++) {
+			if ((!memcmp(FST[rec].FNAME, FN, 8)) &&
+			    (!memcmp(FST[rec].FTYPE, FT, 8))) {
+				memcpy(fst, &FST[rec], sizeof(struct FST));
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+static void update_directory(struct FST *fst)
+{
+	struct FST *FST;
+	int rec;
+	int blk;
+
+	for(blk=0; blk<directory->ADBC; blk++) {
+		FST = read_file_blk(DIRECTOR_FN, DIRECTOR_FT, 0, blk);
+
+		for(rec=0; rec<adt->adt.NFST; rec++) {
+			if ((!memcmp(FST[rec].FNAME, fst->FNAME, 8)) &&
+			    (!memcmp(FST[rec].FTYPE, fst->FTYPE, 8))) {
+				memcpy(&FST[rec], fst, sizeof(struct FST));
+				blk_set_dirty(DIRECTOR_FN, DIRECTOR_FT, 0, blk);
+				return;
+			}
+		}
+	}
+
+	die();
+}
+
+static int file_blocks_at_level(u32 ADBC, int level)
+{
+	const u32 ptrs_per_block = adt->adt.DBSIZ / 4;
+	int blks;
+	int i;
+
+	blks = 1;
+	for(i=0; i<level; i++)
+		blks *= ptrs_per_block;
+	blks = (ADBC + blks - 1) / blks;
+
+	return blks;
+}
+
+static void __read_file(struct FST *fst)
+{
+	const u32 ptrs_per_block = adt->adt.DBSIZ / fst->PTRSZ;
+	u32 *blk_ptrs;
+	int level;
+
+	int blocks;
+	int i,j;
+
+	if (!fst->NLVL) {
+		block_map_add(fst->FNAME, fst->FTYPE, 0, 0, fst->FOP);
+		return;
+	}
+
+	/* there are pointer blocks, let's read them in and then
+	 * follow each pointer
+	 */
+
+	block_map_add(fst->FNAME, fst->FTYPE, fst->NLVL, 0, fst->FOP);
+
+	/* for each level of pointers... */
+	for(level=fst->NLVL; level>0; level--) {
+		blocks = file_blocks_at_level(fst->ADBC, level);
+
+		if (level == fst->NLVL && blocks != 1)
+			die();
+
+		/* read in each block */
+		for(i=0; i<blocks; i++) {
+			blk_ptrs = read_file_blk(fst->FNAME,
+						 fst->FTYPE,
+						 level,
+						 i);
+
+			/* and add each pointer to the next level */
+			for(j=0; j<ptrs_per_block; j++) {
+				block_map_add(fst->FNAME,
+					      fst->FTYPE,
+					      level-1,
+					      (i*ptrs_per_block) + j,
+					      blk_ptrs[j]);
+			}
+		}
+	}
+}
+
+int create_file(char *fn, char *ft, int lrecl, struct FST *fst)
+{
+	/* first, fill in the FST */
+	memset(fst, 0, sizeof(struct FST));
+
+	memcpy(fst->FNAME, fn, 8);
+	memcpy(fst->FTYPE, ft, 8);
+
+	ascii2ebcdic(fst->FNAME, 8);
+	ascii2ebcdic(fst->FTYPE, 8);
+
+	fst->FMODE[0] = '\xc1'; // EBCDIC 'A'
+	fst->FMODE[1] = '\xf1'; // EBCDIC '1'
+	fst->RECFM    = FSTDFIX; // fixed record size
+	fst->LRECL    = lrecl;
+	fst->PTRSZ    = 4;
+
+	append_record(directory, (u8*) fst);
+
+	return 0;
+}
+
+static u32 __get_free_block()
+{
+	u32 blk;
+	u8 *buf;
+	u32 i;
+	u32 bit;
+
+	for(blk=0; blk<allocmap->ADBC; blk++) {
+		buf = read_file_blk(allocmap->FNAME, allocmap->FTYPE, 0, blk);
+
+		for(i=0; i<adt->adt.DBSIZ; i++) {
+			if (buf[i] == 0xff)
+				continue;
+
+			if ((buf[i] & 0x80) == 0) { bit = 0; goto found; }
+			if ((buf[i] & 0x40) == 0) { bit = 1; goto found; }
+			if ((buf[i] & 0x20) == 0) { bit = 2; goto found; }
+			if ((buf[i] & 0x10) == 0) { bit = 3; goto found; }
+			if ((buf[i] & 0x08) == 0) { bit = 4; goto found; }
+			if ((buf[i] & 0x04) == 0) { bit = 5; goto found; }
+			if ((buf[i] & 0x02) == 0) { bit = 6; goto found; }
+			if ((buf[i] & 0x01) == 0) { bit = 7; goto found; }
+
+			continue; /* this is not necessary since we check
+				     for 0xff right away, but GCC really
+				     likes to complain about possibly
+				     uninitialized use of 'bit' */
+
+found:
+			buf[i] |= (0x80 >> bit);
+
+			blk_set_dirty(allocmap->FNAME, allocmap->FTYPE, 0, blk);
+
+			return ((blk * adt->adt.DBSIZ * 8) + (i * 8) + bit) + 1;
+		}
+	}
+
+	die();
+	return 0;
+}
+
+static void __append_block(struct FST *fst)
+{
+	struct block_map *map;
+	u32 *buf;
+	u32 lba, prevlba;
+	u32 blk;
+	u8 lvl;
+
+	/* no data blocks yet */
+	if (!fst->ADBC) {
+		fst->ADBC = 1;
+		fst->NLVL = 0;
+		fst->FOP  = __get_free_block();
+
+		block_map_add(fst->FNAME, fst->FTYPE, 0, 0, fst->FOP);
+		return;
+	}
+
+	for(lvl=0; lvl<=fst->NLVL; lvl++, prevlba=lba) {
+		blk = file_blocks_at_level(fst->ADBC+1, lvl);
+
+		map = block_map_find(fst->FNAME, fst->FTYPE, lvl, blk-1);
+		if (map) {
+			int x;
+
+			if (!lvl)
+				die();
+
+			buf = read_file_blk(fst->FNAME, fst->FTYPE, lvl,
+					    blk-1);
+			blk_set_dirty(fst->FNAME, fst->FTYPE, lvl, blk-1);
+
+			x = file_blocks_at_level(fst->ADBC+1, lvl-1) %
+				(adt->adt.DBSIZ / 4);
+			buf[x-1] = prevlba;
+
+			fst->ADBC++;
+			return;
+		}
+
+		lba = __get_free_block();
+
+		block_map_add(fst->FNAME, fst->FTYPE, lvl, blk-1, lba);
+
+		if (!lvl)
+			continue;
+
+		buf = read_file_blk(fst->FNAME, fst->FTYPE, lvl, blk-1);
+		blk_set_dirty(fst->FNAME, fst->FTYPE, lvl, blk-1);
+
+		*buf = prevlba;
+	}
+
+	lba = __get_free_block();
+	block_map_add(fst->FNAME, fst->FTYPE, fst->NLVL+1, 0, lba);
+
+	buf = read_file_blk(fst->FNAME, fst->FTYPE, fst->NLVL+1, 0);
+	blk_set_dirty(fst->FNAME, fst->FTYPE, fst->NLVL+1, 0);
+
+	buf[0] = fst->FOP;
+	buf[1] = prevlba;
+
+	fst->FOP = lba;
+
+	fst->NLVL++;
+	fst->ADBC++;
+}
+
+void append_record(struct FST *fst, u8 *buf)
+{
+	u32 foff;
+	u32 blk;
+	u32 off;
+	u32 rem;
+
+	u8 *dbuf;
+
+	if (fst->RECFM != FSTDFIX)
+		die();
+
+	foff = fst->AIC * fst->LRECL;
+
+	blk = foff / adt->adt.DBSIZ;
+	off = foff % adt->adt.DBSIZ;
+	rem = (fst->ADBC * adt->adt.DBSIZ) - foff;
+
+	/* need to add another block */
+	if (rem < fst->LRECL)
+		__append_block(fst);
+
+	dbuf = read_file_blk(fst->FNAME, fst->FTYPE, 0, blk);
+
+	if (!rem || (rem >= fst->LRECL)) {
+		memcpy(dbuf + off, buf, fst->LRECL);
+		blk_set_dirty(fst->FNAME, fst->FTYPE, 0, blk);
+	} else {
+		memcpy(dbuf + off, buf, rem);
+		blk_set_dirty(fst->FNAME, fst->FTYPE, 0, blk);
+
+		dbuf = read_file_blk(fst->FNAME, fst->FTYPE, 0, blk+1);
+		memcpy(dbuf, buf + rem, fst->LRECL - rem);
+		blk_set_dirty(fst->FNAME, fst->FTYPE, 0, blk+1);
+	}
+
+	fst->AIC++;
+
+	update_directory(fst);
+}
+
+void mount_fs()
+{
+	struct FST *fst;
+
+	adt = malloc(sizeof(union adt_u));
+
+	read_blk(adt, EDF_LABEL_BLOCK_NO);
+
+	if ((adt->adt.IDENT != __ADTIDENT) ||
+	    (adt->adt.DBSIZ != EDF_SUPPORTED_BLOCK_SIZE) ||
+	    (adt->adt.OFFST != 0) ||
+	    (adt->adt.FSTSZ != sizeof(struct FST)))
+		die();
+
+	block_map_add(DIRECTOR_FN, DIRECTOR_FT, 0, 0, adt->adt.DOP);
+
+	fst = read_file_blk(DIRECTOR_FN, DIRECTOR_FT, 0, 0);
+
+	if (memcmp(fst[0].FNAME, DIRECTOR_FN, 8) ||
+	    memcmp(fst[0].FTYPE, DIRECTOR_FT, 8) ||
+	    (fst[0].RECFM != FSTDFIX) ||
+	    memcmp(fst[1].FNAME, ALLOCMAP_FN, 8) ||
+	    memcmp(fst[1].FTYPE, ALLOCMAP_FT, 8) ||
+	    (fst[1].RECFM != FSTDFIX))
+		die();
+
+	directory = fst;
+	allocmap  = fst+1;
+
+	__read_file(&fst[0]); /* the directory */
+	__read_file(&fst[1]); /* the alloc map */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/layout.txt	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,8 @@
+dasd layout:
+
+0,0,1		eckd.S
+0,0,2		setmode.S
+		loader.c
+		loader_asm.S
+
+<on fs>		hvf.elf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/linker.script	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,10 @@
+SECTIONS
+{
+  ENTRY(MAIN)
+  . = 0x100000;
+  .text : { *(.text) }
+  .data : { *(.data) }
+  .rodata : { *(.rodata) }
+  .rodata.str1.2 : { *(.rodata.str1.2) }
+  .bss : { *(.bss) }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/loader.h	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,340 @@
+#ifndef __LOADER_H
+#define __LOADER_H
+
+#include <errno.h>
+
+/*
+ * "Config" values
+ */
+#define TEMP_BASE	((unsigned char*) 0x200000) /* 2MB */
+
+#define CON_DEVNUM	0x0009
+
+/* 3390 with 0 key length, and 4096 data length */
+#define RECORDS_PER_CYL		(15*12)
+#define RECORDS_PER_TRACK	12
+
+/* the nucleus fn ft (in EBCDIC) => HVF ELF */
+#define CP_FN	"\xC8\xE5\xC6\x40\x40\x40\x40\x40"
+#define CP_FT	"\xC5\xD3\xC6\x40\x40\x40\x40\x40"
+
+static inline int strlen(char *p)
+{
+	int i;
+
+	for(i=0;*p; p++, i++)
+		;
+
+	return i;
+}
+
+/*
+ * halt the cpu
+ *
+ * NOTE: we don't care about not clobbering registers as when this
+ * code executes, the CPU will be stopped.
+ */
+static inline void die(void)
+{
+	asm volatile(
+		"SR	%r1, %r1	# not used, but should be zero\n"
+		"SR	%r3, %r3 	# CPU Address\n"
+		"SIGP	%r1, %r3, 0x05	# Signal, order 0x05\n"
+	);
+
+	/*
+	 * Just in case SIGP fails
+	 */
+	for(;;);
+}
+
+/*
+ * I/O related structs, macros & variables
+ */
+struct ccw {
+	u8 cmd;			/* Command code */
+	u8 flags;		/* Flags */
+	u16 count;		/* Count */
+	u32 addr;		/* Data Address */
+} __attribute__((packed,aligned(8)));
+
+#define CCW_FLAG_CD		0x80	/* Chain-Data */
+#define CCW_FLAG_CC		0x40	/* Chain-Command */
+#define CCW_FLAG_SLI		0x20	/* Suppress-Length-Indication */
+#define CCW_FLAG_SKP		0x10	/* Skip */
+#define CCW_FLAG_PCI		0x08	/* Program-Controlled-Interruption */
+#define CCW_FLAG_IDA		0x04	/* Indirect-Data-Address */
+#define CCW_FLAG_S		0x02	/* Suspend */
+#define CCW_FLAG_MIDA		0x01	/* Modified-Indirect-Data-Address */
+
+struct orb {
+	/* word 0 */
+	u32 param;		/* Interruption Parameter */
+
+	/* word 1 */
+	u8 key:4,		/* Subchannel Key */
+	   s:1,			/* Suspend */
+	   c:1,			/* Streaming-Mode Control */
+	   m:1,			/* Modification Control */
+	   y:1;			/* Synchronization Control */
+	u8 f:1,			/* Format Control */
+	   p:1,			/* Prefetch Control */
+	   i:1,			/* Initial-Status-Interruption Control */
+	   a:1,			/* Address-Limit-Checking control */
+	   u:1,			/* Suppress-Suspend-Interruption Control */
+	   __zero1:1,
+	   h:1,			/* Format-2-IDAW Control */
+	   t:1;			/* 2K-IDAW Control */
+	u8 lpm;			/* Logical-Path Mask */
+	u8 l:1,			/* Incorrect-Length-Suppression Mode */
+	   d:1,			/* Modified-CCW-Indirect-Data-Addressing Control */
+	   __zero2:5,
+	   x:1;			/* ORB-Extension Control */
+
+	/* word 2 */
+	u32 addr;		/* Channel-Program Address */
+
+	/* word 3 */
+	u8 css_prio;		/* Channel-Subsystem Priority */
+	u8 __reserved1;
+	u8 cu_prio;		/* Control-Unit Priority */
+	u8 __reserved2;
+
+	/* word 4 - 7 */
+	u32 __reserved3;
+	u32 __reserved4;
+	u32 __reserved5;
+	u32 __reserved6;
+} __attribute__((packed,aligned(4)));
+
+struct scsw {
+	/* word 0 */
+	u16 key:4,		/* Subchannel key */
+	    s:1,		/* Suspend control */
+	    l:1,		/* ESW format */
+	    cc:2,		/* Deferred condition code */
+	    f:1,		/* Format */
+	    p:1,		/* Prefetch */
+	    i:1,		/* Initial-status interruption control */
+	    a:1,		/* Address-limit-checking control */
+	    u:1,		/* Supress-suspended interruption */
+	    z:1,		/* Zero condition code */
+	    e:1,		/* Extended control */
+	    n:1;		/* Path no operational */
+	u16 __zero:1,
+	    fc:3,		/* Function control */
+	    ac:8,		/* Activity control */
+	    sc:4;		/* Status control */
+
+	/* word 1 */
+	u32 addr;		/* CCW Address */
+
+	/* word 2 */
+	u8 dev_status;		/* Device status */
+	u8 sch_status;		/* Subchannel status */
+	u16 count;		/* Count */
+} __attribute__((packed));
+
+/* Path Management Control Word */
+struct pmcw {
+	/* word 0 */
+	u32 interrupt_param;	/* Interruption Parameter */
+
+	/* word 1*/
+	u8 __zero1:2,
+	   isc:3,		/* I/O-Interruption-Subclass Code */
+	   __zero2:3;
+	u8 e:1,			/* Enabled */
+	   lm:2,		/* Limit Mode */
+	   mm:2,		/* Measurement-Mode Enable */
+	   d:1,			/* Multipath Mode */
+	   t:1,			/* Timing Facility */
+	   v:1;			/* Device Number Valid */
+	u16 dev_num;		/* Device Number */
+
+	/* word 2 */
+	u8 lpm;			/* Logical-Path Mask */
+	u8 pnom;		/* Path-Not-Operational Mask */
+	u8 lpum;		/* Last-Path-Used Mask */
+	u8 pim;			/* Path-Installed Mask */
+
+	/* word 3 */
+	u16 mbi;		/* Measurement-Block Index */
+	u8 pom;			/* Path-Operational Mask */
+	u8 pam;			/* Path-Available Mask */
+
+	/* word 4 & 5 */
+	u8 chpid[8];		/* Channel-Path Identifiers */
+
+	/* word 6 */
+	u16 __zero3;
+	u16 __zero4:13,
+	    f:1,		/* Measurement Block Format Control */
+	    x:1,		/* Extended Measurement Word Mode Enable */
+	    s:1;		/* Concurrent Sense */
+};
+
+struct schib {
+	struct pmcw pmcw;		/* Path Management Control Word */
+	struct scsw scsw;		/* Subchannel Status Word */
+	u32 w0, w1;
+	u32 model_dep_area;
+} __attribute__((packed,aligned(4)));
+
+#define ADDR31(x)	((u32) (u64) (x))
+
+extern void wto(char *str);
+extern void wtor(char *str, char *inp, int buflen);
+extern void __readwrite_blk(void *ptr, u32 lba, int rwccw);
+#define read_blk(ptr, lba)	__readwrite_blk((ptr), (lba), 0x86)
+#define write_blk(ptr, lba)	__readwrite_blk((ptr), (lba), 0x05)
+
+extern int __do_io(u32 sch);
+extern void __wait_for_attn();
+extern void PGMHANDLER();
+extern void IOHANDLER();
+
+extern void load_nucleus(void);
+
+extern struct orb ORB;
+
+extern u64 ipl_sch;
+extern u64 con_sch;
+extern u64 dasd_sch;
+
+static inline int store_sch(u32 sch, struct schib *schib)
+{
+	int cc;
+
+	asm volatile(
+		"lr	%%r1,%2\n"
+		"stsch	%1\n"
+		"ipm	%0\n"
+		"srl	%0,28\n"
+		: /* output */
+		  "=d" (cc),
+		  "=Q" (*schib)
+		: /* input */
+		  "d" (sch)
+		: /* clobbered */
+		  "cc", "r1", "memory"
+	);
+
+	if (cc == 3)
+		return -EINVAL;
+	return 0;
+}
+
+static inline int modify_sch(u32 sch, struct schib *schib)
+{
+	int cc;
+
+	asm volatile(
+		"lr	%%r1,%1\n"
+		"msch	%2\n"
+		"ipm	%0\n"
+		"srl	%0,28\n"
+		: /* output */
+		  "=d" (cc)
+		: /* input */
+		  "d" (sch),
+		  "m" (*schib)
+		: /* clobbered */
+		  "cc", "r1"
+	);
+
+	if (cc == 1 || cc == 2)
+		return -EBUSY;
+	if (cc == 3)
+		return -EINVAL;
+	return 0;
+}
+
+extern void unload_archive(void);
+
+/*
+ * EDF related structs & macros
+ */
+#define EDF_LABEL_BLOCK_NO		3
+#define EDF_SUPPORTED_BLOCK_SIZE	4096
+
+struct ADT {
+	u32     IDENT;       /* VOL START / LABEL IDENTIFIER */
+#define __ADTIDENT 0xC3D4E2F1   /* 'CMS1' in EBCDIC */
+	u8      ID[6];       /* VOL START / VOL IDENTIFIER */
+	u8      VER[2];      /* VERSION LEVEL */
+	u32     DBSIZ;       /* DISK BLOCK SIZE */
+	u32     DOP;         /* DISK ORIGIN POINTER */
+	u32     CYL;         /* NUM OF FORMATTED CYL ON DISK */
+	u32     MCYL;        /* MAX NUM FORMATTED CYL ON DISK */
+	u32     NUM;         /* Number of Blocks on disk */
+	u32     USED;        /* Number of Blocks used */
+	u32     FSTSZ;       /* SIZE OF FST */
+	u32     NFST;        /* NUMBER OF FST'S PER BLOCK */
+	u8      DCRED[6];    /* DISK CREATION DATE (YYMMDDHHMMSS) */
+	u8      FLGL;        /* LABEL FLAG BYTE (ADTFLGL) */
+#define ADTCNTRY        0x01    /* Century for disk creation date (0=19, 1=20),
+	                         * corresponds to ADTDCRED. */
+	u8      reserved[1];
+	u32     OFFST;       /* DISK OFFSET WHEN RESERVED */
+	u32     AMNB;        /* ALLOC MAP BLOCK WITH NEXT HOLE */
+	u32     AMND;        /* DISP INTO HBLK DATA OF NEXT HOLE */
+	u32     AMUP;        /* DISP INTO USER PART OF ALLOC MAP */
+	u32     OFCNT;       /* Count of SFS open files for this ADT */
+	u8      SFNAM[8];    /* NAME OF SHARED SEGMENT */
+};
+
+struct FST {
+	u8    FNAME[8];       /* filename */
+	u8    FTYPE[8];       /* filetype */
+	u8    DATEW[2];       /* DATE LAST WRITTEN - MMDD */
+	u8    TIMEW[2];       /* TIME LAST WRITTEN - HHMM */
+	u16   WRPNT;          /* WRITE POINTER - ITEM NUMBER */
+	u16   RDPNT;          /* READ POINTER - ITEM NUMBER */
+	u8    FMODE[2];       /* FILE MODE - LETTER AND NUMBER */
+	u16   RECCT;          /* NUMBER OF LOGICAL RECORDS */
+	u16   FCLPT;          /* FIRST CHAIN LINK POINTER */
+	u8    RECFM;          /* F*1 - RECORD FORMAT - F OR V */
+#define FSTDFIX        0xC6 /* Fixed record format (EBCDIC 'F') */
+#define FSTDVAR        0xE5 /* Variable record format (EBCDIC 'V') */
+	u8    FLAGS;          /* F*2 - FST FLAG BYTE */
+#define FSTRWDSK       0x80 /* READ/WRITE DISK */
+#define FSTRODSK       0x00 /* READ/ONLY DISK */
+#define FSTDSFS        0x10 /* Shared File FST */
+#define FSTXRDSK       0x40 /* EXTENSION OF R/O DISK */
+#define FSTXWDSK       0xC0 /* EXTENSION OF R/W DISK */
+#define FSTEPL         0x20 /* EXTENDED PLIST */
+#define FSTDIA         0x40 /* ITEM AVAILABLE */
+#define FSTDRA         0x01 /* PREVIOUS RECORD NULL */
+#define FSTCNTRY       0x08 /* Century for date last written (0=19, 1=20),\\
+			       corresponds to FSTYEARW, FSTADATI. */
+#define FSTACTRD       0x04 /* ACTIVE FOR READING */
+#define FSTACTWR       0x02 /* ACTIVE FOR WRITING */
+#define FSTACTPT       0x01 /* ACTIVE FROM A POINT */
+#define FSTFILEA       0x07 /* THE FILE IS ACTIVE */
+	u32   LRECL;          /* LOGICAL RECORD LENGTH */
+	u16   BLKCT;          /* NUMBER OF 800 BYTE BLOCKS */
+	u16   YEARW;          /* YEAR LAST WRITTEN */
+	u32   FOP;            /* ALT. FILE ORIGIN POINTER */
+	u32   ADBC;           /* ALT. NUMBER OF DATA BLOCKS */
+	u32   AIC;            /* ALT. ITEM COUNT */
+	u8    NLVL;           /* NUMBER OF POINTER BLOCK LEVELS */
+	u8    PTRSZ;          /* LENGTH OF A POINTER ELEMENT */
+	u8    ADATI[6];       /* ALT. DATE/TIME(YY MM DD HH MM SS) */
+	u8    REALM;          /* Real filemode */
+	u8    FLAG2;          /* F*3 - FST FLAG BYTE 2 FSTFLAG2 */
+#define FSTPIPEU       0x10 /* Reserved for CMS PIPELINES usage */
+	u8    reserved[2];
+};
+
+extern void init_malloc(void *ptr);
+extern void *malloc(u32 size);
+
+extern void mount_fs();
+extern int find_file(char *fn, char *ft, struct FST *fst);
+extern int create_file(char *fn, char *ft, int lrecl, struct FST *fst);
+extern void append_record(struct FST *fst, u8 *buf);
+
+extern void writeback_buffers();
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/loader_asm.S	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,218 @@
+#
+# Copyright (c) 2007-2011 Josef 'Jeff' Sipek
+#
+
+#include "loader.h"
+
+.text
+	.align	4
+.globl __wait_for_attn
+	.type	__wait_for_attn, @function
+__wait_for_attn:
+	LARL	%r4,WAITPSW		# wait for the interrupt
+	LPSWE	0(%r4)
+
+# int __do_io(u32 sch);
+	.align	4
+.globl __do_io
+	.type	__do_io, @function
+__do_io:
+	LGR	%r1,%r2			# load subsystem ID
+
+	LA	%r2,0xfff		# error return code
+
+	LARL	%r4,ORB
+	SSCH	0(%r4)			# issue IO
+	BCR	7, %r14			# return on error
+
+	LARL	%r4,WAITPSW		# wait for the interrupt
+	LPSWE	0(%r4)
+
+#
+# The IO interrupt handler; it's very much like a continuation of __do_io
+#
+.globl IOHANDLER
+IOHANDLER:
+	# is this for us?
+	LARL	%r1,MAGICVAL
+	L	%r1,0(%r1)
+	C	%r1,0xbc
+	BRC	7,IONOTDONE
+
+	# it is!
+
+	LLGF	%r1,0xb8		# load subsystem ID
+
+	LARL	%r4,IRB
+	TSCH	0(%r4)
+
+	# check the Channel Status for != 0
+	XGR	%r2,%r2
+	IC	%r2,9(%r4)
+	NILL	%r2,0xbf		# get rid of SLI
+	LTR	%r2,%r2
+	BCR	7,%r14			# error, let's bail
+
+	# check the Device Status
+	XGR	%r1,%r1
+	IC	%r1,8(%r4)
+
+	# unit check, unit except?
+	LR	%r2,%r1
+	NILL	%r2,0x03
+	BCR	4,%r14			# error return
+
+	# attention, DE?
+	XGR	%r2,%r2
+	NILL	%r1,0x84
+	BCR	4,%r14			# ok return
+
+IONOTDONE:
+	LPSWE	0x170
+
+#
+# The PGM interrupt handler
+#
+.globl PGMHANDLER
+PGMHANDLER:
+	STMG	%r1,%r3,0x200
+
+	# r3 = 0x80000000
+	XGR	%r3, %r3
+	LHI	%r3, 0x8
+	SLL	%r3, 20
+
+	# is ILC == 3?
+	LGH	%r2,0x8C
+	CGHI	%r2,0x0006
+	BNE	ERR(%r3)
+
+	# grab the old PSW address, subtract length of TPROT, and compare it
+	# with the TPROT opcode (0xe501)
+	LG	%r1,0x158
+	AGHI	%r1,-6
+	LLGH	%r2,TPROTOP(%r3)
+	LLGH	%r1,0(%r1)
+	CGR	%r2,%r1
+	BNE	ERR(%r3)
+
+	# set CC=3
+	OI	0x152,0x30
+
+	LMG	%r1,%r3,0x200
+
+	LPSWE	0x150
+
+ERR:
+.byte	0x00, 0x00
+
+
+#
+# Useful data
+#
+.data
+.globl TPROTOP
+TPROTOP:
+.byte	0xe5, 0x01
+
+	.align 8
+.globl IOPSW
+IOPSW:
+	.byte	0x00
+		#   bits  value   name                        desc
+		#      0      0   <zero>
+		#      1      0   PER Mask (R)                disabled
+		#    2-4      0   <zero>
+		#      5      0   DAT Mode (T)                disabled
+		#      6      0   I/O Mask (IO)               enabled
+		#      7      0   External Mask (EX)          disabled
+
+	.byte	0x00
+		#   bits  value   name                        desc
+		#   8-11      0   Key
+		#     12      0   <one>
+		#     13      0   Machine-Check Mask (M)      disabled
+		#     14      0   Wait State (W)              executing
+		#     15      0   Problem State (P)           supervisor state
+
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  16-17      0   Address-Space Control (AS)  disabled
+		#  18-19      0   Condition Code (CC)
+		#  20-23      0   Program Mask                exceptions disabled
+
+	.byte	0x01
+		#   bits  value   name                        desc
+		#  24-30      0   <zero>
+		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
+
+	.byte	0x80, 0x00, 0x00, 0x00
+	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		#   bits  value   name                        desc
+		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
+		#  33-63      0   <zero>
+		# 64-127   addr   Instruction Address         Address to exec
+
+.globl WAITPSW
+WAITPSW:
+	.byte	0x02
+		#   bits  value   name                        desc
+		#      0      0   <zero>
+		#      1      0   PER Mask (R)                disabled
+		#    2-4      0   <zero>
+		#      5      0   DAT Mode (T)                disabled
+		#      6      1   I/O Mask (IO)               enabled
+		#      7      0   External Mask (EX)          disabled
+
+	.byte	0x02
+		#   bits  value   name                        desc
+		#   8-11      0   Key
+		#     12      0   <zero>
+		#     13      0   Machine-Check Mask (M)      disabled
+		#     14      1   Wait State (W)              not executing
+		#     15      0   Problem State (P)           supervisor state
+
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  16-17      0   Address-Space Control (AS)  disabled
+		#  18-19      0   Condition Code (CC)
+		#  20-23      0   Program Mask                exceptions disabled
+
+	.byte	0x01
+		#   bits  value   name                        desc
+		#  24-30      0   <zero>
+		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
+
+	.byte	0x80, 0x00, 0x00, 0x00
+	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		#   bits  value   name                        desc
+		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
+		#  33-63      0   <zero>
+		# 64-127   addr   Instruction Address         Address to exec
+
+.globl IRB
+IRB:
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+
+	.align 4
+.globl ORB
+ORB:
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+
+.globl MAGICVAL
+MAGICVAL:
+	.4byte 0x12345678
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/loader_c.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2007-2011 Josef 'Jeff' Sipek
+ */
+
+#include "loader.h"
+#include <binfmt_elf.h>
+#include <string.h>
+#include <ebcdic.h>
+
+static unsigned char seek_data[6];
+static unsigned char search_data[5];
+
+extern struct orb ORB;
+
+static u8 *buf = (u8*) (16 * 1024);
+static u32 *ptrbuf = (u32*) (20 * 1024);
+
+static u64 pgm_new_psw_diswait[2] = {
+	0x0002000180000000ULL, 0,
+};
+
+static u64 io_new_psw[2] = {
+	0x0000000180000000ULL, (u64) &IOHANDLER,
+};
+
+static u64 pgm_new_psw[2] = {
+	0x0000000180000000ULL, (u64) &PGMHANDLER,
+};
+
+u64 ipl_sch;
+u64 con_sch;
+u64 dasd_sch;
+
+void __readwrite_blk(void *ptr, u32 lba, int rwccw)
+{
+	struct ccw ccw[4];
+
+	u16 cc, hh, r;
+
+	if (lba < 1)
+		die();
+
+	memset(ccw, 0, sizeof(ccw));
+
+	lba--;
+	cc = lba / RECORDS_PER_CYL;
+	hh = (lba % RECORDS_PER_CYL) / RECORDS_PER_TRACK;
+	r = (lba % RECORDS_PER_CYL) % RECORDS_PER_TRACK;
+	r++;
+
+	ORB.addr = ADDR31(ccw);
+
+	/* SEEK */
+	ccw[0].cmd = 0x07;
+	ccw[0].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
+	ccw[0].count = 6;
+	ccw[0].addr = ADDR31(seek_data);
+
+	seek_data[0] = 0;		/* zero */
+	seek_data[1] = 0;		/* zero */
+	seek_data[2] = cc >> 8;		/* Cc */
+	seek_data[3] = cc & 0xff;	/* cC */
+	seek_data[4] = hh >> 8;		/* Hh */
+	seek_data[5] = hh & 0xff;	/* hH */
+
+	/* SEARCH */
+	ccw[1].cmd = 0x31;
+	ccw[1].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
+	ccw[1].count = 5;
+	ccw[1].addr = ADDR31(search_data);
+
+	search_data[0] = cc >> 8;
+	search_data[1] = cc & 0xff;
+	search_data[2] = hh >> 8;
+	search_data[3] = hh & 0xff;
+	search_data[4] = r;
+
+	/* TIC */
+	ccw[2].cmd = 0x08;
+	ccw[2].flags = 0;
+	ccw[2].count = 0;
+	ccw[2].addr = ADDR31(&ccw[1]);
+
+	/* READ/WRITE DATA */
+	ccw[3].cmd = rwccw;
+	ccw[3].flags = 0;
+	ccw[3].count = 4096;
+	ccw[3].addr = ADDR31(ptr);
+
+	/*
+	 * issue IO
+	 */
+	if (__do_io(dasd_sch))
+		die();
+}
+
+struct senseid_struct {
+	u8 __reserved;
+	u16 cu_type;
+	u8 cu_model;
+	u16 dev_type;
+	u8 dev_model;
+} __attribute__((packed));
+
+
+static int dev_dasd(void)
+{
+	int ret;
+	struct ccw ccw;
+	struct senseid_struct id;
+
+	ccw.cmd   = 0xe4;
+	ccw.flags = CCW_FLAG_SLI;
+	ccw.count = sizeof(struct senseid_struct);
+	ccw.addr  = ADDR31(&id);
+
+	ORB.param = 0x12345678,
+	ORB.f     = 1,
+	ORB.lpm   = 0xff,
+	ORB.addr  = ADDR31(&ccw);
+
+	ret = __do_io(dasd_sch);
+	if (ret)
+		die();
+
+	if ((id.dev_type  == 0x3390) &&
+	    (id.dev_model == 0x0A) &&
+	    (id.cu_type   == 0x3990) &&
+	    (id.cu_model  == 0xC2))
+		return 0; // ECKD 3390-3 that we know how to use
+
+	return -1; // not a DASD
+}
+
+static u64 atoi(char *s, int len)
+{
+	u64 i = 0;
+
+	while(len) {
+		 if ((*s >= '0') && (*s <= '9'))
+			i = (i * 16) + (*s - '0');
+		 else if ((*s >= 'A') && (*s <= 'F'))
+			i = (i * 16) + (*s - 'A' + 10);
+		 else if ((*s >= 'a') && (*s <= 'f'))
+			i = (i * 16) + (*s - 'a' + 10);
+		 else
+			 break;
+		len--;
+		s++;
+	}
+
+	return i;
+}
+
+static u64 find_devnum(u64 devnum)
+{
+	struct schib schib;
+	u64 sch;
+
+	if (devnum > 0xffff)
+		die();
+
+	memset(&schib, 0, sizeof(struct schib));
+
+	/*
+	 * For each possible subchannel id...
+	 */
+	for(sch = 0x10000; sch <= 0x1ffff; sch++) {
+		/*
+		 * ...call store subchannel, to find out whether or not
+		 * there is a device
+		 */
+		if (store_sch(sch, &schib))
+			continue;
+
+		if (!schib.pmcw.v)
+			continue;
+
+		if (schib.pmcw.dev_num != devnum)
+			continue;
+
+		schib.pmcw.e = 1;
+
+		if (modify_sch(sch, &schib))
+			die();
+
+		return sch;
+	}
+
+	return ~0;
+}
+
+void wto(char *str)
+{
+	int ret;
+	struct ccw ccw;
+	char buf[160];
+
+	strncpy(buf, str, 160);
+	buf[159] = '\0';
+
+	ascii2ebcdic((u8*)buf, 160);
+
+	ccw.cmd   = 0x01;
+	ccw.flags = 0;
+	ccw.count = strlen(str);
+	ccw.addr  = ADDR31(buf);
+
+	ORB.param = 0x12345678,
+	ORB.f     = 1,
+	ORB.lpm   = 0xff,
+	ORB.addr  = ADDR31(&ccw);
+
+	ret = __do_io(con_sch);
+	if (ret)
+		die();
+}
+
+void wtor(char *str, char *inp, int buflen)
+{
+	int ret;
+	struct ccw ccw;
+
+	wto(str);
+
+	/* wait for user input */
+	__wait_for_attn();
+
+	/* read user input */
+	ccw.cmd   = 0x0a;
+	ccw.flags = CCW_FLAG_SLI;
+	ccw.count = buflen;
+	ccw.addr  = ADDR31(inp);
+
+	ORB.param = 0x12345678,
+	ORB.f     = 1,
+	ORB.lpm   = 0xff,
+	ORB.addr  = ADDR31(&ccw);
+
+	ret = __do_io(con_sch);
+	if (ret)
+		die();
+
+	ebcdic2ascii((u8*)inp, buflen);
+}
+
+static void init_io(void)
+{
+	u64 cr6;
+
+	/* enable all I/O interrupt classes */
+	asm volatile(
+		"stctg	6,6,%0\n"	/* get cr6 */
+		"oi	%1,0xff\n"	/* enable all */
+		"lctlg	6,6,%0\n"	/* reload cr6 */
+	: /* output */
+	: /* input */
+	  "m" (cr6),
+	  "m" (*(u64*) (((u8*)&cr6) + 4))
+	);
+}
+
+void load_nucleus(void)
+{
+	char inp[160];
+
+	/* Save the IPL device subchannel id */
+	ipl_sch = *((u32*) 0xb8);
+
+	memcpy((void*) 0x1d0, pgm_new_psw_diswait, 16);
+	memcpy((void*) 0x1f0, io_new_psw, 16);
+
+	init_io();
+
+	/*
+	 * try to find the console on 0009
+	 */
+	con_sch = find_devnum(CON_DEVNUM);
+	if (con_sch > 0x1ffff)
+		die();
+
+	/*
+	 * greet the user on the console
+	 */
+	wto("HVF installer\n\n");
+
+	for(;;) {
+		/*
+		 * ask the user for target dasd
+		 */
+		wtor("Specify target device (1-4 hex digits):\n", inp, 160);
+
+		dasd_sch = find_devnum(atoi(inp, 160));
+		if ((dasd_sch < 0x10000) || (dasd_sch > 0x1ffff)) {
+			wto("Invalid device number.\n\n");
+			continue;
+		}
+
+		wto("Device found.\n\n");
+		if (dev_dasd()) {
+			wto("Need a DASD.\n\n");
+			continue;
+		}
+
+		break;
+	}
+
+	inp[0] = 'n';
+	wtor("Format volume? [y/n]\n", inp, 160);
+
+	if ((inp[0] == 'y') || (inp[0] == 'Y')) {
+		wto("Formating volume...\n");
+
+		/*
+		 * FIXME:
+		 * 1) format the volume
+		 * 2) setup EDF
+		 */
+
+		wto("done. (Not yet implemented)\n");
+		die();
+	} else
+		wto("Formatting skipped.\n");
+
+	/*
+	 * initialize the memory allocator
+	 */
+	init_malloc(TEMP_BASE);
+
+	/*
+	 * mount the EDF volume
+	 */
+	mount_fs();
+
+	/*
+	 * read through the archive and decide what to do with each file
+	 */
+	unload_archive();
+
+	/*
+	 * currently, we haven't written anything to disk; flush everything
+	 */
+	writeback_buffers();
+
+	/*
+	 * FIXME: inform the user that we're done, and load a psw with the
+	 * right magic
+	 */
+
+	wto("\nInstallation complete.\n");
+	wto("You can now IPL from the DASD.\n");
+
+	for(;;);
+	die();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/malloc.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Josef 'Jeff' Sipek
+ */
+#include "loader.h"
+
+static char *top;
+
+void init_malloc(void *ptr)
+{
+	top = ptr;
+}
+
+void *malloc(u32 size)
+{
+	char *t;
+
+	t = top;
+	top += size;
+
+	return t;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installer/setmode.S	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 2007-2011 Josef 'Jeff' Sipek
+#
+
+.globl MAIN
+	.type	MAIN, @function
+MAIN:
+#
+# At this point, the machine is running in ESA390 mode. Let's load a new
+# PSW, making it switch to 64-bit mode
+#
+
+	# switch to 64-bit mode
+	#
+	# Signal Processor
+	#   Order 0x12: Set Architecture
+	#   R1 bits 56-63 = 0x01 (switch all CPUs to z/Arch)
+	SR	%r1, %r1
+	LHI	%r1, 0x1	# switch all to z/Arch
+	SR	%r3, %r3	# CPU Address (CPU0000)
+	SIGP	%r1, %r3, 0x12	# Signal, order 0x12
+	SAM64
+	# On error:
+	#   Bit 55 = 1, cc1 (inval param)
+	#   Bit 54 = 1, cc1 (incorrect state)
+
+	# FIXME: check for errors?
+
+#
+# At this point, we should be in 64-bit mode
+#
+
+	#
+	# It is unfortunate that the below code is required.
+	#
+	# Let's set the stack pointer to make gcc happy
+	#
+	# A standard stack frame is 160 bytes.
+	#
+
+	# r15 = 0x100000
+	#     = (1 << 20)
+	#
+	SGR	%r15, %r15
+	LGHI	%r15, 0x1
+	SLLG	%r15, %r15, 20
+	AGHI	%r15, -160
+
+	BRC	15,load_nucleus
--- a/lib/string.c	Sat Feb 26 17:58:57 2011 -0500
+++ b/lib/string.c	Wed Apr 20 20:50:32 2011 -0400
@@ -40,6 +40,23 @@
 }
 
 /**
+ * strncmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ * @len: max length
+ */
+int strncmp(const char *cs, const char *ct, int len)
+{
+        signed char __res;
+
+        while (1) {
+                if ((__res = *cs - *ct++) != 0 || !*cs++ || len--)
+                        break;
+        }
+        return __res;
+}
+
+/**
  * strcasecmp - Compare two strings ignoring case
  * @s1: One string
  * @s2: Another string
@@ -81,6 +98,35 @@
        return dest;
 }
 
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void *memmove(void *dest, const void *src, size_t count)
+{
+	char *tmp;
+	const char *s;
+
+	if (dest <= src) {
+		tmp = dest;
+		s = src;
+		while (count--)
+			*tmp++ = *s++;
+	} else {
+		tmp = dest;
+		tmp += count;
+		s = src;
+		s += count;
+		while (count--)
+			*--tmp = *--s;
+	}
+	return dest;
+}
+
 /* ASCII character info */
 unsigned char _ascii_ctype[] = {
 _C,_C,_C,_C,_C,_C,_C,_C,                        /* 0-7 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/.gitignore	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,2 @@
+*.o
+*.rto
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/Makefile	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,28 @@
+AS=$(CROSS_COMPILE)as
+OBJCOPY=$(CROSS_COMPILE)objcopy
+CC=$(CROSS_COMPILE)gcc
+LD=$(CROSS_COMPILE)ld
+CFLAGS=-g -fno-strict-aliasing -fno-builtin -nostdlib -Wall -m64 -I ../include/ -O2 -include ../include/types.h
+NUCLEUSCFLAGS=
+
+include ../cp/scripts/Makefile.commands
+
+all: eckd.rto loader.rto
+
+clean:
+	rm -f *.o
+	rm -f *.rto
+
+loader.o: setmode.o loader_c.o loader_asm.o
+	$(call link-ipl,$^,$@)
+
+%.o: %.c
+	$(call c-to-o,$<,$@)
+
+%.rto: %.o
+	$(call objcopy-tdr,$<,$@)
+
+%.o: %.S
+	$(call s-to-o,$<,$@)
+
+.PRECIOUS: %.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/eckd.S	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,76 @@
+# During the system IPL, 24 bytes are read from the device.
+#
+# NOTE: zArch IPLs in ESA/390 mode.
+#
+
+#
+# Bytes 0-7 contain PSW to be loaded after IO operation completes
+#
+	.byte	0x00
+		#   bits  value   name                        desc
+		#      0      0   <zero>
+		#      1      0   PER Mask (R)                disabled
+		#    2-4      0   <zero>
+		#      5      0   DAT Mode (T)                disabled
+		#      6      0   I/O Mask (IO)               disabled
+		#      7      0   External Mask (EX)          disabled
+
+	.byte	0x08
+		#   bits  value   name                        desc
+		#   8-11      0   Key
+		#     12      1   <one>
+		#     13      0   Machine-Check Mask (M)      disabled
+		#     14      0   Wait State (W)              executing
+		#     15      0   Problem State (P)           supervisor state
+		
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  16-17      0   Address-Space Control (AS)  disabled
+		#  18-19      0   Condition Code (CC)
+		#  20-23      0   Program Mask                exceptions disabled
+
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  24-30      0   <zero>
+		#     31      0   Extended Addressing (EA)    ! 64 mode
+
+	.byte	0x80	# bits 32-39
+	.byte	0x80	# bits 40-47
+	.byte	0x00	# bits 48-55
+	.byte	0x00	# bits 56-63
+		#   bits  value   name                        desc
+		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
+		#  33-63   addr   Instruction Address         Address to exec
+
+#
+# The remaining 16 bytes should contain CCW to read data from device
+#
+
+# CCW format-0:
+#   bits  name
+#    0-7  Cmd Code
+#   8-31  Data Address
+#     32  Chain-Data (CD)
+#     33  Chain-Command (CC)
+#     34  Sup.-Len.-Inditcation (SLI)
+#     35  Skip (SKP)
+#     36  Prog.-Contr.-Inter. (PCI)
+#     37  Indir.-Data-Addr. (IDA)
+#     38  Suspend (S)
+#     39  Modified I.D.A. (MIDA)
+#  40-47  <ignored>
+#  48-63  number of bytes to read
+
+#
+# CCW 1 (bytes 8-15): format-0
+#
+	# READ DATA 4 kB to 0x800000
+	.byte	0x86, 0x80, 0x00, 0x00
+	.byte	0x00, 0x00, 0x10, 0x00
+
+#
+# CCW 2 (bytes 16-23): format-0
+#
+	# invalid (unused)
+	.byte	0x00, 0x00, 0x00, 0x00
+	.byte	0x00, 0x00, 0x00, 0x00
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/layout.txt	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,8 @@
+dasd layout:
+
+0,0,1		eckd.S
+0,0,2		setmode.S
+		loader.c
+		loader_asm.S
+
+<on fs>		hvf.elf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/linker.script	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,10 @@
+SECTIONS
+{
+  ENTRY(MAIN)
+  . = 0x800000;
+  .text : { *(.text) }
+  .data : { *(.data) }
+  .rodata : { *(.rodata) }
+  .rodata.str1.2 : { *(.rodata.str1.2) }
+  .bss : { *(.bss) }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/loader.h	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,167 @@
+#ifndef __LOADER_H
+#define __LOADER_H
+
+/*
+ * "Config" values
+ */
+#define TEMP_BASE	((unsigned char*) 0x400000) /* 4MB */
+
+/* 3390 with 0 key length, and 4096 data length */
+#define RECORDS_PER_CYL		(15*12)
+#define RECORDS_PER_TRACK	12
+
+/* the nucleus fn ft (in EBCDIC) => HVF ELF */
+#define CP_FN	"\xC8\xE5\xC6\x40\x40\x40\x40\x40"
+#define CP_FT	"\xC5\xD3\xC6\x40\x40\x40\x40\x40"
+
+/*
+ * string manipulation
+ */
+#define memcpy(d,s,l)	__builtin_memcpy((d), (s), (l))
+#define memcmp(d,s,l)	__builtin_memcmp((d), (s), (l))
+#define memset(s,c,n)	__builtin_memset((s),(c),(n))
+
+/*
+ * I/O related structs, macros & variables
+ */
+struct ccw {
+	u8 cmd;			/* Command code */
+	u8 flags;		/* Flags */
+	u16 count;		/* Count */
+	u32 addr;		/* Data Address */
+} __attribute__((packed,aligned(8)));
+
+#define CCW_FLAG_CD		0x80	/* Chain-Data */
+#define CCW_FLAG_CC		0x40	/* Chain-Command */
+#define CCW_FLAG_SLI		0x20	/* Suppress-Length-Indication */
+#define CCW_FLAG_SKP		0x10	/* Skip */
+#define CCW_FLAG_PCI		0x08	/* Program-Controlled-Interruption */
+#define CCW_FLAG_IDA		0x04	/* Indirect-Data-Address */
+#define CCW_FLAG_S		0x02	/* Suspend */
+#define CCW_FLAG_MIDA		0x01	/* Modified-Indirect-Data-Address */
+
+struct orb {
+	/* word 0 */
+	u32 param;		/* Interruption Parameter */
+
+	/* word 1 */
+	u8 key:4,		/* Subchannel Key */
+	   s:1,			/* Suspend */
+	   c:1,			/* Streaming-Mode Control */
+	   m:1,			/* Modification Control */
+	   y:1;			/* Synchronization Control */
+	u8 f:1,			/* Format Control */
+	   p:1,			/* Prefetch Control */
+	   i:1,			/* Initial-Status-Interruption Control */
+	   a:1,			/* Address-Limit-Checking control */
+	   u:1,			/* Suppress-Suspend-Interruption Control */
+	   __zero1:1,
+	   h:1,			/* Format-2-IDAW Control */
+	   t:1;			/* 2K-IDAW Control */
+	u8 lpm;			/* Logical-Path Mask */
+	u8 l:1,			/* Incorrect-Length-Suppression Mode */
+	   d:1,			/* Modified-CCW-Indirect-Data-Addressing Control */
+	   __zero2:5,
+	   x:1;			/* ORB-Extension Control */
+
+	/* word 2 */
+	u32 addr;		/* Channel-Program Address */
+
+	/* word 3 */
+	u8 css_prio;		/* Channel-Subsystem Priority */
+	u8 __reserved1;
+	u8 cu_prio;		/* Control-Unit Priority */
+	u8 __reserved2;
+
+	/* word 4 - 7 */
+	u32 __reserved3;
+	u32 __reserved4;
+	u32 __reserved5;
+	u32 __reserved6;
+} __attribute__((packed,aligned(4)));
+
+#define ADDR31(x)	((u32) (u64) (x))
+
+extern void __do_io();
+extern void PGMHANDLER();
+
+extern void load_nucleus(void);
+
+extern struct orb ORB;
+
+/*
+ * EDF related structs & macros
+ */
+#define EDF_LABEL_BLOCK_NO		3
+#define EDF_SUPPORTED_BLOCK_SIZE	4096
+
+struct ADT {
+	u32     IDENT;       /* VOL START / LABEL IDENTIFIER */
+#define __ADTIDENT 0xC3D4E2F1   /* 'CMS1' in EBCDIC */
+	u8      ID[6];       /* VOL START / VOL IDENTIFIER */
+	u8      VER[2];      /* VERSION LEVEL */
+	u32     DBSIZ;       /* DISK BLOCK SIZE */
+	u32     DOP;         /* DISK ORIGIN POINTER */
+	u32     CYL;         /* NUM OF FORMATTED CYL ON DISK */
+	u32     MCYL;        /* MAX NUM FORMATTED CYL ON DISK */
+	u32     NUM;         /* Number of Blocks on disk */
+	u32     USED;        /* Number of Blocks used */
+	u32     FSTSZ;       /* SIZE OF FST */
+	u32     NFST;        /* NUMBER OF FST'S PER BLOCK */
+	u8      DCRED[6];    /* DISK CREATION DATE (YYMMDDHHMMSS) */
+	u8      FLGL;        /* LABEL FLAG BYTE (ADTFLGL) */
+#define ADTCNTRY        0x01    /* Century for disk creation date (0=19, 1=20),
+	                         * corresponds to ADTDCRED. */
+	u8      reserved[1];
+	u32     OFFST;       /* DISK OFFSET WHEN RESERVED */
+	u32     AMNB;        /* ALLOC MAP BLOCK WITH NEXT HOLE */
+	u32     AMND;        /* DISP INTO HBLK DATA OF NEXT HOLE */
+	u32     AMUP;        /* DISP INTO USER PART OF ALLOC MAP */
+	u32     OFCNT;       /* Count of SFS open files for this ADT */
+	u8      SFNAM[8];    /* NAME OF SHARED SEGMENT */
+};
+
+struct FST {
+	u8    FNAME[8];       /* filename */
+	u8    FTYPE[8];       /* filetype */
+	u8    DATEW[2];       /* DATE LAST WRITTEN - MMDD */
+	u8    TIMEW[2];       /* TIME LAST WRITTEN - HHMM */
+	u16   WRPNT;          /* WRITE POINTER - ITEM NUMBER */
+	u16   RDPNT;          /* READ POINTER - ITEM NUMBER */
+	u8    FMODE[2];       /* FILE MODE - LETTER AND NUMBER */
+	u16   RECCT;          /* NUMBER OF LOGICAL RECORDS */
+	u16   FCLPT;          /* FIRST CHAIN LINK POINTER */
+	u8    RECFM;          /* F*1 - RECORD FORMAT - F OR V */
+#define FSTDFIX        0xC6 /* Fixed record format (EBCDIC 'F') */
+#define FSTDVAR        0xE5 /* Variable record format (EBCDIC 'V') */
+	u8    FLAGS;          /* F*2 - FST FLAG BYTE */
+#define FSTRWDSK       0x80 /* READ/WRITE DISK */
+#define FSTRODSK       0x00 /* READ/ONLY DISK */
+#define FSTDSFS        0x10 /* Shared File FST */
+#define FSTXRDSK       0x40 /* EXTENSION OF R/O DISK */
+#define FSTXWDSK       0xC0 /* EXTENSION OF R/W DISK */
+#define FSTEPL         0x20 /* EXTENDED PLIST */
+#define FSTDIA         0x40 /* ITEM AVAILABLE */
+#define FSTDRA         0x01 /* PREVIOUS RECORD NULL */
+#define FSTCNTRY       0x08 /* Century for date last written (0=19, 1=20),\\
+			       corresponds to FSTYEARW, FSTADATI. */
+#define FSTACTRD       0x04 /* ACTIVE FOR READING */
+#define FSTACTWR       0x02 /* ACTIVE FOR WRITING */
+#define FSTACTPT       0x01 /* ACTIVE FROM A POINT */
+#define FSTFILEA       0x07 /* THE FILE IS ACTIVE */
+	u32   LRECL;          /* LOGICAL RECORD LENGTH */
+	u16   BLKCT;          /* NUMBER OF 800 BYTE BLOCKS */
+	u16   YEARW;          /* YEAR LAST WRITTEN */
+	u32   FOP;            /* ALT. FILE ORIGIN POINTER */
+	u32   ADBC;           /* ALT. NUMBER OF DATA BLOCKS */
+	u32   AIC;            /* ALT. ITEM COUNT */
+	u8    NLVL;           /* NUMBER OF POINTER BLOCK LEVELS */
+	u8    PTRSZ;          /* LENGTH OF A POINTER ELEMENT */
+	u8    ADATI[6];       /* ALT. DATE/TIME(YY MM DD HH MM SS) */
+	u8    REALM;          /* Real filemode */
+	u8    FLAG2;          /* F*3 - FST FLAG BYTE 2 FSTFLAG2 */
+#define FSTPIPEU       0x10 /* Reserved for CMS PIPELINES usage */
+	u8    reserved[2];
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/loader_asm.S	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,249 @@
+#
+# Copyright (c) 2007 Josef 'Jeff' Sipek
+#
+
+#include "loader.h"
+
+.text
+	.align	4
+.globl __do_io
+	.type	__do_io, @function
+__do_io:
+	#
+	# r4 = 0x80000000
+	#
+	XGR	%r4, %r4
+	LHI	%r4, 0x8
+	SLL	%r4, 20
+	#
+	# r14 = r14 & 0x7fffffff don't ask, it's strangely retarded
+	#
+	L	%r1,ADDRMASK(%r4)
+	NR	%r14, %r1  # mask out the bit
+
+	# set up the interrupt handler
+	MVC	0x1f0(16),IOPSW(%r4)	# SET NEW IO PSW
+	LA	%r1,IOHANDLER(%r4)	# GET HANDLER ADDRESS
+	STG	%r1,0x1f0+8		# SAVE IN NEW PSW
+
+	L	%r1, 0xb8		# load subsystem ID
+	
+	SSCH	ORB(%r4)		# issue IO
+
+	# Load Control Register 6 with I/O interrupt subclass mask
+	STCTG	6,6,TMPVAR(%r4)		# GET CR6
+	OI	TMPVAR+4(%r4),0xFF	# enable all
+	LCTLG	6,6,TMPVAR(%r4)		# RELOAD MODIFIED CR6
+
+/*
+7) Enable the PSW for I/O interrupts and go into wait state (you need bits 6, 12 & 14 set to 1 in the PSW : X'020A000000000000' is a good example)
+*/
+	LPSWE	WAITPSW(%r4)
+
+#
+# The IO interrupt handler
+#
+.globl IOHANDLER
+IOHANDLER:
+	# is this for us?
+	L	%r1, MAGICVAL(%r4)
+	C	%r1, 0xbc
+	BNE	IONOTDONE(%r4)
+
+	# it is!
+
+	L	%r1, 0xb8		# load subsystem ID
+
+	TSCH	IRB(%r4)
+
+/*
+FIXME: we should do more checking!
+
+11) If Unit check or Channel Status|=0 : An I/O error occurred and act accordingly
+12) If unit exception : End of media (for tape & cards) and act accordingly
+13) If device end : I/O Completed.. Perform post I/O stuff (like advancing your pointers) and back to step 3
+*/
+
+	# unit check? (end of media?)
+	L	%r1,IRB+5(%r4)
+	LA	%r0,0x02
+	NR	%r0,%r1
+	LA	%r2,1			# return 1 - end of medium
+	BCR	4,%r14			# unit chk => return
+
+	# check the SCSW.. If CE Only : LPSW Old I/O PSW
+	LA	%r0,0x04
+	NR	%r0,%r1
+	LA	%r2,0			# means IO done
+	BCR	4,%r14			# device end => return
+
+IONOTDONE:
+	LPSWE	0x170
+
+#
+# The PGM interrupt handler
+#
+.globl PGMHANDLER
+PGMHANDLER:
+	STMG	%r1,%r3,0x200
+
+	# r3 = 0x80000000
+	XGR	%r3, %r3
+	LHI	%r3, 0x8
+	SLL	%r3, 20
+
+	# is ILC == 3?
+	LGH	%r2,0x8C
+	CGHI	%r2,0x0006
+	BNE	ERR
+
+	# grab the old PSW address, subtract length of TPROT, and compare it
+	# with the TPROT opcode (0xe501)
+	LG	%r1,0x158
+	AGHI	%r1,-6
+	LLGH	%r2,TPROTOP(%r3)
+	LLGH	%r1,0(%r1)
+	CGR	%r2,%r1
+	BNE	ERR(%r3)
+
+	# set CC=3
+	OI	0x152,0x30
+
+	LMG	%r1,%r3,0x200
+
+	LPSWE	0x150
+
+ERR:
+.byte	0x00, 0x00
+
+
+#
+# Useful data
+#
+.data
+.globl TPROTOP
+TPROTOP:
+.byte	0xe5, 0x01
+
+	.align 8
+.globl IOPSW
+IOPSW:
+	.byte	0x00
+		#   bits  value   name                        desc
+		#      0      0   <zero>
+		#      1      0   PER Mask (R)                disabled
+		#    2-4      0   <zero>
+		#      5      0   DAT Mode (T)                disabled
+		#      6      0   I/O Mask (IO)               enabled
+		#      7      0   External Mask (EX)          disabled
+
+	.byte	0x00
+		#   bits  value   name                        desc
+		#   8-11      0   Key
+		#     12      0   <one>
+		#     13      0   Machine-Check Mask (M)      disabled
+		#     14      0   Wait State (W)              executing
+		#     15      0   Problem State (P)           supervisor state
+		
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  16-17      0   Address-Space Control (AS)  disabled
+		#  18-19      0   Condition Code (CC)
+		#  20-23      0   Program Mask                exceptions disabled
+
+	.byte	0x01
+		#   bits  value   name                        desc
+		#  24-30      0   <zero>
+		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
+
+	.byte	0x80
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+		#   bits  value   name                        desc
+		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
+		#  33-63      0   <zero>
+		# 64-127   addr   Instruction Address         Address to exec
+
+.globl WAITPSW
+WAITPSW:
+	.byte	0x02
+		#   bits  value   name                        desc
+		#      0      0   <zero>
+		#      1      0   PER Mask (R)                disabled
+		#    2-4      0   <zero>
+		#      5      0   DAT Mode (T)                disabled
+		#      6      1   I/O Mask (IO)               enabled
+		#      7      0   External Mask (EX)          disabled
+
+	.byte	0x02
+		#   bits  value   name                        desc
+		#   8-11      0   Key
+		#     12      0   <zero>
+		#     13      0   Machine-Check Mask (M)      disabled
+		#     14      1   Wait State (W)              not executing
+		#     15      0   Problem State (P)           supervisor state
+		
+	.byte	0x00
+		#   bits  value   name                        desc
+		#  16-17      0   Address-Space Control (AS)  disabled
+		#  18-19      0   Condition Code (CC)
+		#  20-23      0   Program Mask                exceptions disabled
+
+	.byte	0x01
+		#   bits  value   name                        desc
+		#  24-30      0   <zero>
+		#     31      1   Extended Addressing (EA)    EA + BA = 64 mode
+
+	.byte	0x80
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+	.byte	0x00
+		#   bits  value   name                        desc
+		#     32      1   Basic Addressing (BA)       BA = 31, !BA = 24
+		#  33-63      0   <zero>
+		# 64-127   addr   Instruction Address         Address to exec
+
+.globl TMPVAR
+TMPVAR:
+	.8byte 0x0
+
+.globl ADDRMASK
+ADDRMASK:
+	.4byte 0x7fffffff
+
+.globl MAGICVAL
+MAGICVAL:
+	.4byte 0x12345678
+
+.globl IRB
+IRB:
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+	.8byte 0x00
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/loader_c.c	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2007-2009 Josef 'Jeff' Sipek
+ */
+
+#include "loader.h"
+#include <binfmt_elf.h>
+
+static unsigned char seek_data[6];
+static unsigned char search_data[5];
+static struct ccw ccw[4];
+
+struct orb ORB = {
+	.param		= 0x12345678,
+	.f		= 1,
+	.lpm		= 0xff,
+	.addr		= 0xffffffff,
+};
+
+static u8 *buf = (u8*) (16 * 1024);
+static u32 *ptrbuf = (u32*) (20 * 1024);
+
+/*
+ * halt the cpu
+ *
+ * NOTE: we don't care about not clobbering registers as when this
+ * code executes, the CPU will be stopped.
+ */
+static inline void die(void)
+{
+	asm volatile(
+		"SR	%r1, %r1	# not used, but should be zero\n"
+		"SR	%r3, %r3 	# CPU Address\n"
+		"SIGP	%r1, %r3, 0x05	# Signal, order 0x05\n"
+	);
+
+	/*
+	 * Just in case SIGP fails
+	 */
+	for(;;);
+}
+
+static u64 pgm_new_psw[2] = {
+	0x0000000180000000ULL, (u64) &PGMHANDLER,
+};
+
+/*
+ * determine amount of storage
+ */
+static u64 sense_memsize(void)
+{
+	u64 size;
+	int cc;
+
+#define SKIP_SIZE	(1024*1024ULL)
+
+	/* set new PGM psw */
+	memcpy((void*)0x1d0, pgm_new_psw, 16);
+
+	for(size = 0; size < ((u64)~SKIP_SIZE)-1; size += SKIP_SIZE) {
+		asm volatile(
+			"lg	%%r1,%1\n"
+			"tprot	0(%%r1),0\n"
+			"ipm	%0\n"
+			"srl    %0,28\n"
+		: /* output */
+		  "=d" (cc)
+		: /* input */
+		  "m" (size)
+		: /* clobber */
+		  "cc", "r1"
+		);
+
+		/*
+		 * we cheat here a little...if we try to tprot a location
+		 * that isn't part of the configuration, a program exception
+		 * fires off, but our handler sets the CC to 3, and resumes
+		 * execution
+		 */
+		if (cc == 3)
+			break;
+	}
+
+	/* invalidate new PGM psw */
+	memset((void*)0x1d0, 0, 16);
+
+	return size;
+}
+
+static void read_blk(void *ptr, u32 lba)
+{
+	u16 cc, hh, r;
+
+	if (lba < 1)
+		die();
+
+	memset(ccw, 0, sizeof(ccw));
+
+	lba--;
+	cc = lba / RECORDS_PER_CYL;
+	hh = (lba % RECORDS_PER_CYL) / RECORDS_PER_TRACK;
+	r = (lba % RECORDS_PER_CYL) % RECORDS_PER_TRACK;
+	r++;
+
+	ORB.addr = ADDR31(ccw);
+
+	/* SEEK */
+	ccw[0].cmd = 0x07;
+	ccw[0].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
+	ccw[0].count = 6;
+	ccw[0].addr = ADDR31(seek_data);
+
+	seek_data[0] = 0;		/* zero */
+	seek_data[1] = 0;		/* zero */
+	seek_data[2] = cc >> 8;		/* Cc */
+	seek_data[3] = cc & 0xff;	/* cC */
+	seek_data[4] = hh >> 8;		/* Hh */
+	seek_data[5] = hh & 0xff;	/* hH */
+
+	/* SEARCH */
+	ccw[1].cmd = 0x31;
+	ccw[1].flags = CCW_FLAG_CC | CCW_FLAG_SLI;
+	ccw[1].count = 5;
+	ccw[1].addr = ADDR31(search_data);
+
+	search_data[0] = cc >> 8;
+	search_data[1] = cc & 0xff;
+	search_data[2] = hh >> 8;
+	search_data[3] = hh & 0xff;
+	search_data[4] = r;
+
+	/* TIC */
+	ccw[2].cmd = 0x08;
+	ccw[2].flags = 0;
+	ccw[2].count = 0;
+	ccw[2].addr = ADDR31(&ccw[1]);
+
+	/* READ DATA */
+	ccw[3].cmd = 0x86;
+	ccw[3].flags = 0;
+	ccw[3].count = 4096;
+	ccw[3].addr = ADDR31(ptr);
+
+	/*
+	 * issue IO
+	 */
+	__do_io();
+}
+
+/*
+ * read the entire nucleus into TEMP_BASE
+ */
+static inline void readnucleus(void)
+{
+	struct ADT *ADT = (struct ADT*) buf;
+	struct FST *FST = (struct FST*) buf;
+	struct FST fst;
+	int i, found;
+	u32 nfst;
+
+	read_blk(buf, EDF_LABEL_BLOCK_NO);
+
+	if ((ADT->IDENT != __ADTIDENT) ||
+	    (ADT->DBSIZ != EDF_SUPPORTED_BLOCK_SIZE) ||
+	    (ADT->OFFST != 0) ||
+	    (ADT->FSTSZ != sizeof(struct FST)))
+		die();
+
+	nfst = ADT->NFST;
+
+	read_blk(buf, ADT->DOP);
+
+	if (FST->NLVL != 0)
+	       die(); // FIXME
+
+	for(i=0,found=0; i<nfst; i++) {
+		if ((!memcmp(FST[i].FNAME, CP_FN, 8)) &&
+		    (!memcmp(FST[i].FTYPE, CP_FT, 8))) {
+			memcpy(&fst, &FST[i], sizeof(struct FST));
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		die();
+
+	if (fst.PTRSZ != 4 ||
+	    fst.LRECL != 4096 ||
+	    fst.RECFM != FSTDFIX)
+		die();
+
+	/* Don't allow more than 3MB to be read */
+	if ((FST->AIC * FST->LRECL) > (3ULL * 1024 * 1024))
+		die();
+
+	/* Since we're assuming that NLVL==1, there's only 1 pointer block */
+	read_blk(ptrbuf, fst.FOP);
+
+	/* Read all the blocks pointed to by the ptr block */
+	for(i=0; i<fst.AIC; i++)
+		read_blk(TEMP_BASE + (4096 * i), ptrbuf[i]);
+}
+
+void load_nucleus(void)
+{
+	/*
+	 * These are all stored in registers
+	 */
+	register u32 iplsch;
+	register int i;
+	register Elf64_Ehdr *nucleus_elf;
+	register Elf64_Shdr *section;
+	register void (*start_sym)(u64, u32);
+
+	/* Save the IPL device subchannel id */
+	iplsch = *((u32*) 0xb8);
+
+	/*
+	 * Read entire ELF to temporary location
+	 */
+	readnucleus();
+
+	nucleus_elf = (Elf64_Ehdr*) TEMP_BASE;
+
+	/*
+	 * Check that this looks like a valid ELF
+	 */
+	if (nucleus_elf->e_ident[0] != '\x7f' ||
+	    nucleus_elf->e_ident[1] != 'E' ||
+	    nucleus_elf->e_ident[2] != 'L' ||
+	    nucleus_elf->e_ident[3] != 'F' ||
+	    nucleus_elf->e_ident[EI_CLASS] != ELFCLASS64 ||
+	    nucleus_elf->e_ident[EI_DATA] != ELFDATA2MSB ||
+	    nucleus_elf->e_ident[EI_VERSION] != EV_CURRENT ||
+	    nucleus_elf->e_type != ET_EXEC ||
+	    nucleus_elf->e_machine != 0x16 || // FIXME: find the name for the #define
+	    nucleus_elf->e_version != EV_CURRENT)
+		die();
+
+	/*
+	 * Iterate through each section, and copy it to the final
+	 * destination as necessary
+	 */
+	for (i=0; i<nucleus_elf->e_shnum; i++) {
+		section = (Elf64_Shdr*) (TEMP_BASE +
+					 nucleus_elf->e_shoff +
+					 nucleus_elf->e_shentsize * i);
+
+		switch (section->sh_type) {
+			case SHT_PROGBITS:
+				if (!section->sh_addr)
+					break;
+
+				/*
+				 * just copy the data from TEMP_BASE to
+				 * where it wants to be
+				 */
+				memcpy((void*) section->sh_addr,
+					TEMP_BASE + section->sh_offset,
+					section->sh_size);
+				break;
+			case SHT_NOBITS:
+				/*
+				 * No action needed as there's no data to
+				 * copy, and we assume that the ELF sections
+				 * don't overlap
+				 */
+				memset((void*) section->sh_addr,
+				       0, section->sh_size);
+				break;
+			case SHT_SYMTAB:
+			case SHT_STRTAB:
+				/* TODO: relocate */
+				break;
+			default:
+				/* Ignoring */
+				break;
+		}
+	}
+
+	/*
+	 * Now, jump to the nucleus entry point
+	 */
+	start_sym = (void*) nucleus_elf->e_entry;
+	start_sym(sense_memsize(), iplsch);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loader/setmode.S	Wed Apr 20 20:50:32 2011 -0400
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2007-2009 Josef 'Jeff' Sipek
+#
+
+.globl MAIN
+	.type	MAIN, @function
+MAIN:
+#
+# At this point, the machine is running in ESA390 mode. Let's load a new
+# PSW, making it switch to 64-bit mode
+#
+
+	# switch to 64-bit mode
+	#
+	# Signal Processor
+	#   Order 0x12: Set Architecture
+	#   R1 bits 56-63 = 0x01 (switch all CPUs to z/Arch)
+	SR	%r1, %r1
+	LHI	%r1, 0x1	# switch all to z/Arch
+	SR	%r3, %r3	# CPU Address (CPU0000)
+	SIGP	%r1, %r3, 0x12	# Signal, order 0x12
+	SAM64
+	# On error:
+	#   Bit 55 = 1, cc1 (inval param)
+	#   Bit 54 = 1, cc1 (incorrect state)
+
+	# FIXME: check for errors?
+
+#
+# At this point, we should be in 64-bit mode
+#
+
+	#
+	# It is unfortunate that the below code is required.
+	#
+	# Let's set the stack pointer to make gcc happy
+	#
+	# A standard stack frame is 160 bytes.
+	#
+	# NOTE: Once this thread of execution turns into the idle thread,
+	# the stack can be reused for something else. Since it's allocated
+	# in the 128th processor's PSA, we have to make sure that it won't
+	# get initialized until after the idle thread kicks in.
+	#
+
+	# r15 = 0x100000
+	#     = (1 << 20)
+	#
+	SR	%r15, %r15
+	LHI	%r15, 0x1
+	SLL	%r15, 20
+	AHI	%r15, -160
+
+	BRC	15,load_nucleus