Initial commit.
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..346c798
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,230 @@
+#include <stdalign.h>
+#include <stdatomic.h>
+#include <stddef.h>
+
+#include "cpio.h"
+#include "cpu.h"
+#include "dlog.h"
+#include "fdt.h"
+#include "irq.h"
+#include "std.h"
+#include "timer.h"
+#include "vm.h"
+
+void *fdt;
+
+/* The stack to be used by the CPUs. */
+alignas(2 * sizeof(size_t)) char callstacks[STACK_SIZE * MAX_CPUS];
+
+/* State of all supported CPUs. The stack of the first one is initialized. */
+struct cpu cpus[MAX_CPUS] = {
+	{
+		.cpu_on_count = 1,
+		.stack_bottom = callstacks + STACK_SIZE,
+	},
+};
+
+bool fdt_find_node(struct fdt_node *node, const char *path)
+{
+	if (!fdt_find_child(node, ""))
+		return false;
+
+	while (*path) {
+		if (!fdt_find_child(node, path))
+			return false;
+		path += strlen(path);
+	}
+
+	return true;
+}
+
+bool fdt_read_number(struct fdt_node *node, const char *name, uint64_t *value)
+{
+	const char *data;
+	uint32_t size;
+	union {
+		volatile uint64_t v;
+		char a[8];
+	} t;
+
+	if (!fdt_read_property(node, name, &data, &size))
+		return false;
+
+	switch (size) {
+	case sizeof(uint32_t):
+		*value = ntohl(*(uint32_t *)data);
+		break;
+
+	case sizeof(uint64_t):
+		memcpy(t.a, data, sizeof(uint64_t));
+		*value = ntohll(t.v);
+		break;
+
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+bool fdt_write_number(struct fdt_node *node, const char *name, uint64_t value)
+{
+	const char *data;
+	uint32_t size;
+	union {
+		volatile uint64_t v;
+		char a[8];
+	} t;
+
+	if (!fdt_read_property(node, name, &data, &size))
+		return false;
+
+	switch (size) {
+	case sizeof(uint32_t):
+		*(uint32_t *)data = ntohl(value);
+		break;
+
+	case sizeof(uint64_t):
+		t.v = ntohll(value);
+		memcpy((void *)data, t.a, sizeof(uint64_t));
+		break;
+
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+static void relocate(const char *from, size_t size)
+{
+	extern char bin_end[];
+	size_t tmp = (size_t)&bin_end[0];
+	char *dest = (char *)((tmp + 0x80000 - 1) & ~(0x80000 - 1));
+	dlog("bin_end is at %p, copying to %p\n", &bin_end[0], dest);
+	memcpy(dest, from, size);
+}
+
+/* TODO: Remove this. */
+struct vm vm0;
+
+static void one_time_init(void)
+{
+	size_t i;
+
+	dlog("Initializing hafnium\n");
+
+	/*
+	 * TODO: Re-enable this.
+	irq_init();
+	timer_init();
+	*/
+
+	/* Initialize all CPUs. */
+	for (i = 0; i < MAX_CPUS; i++) {
+		struct cpu *c = cpus + i;
+		cpu_init(c);
+		c->id = i; /* TODO: Initialize ID. */
+		c->stack_bottom = callstacks + STACK_SIZE * (i + 1);
+	}
+
+	/* TODO: Code below this point should be removed from this function. */
+	/* TODO: Remove this. */
+
+	do {
+		struct fdt_node n;
+
+		fdt_root_node(&n, fdt);
+		if (!fdt_find_node(&n, "chosen\0")) {
+			dlog("Unable to find 'chosen'\n");
+			break;
+		}
+
+		uint64_t begin;
+		uint64_t end;
+
+		if (!fdt_read_number(&n, "linux,initrd-start", &begin)) {
+			dlog("Unable to read linux,initrd-start\n");
+			break;
+		}
+
+		if (!fdt_read_number(&n, "linux,initrd-end", &end)) {
+			dlog("Unable to read linux,initrd-end\n");
+			break;
+		}
+
+		dlog("Ramdisk: from %x to %x\n", begin, end);
+
+		struct cpio c;
+		struct cpio_iter iter;
+		cpio_init(&c, (void *)begin, end - begin);
+		cpio_init_iter(&c, &iter);
+
+		const char *name;
+		const void *fcontents;
+		size_t ramdisk = 0;
+		size_t ramdisk_end = 0;
+		size_t fsize;
+		while (cpio_next(&iter, &name, &fcontents, &fsize)) {
+			dlog("File: %s, size=%u\n", name, fsize);
+			if (!strcmp(name, "vm/vmlinuz")) {
+				relocate(fcontents, fsize);
+				continue;
+			}
+
+			if (!strcmp(name, "vm/initrd.img")) {
+				dlog("Found vm/ramdisk @ %p, %u bytes\n", fcontents, fsize);
+				ramdisk = (size_t)fcontents;
+				ramdisk_end = ramdisk + fsize;
+				continue;
+			}
+		}
+
+		dlog("Ramdisk; %p\n", ramdisk);
+
+		/* Patch FDT to point to new ramdisk. */
+		if (!fdt_write_number(&n, "linux,initrd-start", ramdisk)) {
+			dlog("Unable to write linux,initrd-start\n");
+			break;
+		}
+
+		if (!fdt_write_number(&n, "linux,initrd-end", ramdisk_end)) {
+			dlog("Unable to write linux,initrd-end\n");
+			break;
+		}
+
+		/*
+		 * Patch fdt to point remove memory.
+		 */
+		{
+			size_t tmp = (size_t)&relocate;
+			tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
+
+
+			fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
+			vm_init(&vm0, cpus);
+			vm_start_vcpu(&vm0, 0, tmp, (size_t)fdt);
+		}
+	} while (0);
+}
+
+/*
+ * The entry point of CPUs when they are turned on. It is supposed to initialise
+ * all state and return; the caller will ensure that the next vcpu runs.
+ */
+void cpu_main(void)
+{
+	/* Do global one-time initialization just once. */
+	static atomic_flag inited = ATOMIC_FLAG_INIT;
+	if (!atomic_flag_test_and_set_explicit(&inited, memory_order_acq_rel))
+		one_time_init();
+
+	dlog("Starting up cpu %d\n", cpu() - cpus);
+
+	/* Do per-cpu initialization. */
+	/* TODO: What to do here? */
+	/*
+	irq_init_percpu();
+	timer_init_percpu();
+	*/
+}