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/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);
}