Initial commit.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5318d5b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+KERNEL_PATH ?= ../linux
+
+obj-m += hafnium.o
+
+hafnium-y += main.o
+hafnium-y += hvc.o
+
+all:
+	make -C $(KERNEL_PATH) M=$(PWD) modules
+
+clean:
+	make -C $(KERNEL_PATH) M=$(PWD) clean
diff --git a/hvc.S b/hvc.S
new file mode 100644
index 0000000..bec077b
--- /dev/null
+++ b/hvc.S
@@ -0,0 +1,6 @@
+.text
+
+.global hf_hvc
+hf_hvc:
+	hvc #0
+	ret
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..973bd97
--- /dev/null
+++ b/main.c
@@ -0,0 +1,282 @@
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/sched/task.h>
+#include <linux/slab.h>
+
+struct hf_vcpu {
+	spinlock_t lock;
+	long vm_index;
+	long vcpu_index;
+	struct task_struct *task;
+	struct hrtimer timer;
+	bool pending_irq;
+};
+
+struct hf_vm {
+	long vcpu_count;
+	struct hf_vcpu *vcpu;
+};
+
+long hf_hvc(size_t arg0, size_t arg1, size_t arg2, size_t arg3);
+
+static struct hf_vm *hf_vms;
+static long hf_vm_count;
+
+
+/* TODO: Define constants below according to spec. Include shared header. */
+#define HF_VCPU_RUN       0xff00
+#define HF_VM_GET_COUNT   0xff01
+#define HF_VCPU_GET_COUNT 0xff02
+
+/**
+ * Wakes up the thread associated with the vcpu that owns the given timer. This
+ * is called when the timer the thread is waiting on expires.
+ */
+static enum hrtimer_restart hf_vcpu_timer_expired(struct hrtimer *timer)
+{
+	struct hf_vcpu *vcpu = container_of(timer, struct hf_vcpu, timer);
+	wake_up_process(vcpu->task);
+	return HRTIMER_NORESTART;
+}
+
+/**
+ * This is the main loop of each vcpu.
+ */
+static int hf_vcpu_thread(void *data)
+{
+	struct hf_vcpu *vcpu = data;
+	long ret;
+
+	hrtimer_init(&vcpu->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	vcpu->timer.function = &hf_vcpu_timer_expired;
+
+	while (!kthread_should_stop()) {
+		unsigned long flags;
+		size_t irqs;
+
+		set_current_state(TASK_RUNNING);
+
+		/* Determine if we must interrupt the vcpu. */
+		spin_lock_irqsave(&vcpu->lock, flags);
+		irqs = vcpu->pending_irq ? 1 : 0;
+		vcpu->pending_irq = false;
+		spin_unlock_irqrestore(&vcpu->lock, flags);
+
+		/* Call into hafnium to run vcpu. */
+		ret = hf_hvc(HF_VCPU_RUN, vcpu->vm_index, vcpu->vcpu_index,
+			     irqs);
+
+		/* A negative return value indicates that this vcpu needs to
+		 * sleep for the given number of nanoseconds.
+		 */
+		if (ret < 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (kthread_should_stop())
+				break;
+			hrtimer_start(&vcpu->timer, -ret, HRTIMER_MODE_REL);
+			schedule();
+			hrtimer_cancel(&vcpu->timer);
+			continue;
+		}
+
+		/* TODO: Use constants below. */
+		switch ((u8)ret) {
+		case 0x0: /* Yield (forcibly or voluntarily). */
+			break;
+
+		case 0x01: /* WFI. */
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (kthread_should_stop())
+				break;
+			schedule();
+			break;
+
+		case 0x02: /* Wake up another vcpu. */
+			{
+				struct hf_vm *vm = hf_vms + vcpu->vm_index;
+				long target = ret >> 8;
+				if (target < vm->vcpu_count)
+					wake_up_process(vm->vcpu[target].task);
+			}
+			break;
+		}
+	}
+
+	set_current_state(TASK_RUNNING);
+
+	return 0;
+}
+
+/**
+ * Frees all resources, including threads, associated with the hafnium driver.
+ */
+static void hf_free_resources(long vm_count)
+{
+	long i, j;
+
+	/*
+	 * First stop all worker threads. We need to do this before freeing
+	 * resources because workers may reference each other, so it is only
+	 * safe to free resources after they have all stopped.
+	 */
+	for (i = 0; i < vm_count; i++) {
+		struct hf_vm *vm = hf_vms + i;
+		for (j = 0; j < vm->vcpu_count; j++)
+			kthread_stop(vm->vcpu[j].task);
+	}
+
+	/* Free resources. */
+	for (i = 0; i < vm_count; i++) {
+		struct hf_vm *vm = hf_vms + i;
+		for (j = 0; j < vm->vcpu_count; j++)
+			put_task_struct(vm->vcpu[j].task);
+		kfree(vm->vcpu);
+	}
+
+	kfree(hf_vms);
+}
+
+static ssize_t hf_interrupt_store(struct kobject *kobj,
+				  struct kobj_attribute *attr, const char *buf,
+				  size_t count)
+{
+	struct hf_vcpu *vcpu;
+	unsigned long flags;
+	struct task_struct *task;
+
+	/* TODO: Parse input to determine which vcpu to interrupt. */
+	pr_info("Interrupting the first vcpu of the first vm\n");
+	/* TODO: Check bounds. */
+
+	vcpu = hf_vms[0].vcpu + 0;
+
+	spin_lock_irqsave(&vcpu->lock, flags);
+	vcpu->pending_irq = true;
+	/* TODO: Do we need to increment the task's ref count here? */
+	task = vcpu->task;
+	spin_unlock_irqrestore(&vcpu->lock, flags);
+
+	/* Wake up the task. If it's already running, kick it out. */
+	/* TODO: There's a race here: the kick may happen right before we go
+	 * to the hypervisor. */
+	if (wake_up_process(task) == 0)
+		kick_process(task);
+
+	return count;
+}
+
+static struct kobject *hf_sysfs_obj = NULL;
+static struct kobj_attribute interrupt_attr =
+	__ATTR(interrupt, 0200, NULL, hf_interrupt_store);
+
+/**
+ * Initializes the hafnium driver by creating a thread for each vCPU of each
+ * virtual machine.
+ */
+static int __init hf_init(void)
+{
+	long ret;
+	long i, j;
+
+	/* Get the number of VMs and allocate storage for them. */
+	ret = hf_hvc(HF_VM_GET_COUNT, 0, 0, 0);
+	if (ret < 0) {
+		pr_err("Unable to retrieve number of VMs: %ld\n", ret);
+		return ret;
+	}
+
+	hf_vm_count = ret;
+	hf_vms = kmalloc(sizeof(struct hf_vm) * hf_vm_count, GFP_KERNEL);
+	if (!hf_vms)
+		return -ENOMEM;
+
+	/* Initialize each VM. */
+	for (i = 0; i < hf_vm_count; i++) {
+		struct hf_vm *vm = hf_vms + i;
+
+		ret = hf_hvc(HF_VCPU_GET_COUNT, i, 0, 0);
+		if (ret < 0) {
+			pr_err("HF_VCPU_GET_COUNT failed for vm=%ld: %ld", i,
+			       ret);
+			hf_free_resources(i);
+			return ret;
+		}
+
+		vm->vcpu_count = ret;
+		vm->vcpu = kmalloc(sizeof(struct hf_vcpu) * vm->vcpu_count,
+				   GFP_KERNEL);
+		if (!vm->vcpu) {
+			pr_err("No memory for %ld vcpus for vm %ld",
+			       vm->vcpu_count, i);
+			hf_free_resources(i);
+			return -ENOMEM;
+		}
+
+		/* Create a kernel thread for each vcpu. */
+		for (j = 0; j < vm->vcpu_count; j++) {
+			struct hf_vcpu *vcpu = vm->vcpu + j;
+			vcpu->task = kthread_create(hf_vcpu_thread, vcpu,
+						    "vcpu_thread_%ld_%ld",
+						    i, j);
+			if (IS_ERR(vcpu->task)) {
+				pr_err("Error creating task (vm=%ld,vcpu=%ld)"
+				       ": %ld\n", i, j, PTR_ERR(vcpu->task));
+				vm->vcpu_count = j;
+				hf_free_resources(i + 1);
+				return PTR_ERR(vcpu->task);
+			}
+
+			get_task_struct(vcpu->task);
+			spin_lock_init(&vcpu->lock);
+			vcpu->vm_index = i;
+			vcpu->vcpu_index = j;
+			vcpu->pending_irq = false;
+		}
+	}
+
+	/* Start running threads now that all is initialized. */
+	for (i = 0; i < hf_vm_count; i++) {
+		struct hf_vm *vm = hf_vms + i;
+		for (j = 0; j < vm->vcpu_count; j++)
+			wake_up_process(vm->vcpu[j].task);
+	}
+
+	/* Dump vm/vcpu count info. */
+	pr_info("Hafnium successfully loaded with %ld VMs:\n", hf_vm_count);
+	for (i = 0; i < hf_vm_count; i++)
+		pr_info("\tVM %ld: %ld vCPUS\n", i, hf_vms[i].vcpu_count);
+
+	/* Create the sysfs interface to interrupt vcpus. */
+	hf_sysfs_obj = kobject_create_and_add("hafnium", kernel_kobj);
+	if (!hf_sysfs_obj) {
+		pr_err("Unable to create sysfs object");
+	} else {
+		ret = sysfs_create_file(hf_sysfs_obj, &interrupt_attr.attr);
+		if (ret)
+			pr_err("Unable to create 'interrupt' sysfs file");
+	}
+
+	return 0;
+}
+
+/**
+ * Frees up all resources used by the hafnium driver in preparation for
+ * unloading it.
+ */
+static void __exit hf_exit(void)
+{
+	if (hf_sysfs_obj)
+		kobject_put(hf_sysfs_obj);
+
+	pr_info("Preparing to unload hafnium\n");
+	hf_free_resources(hf_vm_count);
+	pr_info("Hafnium ready to unload\n");
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(hf_init);
+module_exit(hf_exit);