mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-06 07:26:56 +00:00
The previous would reserve memory for trusty in SOS kernel. Howerer, there would no available 16 MB continue memory any more after a long time. This result in allocating memory for trusty failed. This patch will reserve memory for trusty in ACRN hypervisor in which case the memory allocation for trusty will never fail. Tracked-On: #1942 Signed-off-by: Li, Fei1 <fei1.li@intel.com>
441 lines
8.4 KiB
C
441 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <hypervisor.h>
|
|
#include <bsp_extern.h>
|
|
#include <multiboot.h>
|
|
#include <vtd.h>
|
|
|
|
/* Local variables */
|
|
|
|
static struct acrn_vm vm_array[CONFIG_MAX_VM_NUM] __aligned(CPU_PAGE_SIZE);
|
|
|
|
static uint64_t vmid_bitmap;
|
|
|
|
/* used for vmid allocation. And this means the max vm number is 64 */
|
|
static inline uint16_t alloc_vm_id(void)
|
|
{
|
|
uint16_t id = ffz64(vmid_bitmap);
|
|
|
|
while (id < (size_t)(sizeof(vmid_bitmap) * 8U)) {
|
|
if (!bitmap_test_and_set_lock(id, &vmid_bitmap)) {
|
|
return id;
|
|
}
|
|
id = ffz64(vmid_bitmap);
|
|
}
|
|
|
|
return INVALID_VM_ID;
|
|
}
|
|
|
|
static inline void free_vm_id(const struct acrn_vm *vm)
|
|
{
|
|
bitmap_clear_lock(vm->vm_id, &vmid_bitmap);
|
|
}
|
|
|
|
static inline bool is_vm_valid(uint16_t vm_id)
|
|
{
|
|
return bitmap_test(vm_id, &vmid_bitmap);
|
|
}
|
|
|
|
/* return a pointer to the virtual machine structure associated with
|
|
* this VM ID
|
|
*/
|
|
struct acrn_vm *get_vm_from_vmid(uint16_t vm_id)
|
|
{
|
|
struct acrn_vm *ret;
|
|
|
|
if (is_vm_valid(vm_id)) {
|
|
ret = &vm_array[vm_id];
|
|
} else {
|
|
ret = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pre vm_desc != NULL && rtn_vm != NULL
|
|
*/
|
|
int create_vm(struct vm_description *vm_desc, struct acrn_vm **rtn_vm)
|
|
{
|
|
struct acrn_vm *vm;
|
|
int status;
|
|
uint16_t vm_id;
|
|
|
|
#ifdef CONFIG_PARTITION_MODE
|
|
vm_id = vm_desc->vm_id;
|
|
bitmap_set_lock(vm_id, &vmid_bitmap);
|
|
#else
|
|
vm_id = alloc_vm_id();
|
|
#endif
|
|
if (vm_id >= CONFIG_MAX_VM_NUM) {
|
|
pr_err("%s, vm id is invalid!\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Allocate memory for virtual machine */
|
|
vm = &vm_array[vm_id];
|
|
(void)memset((void *)vm, 0U, sizeof(struct acrn_vm));
|
|
vm->vm_id = vm_id;
|
|
#ifdef CONFIG_PARTITION_MODE
|
|
/* Map Virtual Machine to its VM Description */
|
|
vm->vm_desc = vm_desc;
|
|
#endif
|
|
vm->hw.created_vcpus = 0U;
|
|
vm->emul_mmio_regions = 0U;
|
|
|
|
/* gpa_lowtop are used for system start up */
|
|
vm->hw.gpa_lowtop = 0UL;
|
|
|
|
init_ept_mem_ops(vm);
|
|
vm->arch_vm.nworld_eptp = vm->arch_vm.ept_mem_ops.get_pml4_page(vm->arch_vm.ept_mem_ops.info, 0UL);
|
|
sanitize_pte((uint64_t *)vm->arch_vm.nworld_eptp);
|
|
|
|
/* Only for SOS: Configure VM software information */
|
|
/* For UOS: This VM software information is configure in DM */
|
|
if (is_vm0(vm)) {
|
|
status = prepare_vm0_memmap_and_e820(vm);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
#ifndef CONFIG_EFI_STUB
|
|
status = init_vm_boot_info(vm);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
#endif
|
|
init_iommu_vm0_domain(vm);
|
|
} else {
|
|
/* populate UOS vm fields according to vm_desc */
|
|
vm->sworld_control.flag.supported = vm_desc->sworld_supported;
|
|
if (vm->sworld_control.flag.supported != 0UL) {
|
|
struct memory_ops *ept_mem_ops = &vm->arch_vm.ept_mem_ops;
|
|
ept_mr_add(vm, (uint64_t *)vm->arch_vm.nworld_eptp,
|
|
hva2hpa(ept_mem_ops->get_sworld_memory_base(ept_mem_ops->info)),
|
|
TRUSTY_EPT_REBASE_GPA, TRUSTY_RAM_SIZE, EPT_WB | EPT_RWX);
|
|
}
|
|
|
|
(void)memcpy_s(&vm->GUID[0], sizeof(vm->GUID),
|
|
&vm_desc->GUID[0], sizeof(vm_desc->GUID));
|
|
#ifdef CONFIG_PARTITION_MODE
|
|
ept_mr_add(vm, (uint64_t *)vm->arch_vm.nworld_eptp,
|
|
vm_desc->start_hpa, 0UL, vm_desc->mem_size,
|
|
EPT_RWX|EPT_WB);
|
|
init_vm_boot_info(vm);
|
|
#endif
|
|
}
|
|
|
|
enable_iommu();
|
|
|
|
INIT_LIST_HEAD(&vm->softirq_dev_entry_list);
|
|
spinlock_init(&vm->softirq_dev_lock);
|
|
vm->intr_inject_delay_delta = 0UL;
|
|
|
|
/* Set up IO bit-mask such that VM exit occurs on
|
|
* selected IO ranges
|
|
*/
|
|
setup_io_bitmap(vm);
|
|
|
|
vm_setup_cpu_state(vm);
|
|
|
|
if (is_vm0(vm)) {
|
|
/* Load pm S state data */
|
|
if (vm_load_pm_s_state(vm) == 0) {
|
|
register_pm1ab_handler(vm);
|
|
}
|
|
|
|
/* Create virtual uart */
|
|
vuart_init(vm);
|
|
}
|
|
vpic_init(vm);
|
|
|
|
#ifdef CONFIG_PARTITION_MODE
|
|
/* Create virtual uart */
|
|
if (vm_desc->vm_vuart) {
|
|
vuart_init(vm);
|
|
}
|
|
vrtc_init(vm);
|
|
#endif
|
|
|
|
vpci_init(vm);
|
|
|
|
/* vpic wire_mode default is INTR */
|
|
vm->wire_mode = VPIC_WIRE_INTR;
|
|
|
|
/* Init full emulated vIOAPIC instance */
|
|
vioapic_init(vm);
|
|
|
|
/* Populate return VM handle */
|
|
*rtn_vm = vm;
|
|
vm->sw.io_shared_page = NULL;
|
|
#ifdef CONFIG_IOREQ_POLLING
|
|
/* Now, enable IO completion polling mode for all VMs with CONFIG_IOREQ_POLLING. */
|
|
vm->sw.is_completion_polling = true;
|
|
#endif
|
|
|
|
status = set_vcpuid_entries(vm);
|
|
if (status != 0) {
|
|
goto err;
|
|
}
|
|
|
|
vm->state = VM_CREATED;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
if (vm->arch_vm.nworld_eptp != NULL) {
|
|
(void)memset(vm->arch_vm.nworld_eptp, 0U, CPU_PAGE_SIZE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @pre vm != NULL
|
|
*/
|
|
int32_t shutdown_vm(struct acrn_vm *vm)
|
|
{
|
|
uint16_t i;
|
|
struct acrn_vcpu *vcpu = NULL;
|
|
int32_t ret;
|
|
|
|
pause_vm(vm);
|
|
|
|
/* Only allow shutdown paused vm */
|
|
if (vm->state == VM_PAUSED) {
|
|
foreach_vcpu(i, vm, vcpu) {
|
|
reset_vcpu(vcpu);
|
|
offline_vcpu(vcpu);
|
|
}
|
|
|
|
ptdev_release_all_entries(vm);
|
|
|
|
/* Free EPT allocated resources assigned to VM */
|
|
destroy_ept(vm);
|
|
|
|
/* Free iommu */
|
|
if (vm->iommu != NULL) {
|
|
destroy_iommu_domain(vm->iommu);
|
|
}
|
|
|
|
/* Free vm id */
|
|
free_vm_id(vm);
|
|
|
|
vpci_cleanup(vm);
|
|
ret = 0;
|
|
} else {
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
/* Return status to caller */
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* * @pre vm != NULL
|
|
*/
|
|
int start_vm(struct acrn_vm *vm)
|
|
{
|
|
struct acrn_vcpu *vcpu = NULL;
|
|
|
|
vm->state = VM_STARTED;
|
|
|
|
/* Only start BSP (vid = 0) and let BSP start other APs */
|
|
vcpu = vcpu_from_vid(vm, 0U);
|
|
schedule_vcpu(vcpu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* * @pre vm != NULL
|
|
*/
|
|
int32_t reset_vm(struct acrn_vm *vm)
|
|
{
|
|
uint16_t i;
|
|
struct acrn_vcpu *vcpu = NULL;
|
|
int32_t ret;
|
|
|
|
if (vm->state == VM_PAUSED) {
|
|
foreach_vcpu(i, vm, vcpu) {
|
|
reset_vcpu(vcpu);
|
|
}
|
|
|
|
if (is_vm0(vm)) {
|
|
(void )vm_sw_loader(vm);
|
|
}
|
|
|
|
reset_vm_ioreqs(vm);
|
|
vioapic_reset(vm_ioapic(vm));
|
|
destroy_secure_world(vm, false);
|
|
vm->sworld_control.flag.active = 0UL;
|
|
ret = 0;
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* * @pre vm != NULL
|
|
*/
|
|
void pause_vm(struct acrn_vm *vm)
|
|
{
|
|
uint16_t i;
|
|
struct acrn_vcpu *vcpu = NULL;
|
|
|
|
if (vm->state != VM_PAUSED) {
|
|
vm->state = VM_PAUSED;
|
|
|
|
foreach_vcpu(i, vm, vcpu) {
|
|
pause_vcpu(vcpu, VCPU_ZOMBIE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* * @pre vm != NULL
|
|
*/
|
|
void resume_vm(struct acrn_vm *vm)
|
|
{
|
|
uint16_t i;
|
|
struct acrn_vcpu *vcpu = NULL;
|
|
|
|
foreach_vcpu(i, vm, vcpu) {
|
|
resume_vcpu(vcpu);
|
|
}
|
|
|
|
vm->state = VM_STARTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Resume vm from S3 state
|
|
*
|
|
* To resume vm after guest enter S3 state:
|
|
* - reset BSP
|
|
* - BSP will be put to real mode with entry set as wakeup_vec
|
|
* - init_vmcs BSP. We could call init_vmcs here because we know current
|
|
* pcpu is mapped to BSP of vm.
|
|
*
|
|
* @vm[in] vm pointer to vm data structure
|
|
* @wakeup_vec[in] The resume address of vm
|
|
*
|
|
* @pre vm != NULL
|
|
*/
|
|
void resume_vm_from_s3(struct acrn_vm *vm, uint32_t wakeup_vec)
|
|
{
|
|
struct acrn_vcpu *bsp = vcpu_from_vid(vm, 0U);
|
|
|
|
vm->state = VM_STARTED;
|
|
|
|
reset_vcpu(bsp);
|
|
|
|
/* When SOS resume from S3, it will return to real mode
|
|
* with entry set to wakeup_vec.
|
|
*/
|
|
set_ap_entry(bsp, wakeup_vec);
|
|
|
|
init_vmcs(bsp);
|
|
schedule_vcpu(bsp);
|
|
}
|
|
|
|
#ifdef CONFIG_PARTITION_MODE
|
|
/* Create vm/vcpu for vm */
|
|
int prepare_vm(uint16_t pcpu_id)
|
|
{
|
|
int ret = 0;
|
|
uint16_t i;
|
|
struct acrn_vm *vm = NULL;
|
|
struct vm_description *vm_desc = NULL;
|
|
bool is_vm_bsp;
|
|
|
|
vm_desc = pcpu_vm_desc_map[pcpu_id].vm_desc_ptr;
|
|
is_vm_bsp = pcpu_vm_desc_map[pcpu_id].is_bsp;
|
|
|
|
if (is_vm_bsp) {
|
|
ret = create_vm(vm_desc, &vm);
|
|
ASSERT(ret == 0, "VM creation failed!");
|
|
|
|
mptable_build(vm);
|
|
|
|
prepare_vcpu(vm, vm_desc->vm_pcpu_ids[0]);
|
|
|
|
/* Prepare the AP for vm */
|
|
for (i = 1U; i < vm_desc->vm_hw_num_cores; i++)
|
|
prepare_vcpu(vm, vm_desc->vm_pcpu_ids[i]);
|
|
|
|
if (vm_sw_loader == NULL) {
|
|
vm_sw_loader = general_sw_loader;
|
|
}
|
|
|
|
(void )vm_sw_loader(vm);
|
|
|
|
/* start vm BSP automatically */
|
|
start_vm(vm);
|
|
|
|
pr_acrnlog("Start VM%x", vm_desc->vm_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#else
|
|
|
|
/* Create vm/vcpu for vm0 */
|
|
int prepare_vm0(void)
|
|
{
|
|
int err;
|
|
uint16_t i;
|
|
struct acrn_vm *vm = NULL;
|
|
struct vm_description vm0_desc;
|
|
|
|
(void)memset((void *)&vm0_desc, 0U, sizeof(vm0_desc));
|
|
vm0_desc.vm_hw_num_cores = phys_cpu_num;
|
|
|
|
err = create_vm(&vm0_desc, &vm);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
/* Allocate all cpus to vm0 at the beginning */
|
|
for (i = 0U; i < vm0_desc.vm_hw_num_cores; i++) {
|
|
err = prepare_vcpu(vm, i);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (vm_sw_loader == NULL) {
|
|
vm_sw_loader = general_sw_loader;
|
|
}
|
|
|
|
if (is_vm0(vm)) {
|
|
(void )vm_sw_loader(vm);
|
|
}
|
|
|
|
/* start vm0 BSP automatically */
|
|
err = start_vm(vm);
|
|
|
|
pr_acrnlog("Start VM0");
|
|
|
|
return err;
|
|
}
|
|
|
|
int prepare_vm(uint16_t pcpu_id)
|
|
{
|
|
int err = 0;
|
|
|
|
/* prepare vm0 if pcpu_id is BOOT_CPU_ID */
|
|
if (pcpu_id == BOOT_CPU_ID) {
|
|
err = prepare_vm0();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#endif
|