Improve arch-specific separation in API code.
diff --git a/inc/api.h b/inc/api.h
new file mode 100644
index 0000000..edc9797
--- /dev/null
+++ b/inc/api.h
@@ -0,0 +1,17 @@
+#ifndef _API_H
+#define _API_H
+
+#include "cpu.h"
+#include "vm.h"
+
+/* TODO: Can we hide these? */
+extern struct vm secondary_vm[MAX_VMS];
+extern uint32_t secondary_vm_count;
+extern struct vm primary_vm;
+
+int32_t api_vm_get_count(void);
+int32_t api_vcpu_get_count(uint32_t vm_idx);
+int32_t api_vcpu_run(uint32_t vm_idx, uint32_t vcpu_idx, struct vcpu **next);
+struct vcpu *api_wait_for_interrupt(void);
+
+#endif  /* _API_H */
diff --git a/inc/cpu.h b/inc/cpu.h
index 2eae33b..43bd356 100644
--- a/inc/cpu.h
+++ b/inc/cpu.h
@@ -37,7 +37,10 @@
 	void *stack_bottom;
 };
 
+void cpu_module_init(void);
+
 void cpu_init(struct cpu *c);
+size_t cpu_index(struct cpu *c);
 void cpu_irq_enable(struct cpu *c);
 void cpu_irq_disable(struct cpu *c);
 bool cpu_on(struct cpu *c);
diff --git a/src/api.c b/src/api.c
new file mode 100644
index 0000000..a105f6d
--- /dev/null
+++ b/src/api.c
@@ -0,0 +1,72 @@
+#include "api.h"
+
+#include "arch_api.h"
+#include "vm.h"
+
+struct vm secondary_vm[MAX_VMS];
+uint32_t secondary_vm_count;
+struct vm primary_vm;
+
+/**
+ * Returns the number of VMs configured to run.
+ */
+int32_t api_vm_get_count(void)
+{
+	return secondary_vm_count;
+}
+
+/**
+ * Returns the number of vcpus configured in the given VM.
+ */
+int32_t api_vcpu_get_count(uint32_t vm_idx)
+{
+	if (vm_idx >= secondary_vm_count)
+		return -1;
+
+	return secondary_vm[vm_idx].vcpu_count;
+}
+
+/**
+ * Runs the given vcpu of the given vm.
+ */
+int32_t api_vcpu_run(uint32_t vm_idx, uint32_t vcpu_idx, struct vcpu **next)
+{
+	struct vm *vm = secondary_vm + vm_idx;
+	struct vcpu *vcpu;
+
+	/* Only the primary VM can switch vcpus. */
+	if (cpu()->current->vm != &primary_vm)
+		return HF_VCPU_WAIT_FOR_INTERRUPT;
+
+	if (vm_idx >= secondary_vm_count)
+		return HF_VCPU_WAIT_FOR_INTERRUPT;
+
+	vcpu = vm->vcpus + vcpu_idx;
+	if (vcpu_idx >= vm->vcpu_count || !vcpu->is_on)
+		return HF_VCPU_WAIT_FOR_INTERRUPT;
+
+	arch_set_vm_mm(&vm->page_table);
+	*next = vcpu;
+
+	return HF_VCPU_YIELD;
+}
+
+/**
+ * Puts current vcpu in wait for interrupt mode, and returns to the primary
+ * vm.
+ */
+struct vcpu *api_wait_for_interrupt(void)
+{
+	struct vcpu *vcpu = &primary_vm.vcpus[cpu_index(cpu())];
+
+	/* Switch back to primary VM. */
+	arch_set_vm_mm(&primary_vm.page_table);
+
+	/*
+	 * Inidicate to primary VM that this vcpu blocked waiting for an
+	 * interrupt.
+	 */
+	arch_regs_set_retval(&vcpu->regs, HF_VCPU_WAIT_FOR_INTERRUPT);
+
+	return vcpu;
+}
diff --git a/src/arch/aarch64/handler.c b/src/arch/aarch64/handler.c
index 1844e37..48de85d 100644
--- a/src/arch/aarch64/handler.c
+++ b/src/arch/aarch64/handler.c
@@ -1,3 +1,5 @@
+#include "api.h"
+#include "arch_api.h"
 #include "cpu.h"
 #include "dlog.h"
 #include "vm.h"
@@ -21,17 +23,6 @@
 	for (;;);
 }
 
-/* TODO: Define constants below according to spec. */
-#define HF_VCPU_RUN       0xff00
-#define HF_VM_GET_COUNT   0xff01
-#define HF_VCPU_GET_COUNT 0xff02
-
-/* TODO: Move these decl elsewhere. */
-extern struct vm secondary_vm[MAX_VMS];
-extern uint32_t secondary_vm_count;
-extern struct vm primary_vm;
-extern struct cpu cpus[];
-
 struct hvc_handler_return hvc_handler(size_t arg0, size_t arg1, size_t arg2,
 				      size_t arg3)
 {
@@ -49,28 +40,15 @@
 		break;
 
 	case HF_VM_GET_COUNT:
-		ret.user_ret = secondary_vm_count;
+		ret.user_ret = api_vm_get_count();
 		break;
 
 	case HF_VCPU_GET_COUNT:
-		if (arg1 >= secondary_vm_count)
-			ret.user_ret = -1;
-		else
-			ret.user_ret = secondary_vm[arg1].vcpu_count;
+		ret.user_ret = api_vcpu_get_count(arg1);
 		break;
 
 	case HF_VCPU_RUN:
-		/* TODO: Make sure we don't allow secondary VMs to make this
-		 * hvc call. */
-		ret.user_ret = 1; /* WFI */
-		if (arg1 < secondary_vm_count &&
-		    arg2 < secondary_vm[arg1].vcpu_count &&
-		    secondary_vm[arg1].vcpus[arg2].is_on) {
-			arch_set_vm_mm(&secondary_vm[arg1].page_table);
-			/* TODO: Update the virtual memory. */
-			ret.new = secondary_vm[arg1].vcpus + arg2;
-			ret.user_ret = 0;
-		}
+		ret.user_ret = api_vcpu_run(arg1, arg2, &ret.new);
 		break;
 
 	default:
@@ -87,7 +65,7 @@
 
 	/* Switch back to primary VM, interrupts will be handled there. */
 	arch_set_vm_mm(&primary_vm.page_table);
-	return &primary_vm.vcpus[cpus - cpu()];
+	return &primary_vm.vcpus[cpu_index(cpu())];
 }
 
 struct vcpu *sync_lower_exception(uint64_t esr)
@@ -100,18 +78,7 @@
 		/* Check TI bit of ISS, 0 = WFI, 1 = WFE. */
 		if (esr & 1)
 			return NULL;
-
-		/* Switch back to primary VM. */
-		arch_set_vm_mm(&primary_vm.page_table);
-		vcpu = &primary_vm.vcpus[cpus - cpu()];
-
-		dlog("Returning due to WFI\n");
-
-		/* TODO: Use constant. */
-		/* Set return value to 1, indicating to primary VM that this
-		 * vcpu blocked on a WFI. */
-		arch_regs_set_retval(&vcpu->regs, 1);
-		return vcpu;
+		return api_wait_for_interrupt();
 
 	case 0x24: /* EC = 100100, Data abort. */
 		dlog("Data abort: pc=0x%x, esr=0x%x, ec=0x%x", vcpu->regs.pc, esr, esr >> 26);
diff --git a/src/arch/aarch64/inc/arch_api.h b/src/arch/aarch64/inc/arch_api.h
new file mode 100644
index 0000000..733d818
--- /dev/null
+++ b/src/arch/aarch64/inc/arch_api.h
@@ -0,0 +1,14 @@
+#ifndef _ARCH_API_H
+#define _ARCH_API_H
+
+/* Return values for vcpu_run() hypervisor call. */
+#define HF_VCPU_YIELD              0x00
+#define HF_VCPU_WAIT_FOR_INTERRUPT 0x01
+#define HF_VCPU_WAKE_UP            0x02
+
+/* TODO: Define constants below according to spec. */
+#define HF_VCPU_RUN       0xff00
+#define HF_VM_GET_COUNT   0xff01
+#define HF_VCPU_GET_COUNT 0xff02
+
+#endif  /* _ARCH_API_H */
diff --git a/src/cpu.c b/src/cpu.c
index 73a2d1c..bb84bbb 100644
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -5,6 +5,35 @@
 #include "std.h"
 #include "vm.h"
 
+/* The stack to be used by the CPUs. */
+alignas(2 * sizeof(size_t)) static char callstacks[STACK_SIZE * MAX_CPUS];
+
+/* State of all supported CPUs. The stack of the first one is initialized. */
+struct cpu cpus[MAX_CPUS] = {
+	{
+		.is_on = 1,
+		.stack_bottom = callstacks + STACK_SIZE,
+	},
+};
+
+void cpu_module_init(void)
+{
+	size_t i;
+
+	/* 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);
+	}
+}
+
+size_t cpu_index(struct cpu *c)
+{
+	return cpus - c;
+}
+
 void cpu_init(struct cpu *c)
 {
 	/* TODO: Assumes that c is zeroed out already. */
diff --git a/src/main.c b/src/main.c
index 885f577..946dceb 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,17 +11,6 @@
 
 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] = {
-	{
-		.is_on = 1,
-		.stack_bottom = callstacks + STACK_SIZE,
-	},
-};
-
 bool fdt_find_node(struct fdt_node *node, const char *path)
 {
 	while (*path) {
@@ -411,17 +400,9 @@
 
 static void one_time_init(void)
 {
-	size_t i;
-
 	dlog("Initializing hafnium\n");
 
-	/* 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);
-	}
+	cpu_module_init();
 
 	/* TODO: Code below this point should be removed from this function. */
 	/* TODO: Remove this. */
@@ -470,7 +451,7 @@
 	if (!atomic_flag_test_and_set_explicit(&inited, memory_order_acq_rel))
 		one_time_init();
 
-	dlog("Starting up cpu %d\n", c - cpus);
+	dlog("Starting up cpu %d\n", cpu_index(c));
 
-	return primary_vm.vcpus + (c - cpus);
+	return primary_vm.vcpus + cpu_index(c);
 }
diff --git a/src/rules.mk b/src/rules.mk
index e8022f2..313748d 100644
--- a/src/rules.mk
+++ b/src/rules.mk
@@ -1,4 +1,5 @@
 SRCS += alloc.c
+SRCS += api.c
 SRCS += cpio.c
 SRCS += cpu.c
 SRCS += fdt.c