blob: 08e9531021da149d90fc008177f85f9455af15dd [file] [log] [blame]
/*
* Copyright 2018 The Hafnium Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hf/vm.h"
#include "hf/api.h"
#include "hf/check.h"
#include "hf/cpu.h"
#include "hf/spci.h"
#include "hf/std.h"
#include "vmapi/hf/call.h"
static struct vm vms[MAX_VMS];
static spci_vm_count_t vm_count;
/**
* Returns the index of the VM within the VM array.
*/
static uint16_t vm_get_vm_index(spci_vm_id_t vm_id)
{
CHECK(vm_id >= HF_VM_ID_OFFSET);
return vm_id - HF_VM_ID_OFFSET;
}
bool vm_init(spci_vcpu_count_t vcpu_count, struct mpool *ppool,
struct vm **new_vm)
{
uint32_t i;
struct vm *vm;
if (vm_count >= MAX_VMS) {
return false;
}
vm = &vms[vm_count];
memset_s(vm, sizeof(*vm), 0, sizeof(*vm));
list_init(&vm->mailbox.waiter_list);
list_init(&vm->mailbox.ready_list);
sl_init(&vm->lock);
/* Generate IDs based on an offset, as low IDs e.g., 0, are reserved */
vm->id = vm_count + HF_VM_ID_OFFSET;
vm->vcpu_count = vcpu_count;
vm->mailbox.state = MAILBOX_STATE_EMPTY;
atomic_init(&vm->aborting, false);
if (!mm_vm_init(&vm->ptable, ppool)) {
return false;
}
/* Initialise waiter entries. */
for (i = 0; i < MAX_VMS; i++) {
vm->wait_entries[i].waiting_vm = vm;
list_init(&vm->wait_entries[i].wait_links);
list_init(&vm->wait_entries[i].ready_links);
}
/* Do basic initialization of vcpus. */
for (i = 0; i < vcpu_count; i++) {
vcpu_init(vm_get_vcpu(vm, i), vm);
}
++vm_count;
*new_vm = vm;
return true;
}
spci_vm_count_t vm_get_count(void)
{
return vm_count;
}
struct vm *vm_find(spci_vm_id_t id)
{
uint16_t vm_index = vm_get_vm_index(id);
/* Ensure the VM is initialized. */
if (vm_index >= vm_count) {
return NULL;
}
return &vms[vm_index];
}
/**
* Locks the given VM and updates `locked` to hold the newly locked vm.
*/
struct vm_locked vm_lock(struct vm *vm)
{
struct vm_locked locked = {
.vm = vm,
};
sl_lock(&vm->lock);
return locked;
}
/**
* Locks two VMs ensuring that the locking order is according to the locks'
* addresses.
*/
struct two_vm_locked vm_lock_both(struct vm *vm1, struct vm *vm2)
{
struct two_vm_locked dual_lock;
sl_lock_both(&vm1->lock, &vm2->lock);
dual_lock.vm1.vm = vm1;
dual_lock.vm2.vm = vm2;
return dual_lock;
}
/**
* Unlocks a VM previously locked with vm_lock, and updates `locked` to reflect
* the fact that the VM is no longer locked.
*/
void vm_unlock(struct vm_locked *locked)
{
sl_unlock(&locked->vm->lock);
locked->vm = NULL;
}
/**
* Get the vCPU with the given index from the given VM.
* This assumes the index is valid, i.e. less than vm->vcpu_count.
*/
struct vcpu *vm_get_vcpu(struct vm *vm, spci_vcpu_index_t vcpu_index)
{
CHECK(vcpu_index < vm->vcpu_count);
return &vm->vcpus[vcpu_index];
}