Store all VMs together.

In preparation for the primary VM becoming less special, particularly in
the context of IPC where any VM will be able to talk to any other VM.
The primary VM takes ID 0 and the secondary VMs take the IDs from 1.

`hf_vm_get_count()` now returns the total number of VMs including the
primary.

`hf_rpc_request()` block reflexive calls as there is no good reason for
them and the error has the chance of catching bugs in the calling VM.

Change-Id: Ic95ef0ab8b967bbbd3232c207c53074cff8e138d
diff --git a/driver/linux b/driver/linux
index b3a61b5..b722f95 160000
--- a/driver/linux
+++ b/driver/linux
@@ -1 +1 @@
-Subproject commit b3a61b5fe4faf0f1111c203e385b1f100ecadc74
+Subproject commit b722f95ee1dfd26dc4d54628aa343170a7d3b2de
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 934404d..d598ffc 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -3,21 +3,16 @@
 #include "hf/cpu.h"
 #include "hf/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;
-
 struct vcpu *api_switch_to_primary(size_t primary_retval,
 				   enum vcpu_state secondary_state);
 
 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);
+int32_t api_vcpu_get_count(uint32_t vm_id);
+int32_t api_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx, struct vcpu **next);
 struct vcpu *api_wait_for_interrupt(void);
 int32_t api_vm_configure(ipaddr_t send, ipaddr_t recv);
 
-int32_t api_rpc_request(uint32_t vm_idx, size_t size);
+int32_t api_rpc_request(uint32_t vm_id, size_t size);
 int32_t api_rpc_read_request(bool block, struct vcpu **next);
 int32_t api_rpc_reply(size_t size, bool ack, struct vcpu **next);
 int32_t api_rpc_ack(void);
diff --git a/inc/hf/vm.h b/inc/hf/vm.h
index 5389e97..3d7bf2e 100644
--- a/inc/hf/vm.h
+++ b/inc/hf/vm.h
@@ -26,6 +26,8 @@
 	struct rpc rpc;
 };
 
-bool vm_init(struct vm *vm, uint32_t id, uint32_t vcpu_count);
+bool vm_init(uint32_t vcpu_count, struct vm **new_vm);
+uint32_t vm_get_count(void);
+struct vm *vm_get(uint32_t id);
 void vm_start_vcpu(struct vm *vm, size_t index, ipaddr_t entry, size_t arg);
 void vm_set_current(struct vm *vm);
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 2195113..418f0c0 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -29,6 +29,9 @@
 #define HF_RPC_ACK          0xff06
 #define HF_RPC_REPLY        0xff07
 
+/* The ID of the primary VM which is responsile for scheduling. */
+#define HF_PRIMARY_VM_ID 0
+
 /* Return codes for hf_vcpu_run(). */
 #define HF_VCPU_RUN_YIELD              0x00
 #define HF_VCPU_RUN_WAIT_FOR_INTERRUPT 0x01
@@ -53,9 +56,9 @@
 /**
  * Runs the given vcpu of the given vm.
  */
-static inline int32_t hf_vcpu_run(uint32_t vm_idx, uint32_t vcpu_idx)
+static inline int32_t hf_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx)
 {
-	return hf_call(HF_VCPU_RUN, vm_idx, vcpu_idx, 0);
+	return hf_call(HF_VCPU_RUN, vm_id, vcpu_idx, 0);
 }
 
 /**
@@ -69,9 +72,9 @@
 /**
  * Returns the number of VCPUs configured in the given secondary VM.
  */
-static inline int32_t hf_vcpu_get_count(uint32_t vm_idx)
+static inline int32_t hf_vcpu_get_count(uint32_t vm_id)
 {
-	return hf_call(HF_VCPU_GET_COUNT, vm_idx, 0, 0);
+	return hf_call(HF_VCPU_GET_COUNT, vm_id, 0, 0);
 }
 
 /**
@@ -87,9 +90,9 @@
  * Called by the primary VM to send an RPC request to a secondary VM. Data is
  * copied from the caller's send buffer to the destination's receive buffer.
  */
-static inline int32_t hf_rpc_request(uint32_t vm_idx, size_t size)
+static inline int32_t hf_rpc_request(uint32_t vm_id, size_t size)
 {
-	return hf_call(HF_RPC_REQUEST, vm_idx, size, 0);
+	return hf_call(HF_RPC_REQUEST, vm_id, size, 0);
 }
 
 /**
diff --git a/src/api.c b/src/api.c
index 8195ad6..b28cd59 100644
--- a/src/api.c
+++ b/src/api.c
@@ -11,10 +11,6 @@
 	      "Currently, a page is mapped for the send and receive buffers so "
 	      "the maximum request is the size of a page.");
 
-struct vm secondary_vm[MAX_VMS];
-uint32_t secondary_vm_count;
-struct vm primary_vm;
-
 /**
  * Switches the physical CPU back to the corresponding vcpu of the primary VM.
  */
@@ -22,10 +18,11 @@
 				   enum vcpu_state secondary_state)
 {
 	struct vcpu *vcpu = cpu()->current;
-	struct vcpu *next = &primary_vm.vcpus[cpu_index(cpu())];
+	struct vm *primary = vm_get(HF_PRIMARY_VM_ID);
+	struct vcpu *next = &primary->vcpus[cpu_index(cpu())];
 
 	/* Switch back to primary VM. */
-	vm_set_current(&primary_vm);
+	vm_set_current(primary);
 
 	/*
 	 * Inidicate to primary VM that this vcpu blocked waiting for an
@@ -46,42 +43,57 @@
  */
 int32_t api_vm_get_count(void)
 {
-	return secondary_vm_count;
+	return vm_get_count();
 }
 
 /**
  * Returns the number of vcpus configured in the given VM.
  */
-int32_t api_vcpu_get_count(uint32_t vm_idx)
+int32_t api_vcpu_get_count(uint32_t vm_id)
 {
-	if (vm_idx >= secondary_vm_count) {
+	struct vm *vm;
+
+	/* Only the primary VM needs to know about vcpus for scheduling. */
+	if (cpu()->current->vm->id != HF_PRIMARY_VM_ID) {
 		return -1;
 	}
 
-	return secondary_vm[vm_idx].vcpu_count;
+	vm = vm_get(vm_id);
+	if (vm == NULL) {
+		return -1;
+	}
+
+	return vm->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)
+int32_t api_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx, struct vcpu **next)
 {
 	struct vm *vm;
 	struct vcpu *vcpu;
 	int32_t ret;
 
 	/* Only the primary VM can switch vcpus. */
-	if (cpu()->current->vm != &primary_vm) {
-		return HF_VCPU_RUN_RESPONSE(HF_VCPU_RUN_WAIT_FOR_INTERRUPT, 0);
+	if (cpu()->current->vm->id != HF_PRIMARY_VM_ID) {
+		goto fail;
 	}
 
-	if (vm_idx >= secondary_vm_count) {
-		return HF_VCPU_RUN_RESPONSE(HF_VCPU_RUN_WAIT_FOR_INTERRUPT, 0);
+	/* Only secondary VM vcpus can be run. */
+	if (vm_id == HF_PRIMARY_VM_ID) {
+		goto fail;
 	}
 
-	vm = &secondary_vm[vm_idx];
+	/* The requested VM must exist. */
+	vm = vm_get(vm_id);
+	if (vm == NULL) {
+		goto fail;
+	}
+
+	/* The requested vcpu must exist. */
 	if (vcpu_idx >= vm->vcpu_count) {
-		return HF_VCPU_RUN_RESPONSE(HF_VCPU_RUN_WAIT_FOR_INTERRUPT, 0);
+		goto fail;
 	}
 
 	vcpu = &vm->vcpus[vcpu_idx];
@@ -98,6 +110,9 @@
 	sl_unlock(&vcpu->lock);
 
 	return ret;
+
+fail:
+	return HF_VCPU_RUN_RESPONSE(HF_VCPU_RUN_WAIT_FOR_INTERRUPT, 0);
 }
 
 /**
@@ -195,7 +210,7 @@
  * Sends an RPC request from the primary VM to a secondary VM. Data is copied
  * from the caller's send buffer to the destination's receive buffer.
  */
-int32_t api_rpc_request(uint32_t vm_idx, size_t size)
+int32_t api_rpc_request(uint32_t vm_id, size_t size)
 {
 	struct vm *from = cpu()->current->vm;
 	struct vm *to;
@@ -203,12 +218,23 @@
 	int32_t ret;
 
 	/* Basic argument validation. */
-	if (size > PAGE_SIZE || vm_idx >= secondary_vm_count) {
+	if (size > HF_RPC_REQUEST_MAX_SIZE) {
+		return -1;
+	}
+
+	/* Disallow reflexive requests as this suggests an error in the VM. */
+	if (vm_id == from->id) {
+		return -1;
+	}
+
+	/* Ensure the target VM exists. */
+	to = vm_get(vm_id);
+	if (to == NULL) {
 		return -1;
 	}
 
 	/* Only the primary VM can make calls. */
-	if (from != &primary_vm) {
+	if (from->id != HF_PRIMARY_VM_ID) {
 		return -1;
 	}
 
@@ -224,7 +250,6 @@
 		return -1;
 	}
 
-	to = &secondary_vm[vm_idx];
 	sl_lock(&to->lock);
 
 	if (to->rpc.state != rpc_state_idle || !to->rpc.recv) {
@@ -275,10 +300,11 @@
 {
 	struct vcpu *vcpu = cpu()->current;
 	struct vm *vm = vcpu->vm;
+	struct vm *primary = vm_get(HF_PRIMARY_VM_ID);
 	int32_t ret;
 
 	/* Only the secondary VMs can receive calls. */
-	if (vm == &primary_vm) {
+	if (vm->id == HF_PRIMARY_VM_ID) {
 		return -1;
 	}
 
@@ -298,8 +324,8 @@
 		sl_unlock(&vcpu->lock);
 
 		/* Switch back to primary vm. */
-		*next = &primary_vm.vcpus[cpu_index(cpu())];
-		vm_set_current(&primary_vm);
+		*next = &primary->vcpus[cpu_index(cpu())];
+		vm_set_current(primary);
 
 		/*
 		 * Inidicate to primary VM that this vcpu blocked waiting for an
@@ -334,7 +360,7 @@
 	}
 
 	/* Only the secondary VM can send responses. */
-	if (from == &primary_vm) {
+	if (from->id == HF_PRIMARY_VM_ID) {
 		return -1;
 	}
 
@@ -355,7 +381,7 @@
 		return -1;
 	}
 
-	to = &primary_vm;
+	to = vm_get(HF_PRIMARY_VM_ID);
 	sl_lock(&to->lock);
 
 	if (to->rpc.state != rpc_state_idle || !to->rpc.recv) {
diff --git a/src/arch/aarch64/handler.c b/src/arch/aarch64/handler.c
index 82ae461..538c3b3 100644
--- a/src/arch/aarch64/handler.c
+++ b/src/arch/aarch64/handler.c
@@ -164,7 +164,7 @@
 
 	ret.new = NULL;
 
-	if (cpu()->current->vm == &primary_vm &&
+	if (cpu()->current->vm->id == HF_PRIMARY_VM_ID &&
 	    psci_handler(arg0, arg1, arg2, arg3, &ret.user_ret)) {
 		return ret;
 	}
@@ -264,7 +264,7 @@
 		}
 
 	case 0x17: /* EC = 010111, SMC instruction. */
-		if (vcpu->vm != &primary_vm ||
+		if (vcpu->vm->id != HF_PRIMARY_VM_ID ||
 		    !psci_handler(vcpu->regs.r[0], vcpu->regs.r[1],
 				  vcpu->regs.r[2], vcpu->regs.r[3], &ret)) {
 			dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
diff --git a/src/cpu.c b/src/cpu.c
index 185d993..2c09d81 100644
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -9,6 +9,8 @@
 #include "hf/std.h"
 #include "hf/vm.h"
 
+#include "vmapi/hf/call.h"
+
 /* The stack to be used by the CPUs. */
 alignas(2 * sizeof(size_t)) static char callstacks[MAX_CPUS][STACK_SIZE];
 
@@ -74,7 +76,8 @@
 	sl_unlock(&c->lock);
 
 	if (!prev) {
-		struct vcpu *vcpu = &primary_vm.vcpus[cpu_index(c)];
+		struct vcpu *vcpu =
+			&vm_get(HF_PRIMARY_VM_ID)->vcpus[cpu_index(c)];
 		arch_regs_init(&vcpu->regs, entry, arg);
 		vcpu_on(vcpu);
 	}
diff --git a/src/load.c b/src/load.c
index cad62c9..a6bf7d7 100644
--- a/src/load.c
+++ b/src/load.c
@@ -11,6 +11,8 @@
 #include "hf/std.h"
 #include "hf/vm.h"
 
+#include "vmapi/hf/call.h"
+
 /**
  * Copies data to an unmapped location by mapping it for write, copying the
  * data, then unmapping it.
@@ -117,16 +119,23 @@
 
 	{
 		uintpaddr_t tmp = (uintpaddr_t)&load_primary;
+		struct vm *vm;
+
 		tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
-		if (!vm_init(&primary_vm, 0, MAX_CPUS)) {
+		if (!vm_init(MAX_CPUS, &vm)) {
 			dlog("Unable to initialise primary vm\n");
 			return false;
 		}
 
+		if (vm->id != HF_PRIMARY_VM_ID) {
+			dlog("Primary vm was not given correct id\n");
+			return false;
+		}
+
 		/* Map the 1TB of memory. */
 		/* TODO: We should do a whitelist rather than a blacklist. */
 		if (!mm_vm_identity_map(
-			    &primary_vm.ptable, pa_init(0),
+			    &vm->ptable, pa_init(0),
 			    pa_init(UINT64_C(1024) * 1024 * 1024 * 1024),
 			    MM_MODE_R | MM_MODE_W | MM_MODE_X |
 				    MM_MODE_NOINVALIDATE,
@@ -135,13 +144,13 @@
 			return false;
 		}
 
-		if (!mm_ptable_unmap_hypervisor(&primary_vm.ptable,
+		if (!mm_ptable_unmap_hypervisor(&vm->ptable,
 						MM_MODE_NOINVALIDATE)) {
 			dlog("Unable to unmap hypervisor from primary vm\n");
 			return false;
 		}
 
-		vm_start_vcpu(&primary_vm, 0, ipa_init(tmp), kernel_arg);
+		vm_start_vcpu(vm, 0, ipa_init(tmp), kernel_arg);
 	}
 
 	return true;
@@ -227,11 +236,11 @@
 		    const struct boot_params *params,
 		    struct boot_params_update *update)
 {
+	struct vm *primary;
 	struct memiter it;
 	struct memiter name;
 	uint64_t mem;
 	uint64_t cpu;
-	uint32_t count;
 	struct mem_range mem_ranges_available[MAX_MEM_RANGES];
 	size_t i;
 
@@ -244,6 +253,8 @@
 	memcpy(mem_ranges_available, params->mem_ranges,
 	       sizeof(mem_ranges_available));
 
+	primary = vm_get(HF_PRIMARY_VM_ID);
+
 	if (!find_file(cpio, "vms.txt", &it)) {
 		dlog("vms.txt is missing\n");
 		return true;
@@ -256,15 +267,14 @@
 				~(PAGE_SIZE - 1));
 	}
 
-	for (count = 0;
-	     memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
-	     memiter_parse_str(&it, &name) && count < MAX_VMS;
-	     count++) {
+	while (memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
+	       memiter_parse_str(&it, &name)) {
 		struct memiter kernel;
 		paddr_t secondary_mem_begin;
 		paddr_t secondary_mem_end;
 		ipaddr_t secondary_entry;
 		const char *p;
+		struct vm *vm;
 
 		dlog("Loading ");
 		for (p = name.next; p != name.limit; ++p) {
@@ -298,22 +308,21 @@
 			continue;
 		}
 
-		if (!vm_init(&secondary_vm[count], count + 1, cpu)) {
+		if (!vm_init(cpu, &vm)) {
 			dlog("Unable to initialise VM\n");
 			continue;
 		}
 
 		/* TODO: Remove this. */
 		/* Grant VM access to uart. */
-		mm_vm_identity_map_page(&secondary_vm[count].ptable,
-					pa_init(PL011_BASE),
+		mm_vm_identity_map_page(&vm->ptable, pa_init(PL011_BASE),
 					MM_MODE_R | MM_MODE_W | MM_MODE_D |
 						MM_MODE_NOINVALIDATE,
 					NULL);
 
 		/* Grant the VM access to the memory. */
-		if (!mm_vm_identity_map(&secondary_vm[count].ptable,
-					secondary_mem_begin, secondary_mem_end,
+		if (!mm_vm_identity_map(&vm->ptable, secondary_mem_begin,
+					secondary_mem_end,
 					MM_MODE_R | MM_MODE_W | MM_MODE_X |
 						MM_MODE_NOINVALIDATE,
 					&secondary_entry)) {
@@ -322,7 +331,7 @@
 		}
 
 		/* Deny the primary VM access to this memory. */
-		if (!mm_vm_unmap(&primary_vm.ptable, secondary_mem_begin,
+		if (!mm_vm_unmap(&primary->ptable, secondary_mem_begin,
 				 secondary_mem_end, MM_MODE_NOINVALIDATE)) {
 			dlog("Unable to unmap secondary VM from primary VM\n");
 			return false;
@@ -331,11 +340,9 @@
 		dlog("Loaded with %u vcpus, entry at 0x%x\n", cpu,
 		     pa_addr(secondary_mem_begin));
 
-		vm_start_vcpu(&secondary_vm[count], 0, secondary_entry, 0);
+		vm_start_vcpu(vm, 0, secondary_entry, 0);
 	}
 
-	secondary_vm_count = count;
-
 	/* Add newly reserved areas to update params by looking at the
 	 * difference between the available ranges from the original params and
 	 * the updated mem_ranges_available. We assume that the number and order
diff --git a/src/main.c b/src/main.c
index 3b9018d..6ae6607 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,8 @@
 #include "hf/std.h"
 #include "hf/vm.h"
 
+#include "vmapi/hf/call.h"
+
 char ptable_buf[PAGE_SIZE * 40];
 
 /**
@@ -113,6 +115,7 @@
 struct vcpu *cpu_main(void)
 {
 	struct cpu *c = cpu();
+	struct vm *primary;
 
 	/*
 	 * Do global one-time initialisation just once. We avoid using atomics
@@ -130,7 +133,8 @@
 		panic("mm_cpu_init failed");
 	}
 
-	vm_set_current(&primary_vm);
+	primary = vm_get(HF_PRIMARY_VM_ID);
+	vm_set_current(primary);
 
-	return &primary_vm.vcpus[cpu_index(c)];
+	return &primary->vcpus[cpu_index(c)];
 }
diff --git a/src/vm.c b/src/vm.c
index d2beeb0..0958682 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -4,13 +4,25 @@
 #include "hf/cpu.h"
 #include "hf/std.h"
 
-bool vm_init(struct vm *vm, uint32_t id, uint32_t vcpu_count)
+#include "vmapi/hf/call.h"
+
+static struct vm vms[MAX_VMS];
+static uint32_t vm_count;
+
+bool vm_init(uint32_t vcpu_count, struct vm **new_vm)
 {
 	uint32_t i;
+	struct vm *vm;
+
+	if (vm_count >= MAX_VMS) {
+		return false;
+	}
+
+	vm = &vms[vm_count];
 
 	memset(vm, 0, sizeof(*vm));
 
-	vm->id = id;
+	vm->id = vm_count;
 	vm->vcpu_count = vcpu_count;
 	vm->rpc.state = rpc_state_idle;
 
@@ -19,9 +31,27 @@
 		vcpu_init(&vm->vcpus[i], vm);
 	}
 
+	++vm_count;
+	*new_vm = vm;
+
 	return mm_ptable_init(&vm->ptable, 0);
 }
 
+uint32_t vm_get_count(void)
+{
+	return vm_count;
+}
+
+struct vm *vm_get(uint32_t id)
+{
+	/* Ensure the VM is initialized. */
+	if (id >= vm_count) {
+		return NULL;
+	}
+
+	return &vms[id];
+}
+
 /* TODO: Shall we use index or id here? */
 void vm_start_vcpu(struct vm *vm, size_t index, ipaddr_t entry, size_t arg)
 {
@@ -34,6 +64,6 @@
 
 void vm_set_current(struct vm *vm)
 {
-	arch_cpu_update(vm == &primary_vm);
+	arch_cpu_update(vm->id == HF_PRIMARY_VM_ID);
 	arch_mm_set_vm(vm->id, vm->ptable.table);
 }
diff --git a/test/vm/primary_only/primary.c b/test/vm/primary_only/primary.c
index 18c4709..b2ee0d5 100644
--- a/test/vm/primary_only/primary.c
+++ b/test/vm/primary_only/primary.c
@@ -4,12 +4,17 @@
 
 TEST(hf_vm_get_count, no_secondary_vms)
 {
-	EXPECT_EQ(hf_vm_get_count(), 0);
+	EXPECT_EQ(hf_vm_get_count(), 1);
+}
+
+TEST(hf_vcpu_get_count, primary_has_at_least_one)
+{
+	EXPECT_GE(hf_vcpu_get_count(0), 0);
 }
 
 TEST(hf_vcpu_get_count, no_secondary_vms)
 {
-	EXPECT_EQ(hf_vcpu_get_count(0), -1);
+	EXPECT_EQ(hf_vcpu_get_count(1), -1);
 }
 
 TEST(hf_vcpu_get_count, large_invalid_vm_index)
diff --git a/test/vm/primary_with_secondary/primary.c b/test/vm/primary_with_secondary/primary.c
index 58d8ba4..af488c2 100644
--- a/test/vm/primary_with_secondary/primary.c
+++ b/test/vm/primary_with_secondary/primary.c
@@ -21,7 +21,7 @@
  */
 TEST(hf_vm_get_count, one_secondary_vm)
 {
-	EXPECT_EQ(hf_vm_get_count(), 1);
+	EXPECT_EQ(hf_vm_get_count(), 2);
 }
 
 /**
@@ -29,7 +29,7 @@
  */
 TEST(hf_vcpu_get_count, secondary_has_one_vcpu)
 {
-	EXPECT_EQ(hf_vcpu_get_count(0), 1);
+	EXPECT_EQ(hf_vcpu_get_count(1), 1);
 }
 
 /**