mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2026-01-13 11:33:58 +00:00
initial import
internal commit: 14ac2bc2299032fa6714d1fefa7cf0987b3e3085 Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
324
hypervisor/arch/x86/guest/vm.c
Normal file
324
hypervisor/arch/x86/guest/vm.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <hv_lib.h>
|
||||
#include <acrn_common.h>
|
||||
#include <hv_arch.h>
|
||||
#include <bsp_extern.h>
|
||||
#include <hv_debug.h>
|
||||
|
||||
/* Local variables */
|
||||
|
||||
/* VMs list */
|
||||
struct list_head vm_list = {
|
||||
.next = &vm_list,
|
||||
.prev = &vm_list,
|
||||
};
|
||||
|
||||
/* Lock for VMs list */
|
||||
spinlock_t vm_list_lock = {
|
||||
.head = 0,
|
||||
.tail = 0
|
||||
};
|
||||
|
||||
/* used for vmid allocation. And this means the max vm number is 64 */
|
||||
static unsigned long vmid_bitmap;
|
||||
|
||||
static void init_vm(struct vm_description *vm_desc,
|
||||
struct vm *vm_handle)
|
||||
{
|
||||
/* Populate VM attributes from VM description */
|
||||
vm_handle->hw.num_vcpus = vm_desc->vm_hw_num_cores;
|
||||
vm_handle->state_info.privilege = vm_desc->vm_state_info_privilege;
|
||||
vm_handle->state_info.boot_count = 0;
|
||||
}
|
||||
|
||||
/* return a pointer to the virtual machine structure associated with
|
||||
* this VM ID
|
||||
*/
|
||||
struct vm *get_vm_from_vmid(int vm_id)
|
||||
{
|
||||
struct vm *vm = NULL;
|
||||
struct list_head *pos;
|
||||
|
||||
spinlock_obtain(&vm_list_lock);
|
||||
list_for_each(pos, &vm_list) {
|
||||
vm = list_entry(pos, struct vm, list);
|
||||
if (vm->attr.id == vm_id) {
|
||||
spinlock_release(&vm_list_lock);
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
spinlock_release(&vm_list_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int create_vm(struct vm_description *vm_desc, struct vm **rtn_vm)
|
||||
{
|
||||
unsigned int id;
|
||||
struct vm *vm;
|
||||
int status = 0;
|
||||
|
||||
if ((vm_desc == NULL) || (rtn_vm == NULL))
|
||||
status = -EINVAL;
|
||||
|
||||
if (status == 0) {
|
||||
/* Allocate memory for virtual machine */
|
||||
vm = calloc(1, sizeof(struct vm));
|
||||
ASSERT(vm != NULL, "vm allocation failed");
|
||||
|
||||
/*
|
||||
* Map Virtual Machine to its VM Description
|
||||
*/
|
||||
init_vm(vm_desc, vm);
|
||||
|
||||
|
||||
/* Init mmio list */
|
||||
INIT_LIST_HEAD(&vm->mmio_list);
|
||||
|
||||
if (vm->hw.num_vcpus == 0)
|
||||
vm->hw.num_vcpus = phy_cpu_num;
|
||||
|
||||
vm->hw.vcpu_array =
|
||||
calloc(1, sizeof(struct vcpu *) * vm->hw.num_vcpus);
|
||||
ASSERT(vm->hw.vcpu_array != NULL,
|
||||
"vcpu_array allocation failed");
|
||||
|
||||
for (id = 0; id < sizeof(long) * 8; id++)
|
||||
if (bitmap_test_and_set(id, &vmid_bitmap) == 0)
|
||||
break;
|
||||
vm->attr.id = vm->attr.boot_idx = id;
|
||||
snprintf(&vm->attr.name[0], MAX_VM_NAME_LEN, "vm_%d",
|
||||
vm->attr.id);
|
||||
|
||||
atomic_store_rel_int(&vm->hw.created_vcpus, 0);
|
||||
|
||||
/* gpa_lowtop are used for system start up */
|
||||
vm->hw.gpa_lowtop = 0;
|
||||
/* Only for SOS: Configure VM software information */
|
||||
/* For UOS: This VM software information is configure in DM */
|
||||
if (is_vm0(vm)) {
|
||||
prepare_vm0_memmap_and_e820(vm);
|
||||
#ifndef CONFIG_EFI_STUB
|
||||
status = init_vm0_boot_info(vm);
|
||||
#endif
|
||||
} else {
|
||||
/* populate UOS vm fields according to vm_desc */
|
||||
vm->secure_world_enabled =
|
||||
vm_desc->secure_world_enabled;
|
||||
memcpy_s(&vm->GUID[0], sizeof(vm->GUID),
|
||||
&vm_desc->GUID[0],
|
||||
sizeof(vm_desc->GUID));
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&vm->list);
|
||||
spinlock_obtain(&vm_list_lock);
|
||||
list_add(&vm->list, &vm_list);
|
||||
spinlock_release(&vm_list_lock);
|
||||
|
||||
/* Ensure VM software information obtained */
|
||||
if (status == 0) {
|
||||
|
||||
/* Set up IO bit-mask such that VM exit occurs on
|
||||
* selected IO ranges
|
||||
*/
|
||||
setup_io_bitmap(vm);
|
||||
|
||||
/* Create virtual uart */
|
||||
if (is_vm0(vm))
|
||||
vm->vuart = vuart_init(vm);
|
||||
|
||||
vm->vpic = vpic_init(vm);
|
||||
|
||||
/* vpic wire_mode default is INTR */
|
||||
vm->vpic_wire_mode = VPIC_WIRE_INTR;
|
||||
|
||||
/* Allocate full emulated vIOAPIC instance */
|
||||
vm->arch_vm.virt_ioapic = vioapic_init(vm);
|
||||
|
||||
/* Populate return VM handle */
|
||||
*rtn_vm = vm;
|
||||
ptdev_vm_init(vm);
|
||||
vm->sw.req_buf = 0;
|
||||
|
||||
vm->state = VM_CREATED;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Return status to caller */
|
||||
return status;
|
||||
}
|
||||
|
||||
int shutdown_vm(struct vm *vm)
|
||||
{
|
||||
int i, status = 0;
|
||||
struct vcpu *vcpu = NULL;
|
||||
|
||||
if (vm == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
pause_vm(vm);
|
||||
|
||||
/* Only allow shutdown paused vm */
|
||||
if (vm->state != VM_PAUSED)
|
||||
return -EINVAL;
|
||||
|
||||
foreach_vcpu(i, vm, vcpu) {
|
||||
reset_vcpu(vcpu);
|
||||
destroy_vcpu(vcpu);
|
||||
}
|
||||
|
||||
spinlock_obtain(&vm_list_lock);
|
||||
list_del_init(&vm->list);
|
||||
spinlock_release(&vm_list_lock);
|
||||
|
||||
ptdev_vm_deinit(vm);
|
||||
|
||||
/* cleanup and free vioapic */
|
||||
vioapic_cleanup(vm->arch_vm.virt_ioapic);
|
||||
|
||||
/* Free EPT allocated resources assigned to VM */
|
||||
destroy_ept(vm);
|
||||
|
||||
/* Free MSR bitmap */
|
||||
free(vm->arch_vm.msr_bitmap);
|
||||
|
||||
/* TODO: De-initialize I/O Emulation */
|
||||
free_io_emulation_resource(vm);
|
||||
|
||||
/* Free iommu_domain */
|
||||
if (vm->iommu_domain)
|
||||
destroy_iommu_domain(vm->iommu_domain);
|
||||
|
||||
bitmap_clr(vm->attr.id, &vmid_bitmap);
|
||||
|
||||
if (vm->vpic)
|
||||
vpic_cleanup(vm);
|
||||
|
||||
free(vm->hw.vcpu_array);
|
||||
|
||||
/* TODO: De-Configure HV-SW */
|
||||
/* Deallocate VM */
|
||||
free(vm);
|
||||
|
||||
/* Return status to caller */
|
||||
return status;
|
||||
}
|
||||
|
||||
int start_vm(struct vm *vm)
|
||||
{
|
||||
struct vcpu *vcpu = NULL;
|
||||
|
||||
vm->state = VM_STARTED;
|
||||
|
||||
/* Only start BSP (vid = 0) and let BSP start other APs */
|
||||
vcpu = vcpu_from_vid(vm, 0);
|
||||
ASSERT(vcpu != NULL, "vm%d, vcpu0", vm->attr.id);
|
||||
schedule_vcpu(vcpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DM only pause vm for shutdown/reboot. If we need to
|
||||
* extend the pause vm for DM, this API should be extended.
|
||||
*/
|
||||
int pause_vm(struct vm *vm)
|
||||
{
|
||||
int i;
|
||||
struct vcpu *vcpu = NULL;
|
||||
|
||||
if (vm->state == VM_PAUSED)
|
||||
return 0;
|
||||
|
||||
vm->state = VM_PAUSED;
|
||||
|
||||
foreach_vcpu(i, vm, vcpu)
|
||||
pause_vcpu(vcpu, VCPU_ZOMBIE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vm_resume(struct vm *vm)
|
||||
{
|
||||
int i;
|
||||
struct vcpu *vcpu = NULL;
|
||||
|
||||
foreach_vcpu(i, vm, vcpu)
|
||||
resume_vcpu(vcpu);
|
||||
|
||||
vm->state = VM_STARTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finally, we will remove the array and only maintain vm0 desc */
|
||||
struct vm_description *get_vm_desc(int idx)
|
||||
{
|
||||
struct vm_description_array *vm_desc_array;
|
||||
|
||||
/* Obtain base of user defined VM description array data
|
||||
* structure
|
||||
*/
|
||||
vm_desc_array = (struct vm_description_array *)get_vm_desc_base();
|
||||
/* Obtain VM description array base */
|
||||
if (idx >= vm_desc_array->num_vm_desc)
|
||||
return NULL;
|
||||
else
|
||||
return &vm_desc_array->vm_desc_array[idx];
|
||||
}
|
||||
|
||||
/* Create vm/vcpu for vm0 */
|
||||
int prepare_vm0(void)
|
||||
{
|
||||
int i, ret;
|
||||
struct vm *vm = NULL;
|
||||
struct vm_description *vm_desc = NULL;
|
||||
|
||||
vm_desc = get_vm_desc(0);
|
||||
ASSERT(vm_desc, "get vm desc failed");
|
||||
ret = create_vm(vm_desc, &vm);
|
||||
ASSERT(ret == 0, "VM creation failed!");
|
||||
|
||||
prepare_vcpu(vm, vm_desc->vm_hw_logical_core_ids[0]);
|
||||
|
||||
/* Prepare the AP for vm0 */
|
||||
for (i = 1; i < vm_desc->vm_hw_num_cores; i++)
|
||||
prepare_vcpu(vm, vm_desc->vm_hw_logical_core_ids[i]);
|
||||
|
||||
/* start vm0 BSP automatically */
|
||||
start_vm(vm);
|
||||
|
||||
pr_fatal("Start VM0");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user