mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-12-20 20:02:35 +00:00
Previously the load GPA of LaaG boot params like zeropage/cmdline and initgdt are all hard-coded, this would bring potential LaaG boot issues. The patch will try to fix this issue by finding a 32KB load_params memory block for LaaG to store these guest boot params. For other guest with raw image, in general only vgdt need to be cared of so the load_params will be put at 0x800 since it is a common place that most guests won't touch for entering protected mode. Tracked-On: #5626 Signed-off-by: Victor Sun <victor.sun@intel.com> Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com>
282 lines
8.4 KiB
C
282 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <asm/guest/vm.h>
|
|
#include <asm/e820.h>
|
|
#include <asm/zeropage.h>
|
|
#include <asm/guest/ept.h>
|
|
#include <asm/mmu.h>
|
|
#include <boot.h>
|
|
#include <errno.h>
|
|
#include <logmsg.h>
|
|
|
|
/* Define a 32KB memory block to store LaaG VM load params in guest address space
|
|
* The params including:
|
|
* Init GDT entries : 1KB (must be 8byte aligned)
|
|
* Linux Zeropage : 4KB
|
|
* Boot cmdline : 2KB
|
|
* EFI memory map : 12KB
|
|
* Reserved region for trampoline code : 8KB
|
|
* Each param should keep 8byte aligned and the total region size should be less than 32KB
|
|
* so that it could be put below MEM_1M.
|
|
* Please note in Linux VM, the last 8KB space below MEM_1M is for trampoline code. The block
|
|
* should be able to accommodate it and so that avoid the trampoline corruption.
|
|
*/
|
|
#define BZIMG_LOAD_PARAMS_SIZE (MEM_4K * 8)
|
|
#define BZIMG_INITGDT_GPA(load_params_gpa) (load_params_gpa + 0UL)
|
|
#define BZIMG_ZEROPAGE_GPA(load_params_gpa) (load_params_gpa + MEM_1K)
|
|
#define BZIMG_CMDLINE_GPA(load_params_gpa) (load_params_gpa + MEM_1K + MEM_4K)
|
|
#define BZIMG_EFIMMAP_GPA(load_params_gpa) (load_params_gpa + MEM_1K + MEM_4K + MEM_2K)
|
|
|
|
/**
|
|
* @pre zp != NULL && vm != NULL
|
|
*/
|
|
static uint32_t create_zeropage_e820(struct zero_page *zp, const struct acrn_vm *vm)
|
|
{
|
|
uint32_t entry_num = vm->e820_entry_num;
|
|
struct e820_entry *zp_e820 = zp->entries;
|
|
const struct e820_entry *vm_e820 = vm->e820_entries;
|
|
|
|
if ((zp_e820 == NULL) || (vm_e820 == NULL) || (entry_num == 0U) || (entry_num > E820_MAX_ENTRIES)) {
|
|
pr_err("e820 create error");
|
|
entry_num = 0U;
|
|
} else {
|
|
(void)memcpy_s((void *)zp_e820, entry_num * sizeof(struct e820_entry),
|
|
(void *)vm_e820, entry_num * sizeof(struct e820_entry));
|
|
}
|
|
return entry_num;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
* @pre (vm->min_mem_addr <= kernel_load_addr) && (kernel_load_addr < vm->max_mem_addr)
|
|
*/
|
|
static uint64_t create_zero_page(struct acrn_vm *vm, uint64_t load_params_gpa)
|
|
{
|
|
struct zero_page *zeropage, *hva;
|
|
struct sw_kernel_info *sw_kernel = &(vm->sw.kernel_info);
|
|
struct sw_module_info *bootargs_info = &(vm->sw.bootargs_info);
|
|
struct sw_module_info *ramdisk_info = &(vm->sw.ramdisk_info);
|
|
uint64_t gpa, addr;
|
|
|
|
gpa = BZIMG_ZEROPAGE_GPA(load_params_gpa);
|
|
hva = (struct zero_page *)gpa2hva(vm, gpa);
|
|
zeropage = hva;
|
|
|
|
stac();
|
|
/* clear the zeropage */
|
|
(void)memset(zeropage, 0U, MEM_2K);
|
|
|
|
#ifdef CONFIG_MULTIBOOT2
|
|
if (is_sos_vm(vm)) {
|
|
struct acrn_boot_info *abi = get_acrn_boot_info();
|
|
|
|
if (boot_from_uefi(abi)) {
|
|
struct efi_info *sos_efi_info = &zeropage->boot_efi_info;
|
|
|
|
sos_efi_info->efi_loader_signature = 0x34364c45; /* "EL64" */
|
|
sos_efi_info->efi_memdesc_version = abi->efi_info.memdesc_version;
|
|
sos_efi_info->efi_memdesc_size = abi->efi_info.memdesc_size;
|
|
sos_efi_info->efi_memmap_size = abi->efi_info.memmap_size;
|
|
sos_efi_info->efi_memmap = (uint32_t)(uint64_t)abi->efi_info.memmap;
|
|
sos_efi_info->efi_memmap_hi = (uint32_t)((uint64_t)abi->efi_info.memmap >> 32U);
|
|
sos_efi_info->efi_systab = (uint32_t)(uint64_t)abi->efi_info.system_table;
|
|
sos_efi_info->efi_systab_hi = (uint32_t)((uint64_t)abi->efi_info.system_table >> 32U);
|
|
}
|
|
}
|
|
#endif
|
|
/* copy part of the header into the zero page */
|
|
hva = (struct zero_page *)gpa2hva(vm, (uint64_t)sw_kernel->kernel_load_addr);
|
|
(void)memcpy_s(&(zeropage->hdr), sizeof(zeropage->hdr),
|
|
&(hva->hdr), sizeof(hva->hdr));
|
|
|
|
/* See if kernel has a RAM disk */
|
|
if (ramdisk_info->src_addr != NULL) {
|
|
/* Copy ramdisk load_addr and size in zeropage header structure
|
|
*/
|
|
addr = (uint64_t)ramdisk_info->load_addr;
|
|
zeropage->hdr.ramdisk_addr = (uint32_t)addr;
|
|
zeropage->hdr.ramdisk_size = (uint32_t)ramdisk_info->size;
|
|
}
|
|
|
|
/* Copy bootargs load_addr in zeropage header structure */
|
|
addr = (uint64_t)bootargs_info->load_addr;
|
|
zeropage->hdr.bootargs_addr = (uint32_t)addr;
|
|
|
|
/* set constant arguments in zero page */
|
|
zeropage->hdr.loader_type = 0xffU;
|
|
zeropage->hdr.load_flags |= (1U << 5U); /* quiet */
|
|
|
|
/* Create/add e820 table entries in zeropage */
|
|
zeropage->e820_nentries = (uint8_t)create_zeropage_e820(zeropage, vm);
|
|
clac();
|
|
|
|
/* Return Physical Base Address of zeropage */
|
|
return gpa;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
static void prepare_loading_bzimage(struct acrn_vm *vm, struct acrn_vcpu *vcpu, uint64_t load_params_gpa)
|
|
{
|
|
uint32_t i;
|
|
uint32_t kernel_entry_offset;
|
|
struct zero_page *zeropage;
|
|
struct sw_kernel_info *sw_kernel = &(vm->sw.kernel_info);
|
|
|
|
/* calculate the kernel entry point */
|
|
zeropage = (struct zero_page *)sw_kernel->kernel_src_addr;
|
|
stac();
|
|
kernel_entry_offset = (uint32_t)(zeropage->hdr.setup_sects + 1U) * 512U;
|
|
clac();
|
|
if (vcpu->arch.cpu_mode == CPU_MODE_64BIT) {
|
|
/* 64bit entry is the 512bytes after the start */
|
|
kernel_entry_offset += 512U;
|
|
}
|
|
|
|
sw_kernel->kernel_entry_addr = (void *)((uint64_t)sw_kernel->kernel_load_addr + kernel_entry_offset);
|
|
|
|
/* Documentation states: ebx=0, edi=0, ebp=0, esi=ptr to
|
|
* zeropage
|
|
*/
|
|
for (i = 0U; i < NUM_GPRS; i++) {
|
|
vcpu_set_gpreg(vcpu, i, 0UL);
|
|
}
|
|
|
|
/* Create Zeropage and copy Physical Base Address of Zeropage
|
|
* in RSI
|
|
*/
|
|
vcpu_set_gpreg(vcpu, CPU_REG_RSI, create_zero_page(vm, load_params_gpa));
|
|
pr_info("%s, RSI pointing to zero page for VM %d at GPA %X",
|
|
__func__, vm->vm_id, vcpu_get_gpreg(vcpu, CPU_REG_RSI));
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
static void prepare_loading_rawimage(struct acrn_vm *vm)
|
|
{
|
|
struct sw_kernel_info *sw_kernel = &(vm->sw.kernel_info);
|
|
const struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
|
|
sw_kernel->kernel_entry_addr = (void *)vm_config->os_config.kernel_entry_addr;
|
|
}
|
|
|
|
/**
|
|
* @pre sw_module != NULL
|
|
*/
|
|
static void load_sw_module(struct acrn_vm *vm, struct sw_module_info *sw_module)
|
|
{
|
|
if (sw_module->size != 0) {
|
|
(void)copy_to_gpa(vm, sw_module->src_addr, (uint64_t)sw_module->load_addr, sw_module->size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
static void load_sw_modules(struct acrn_vm *vm, uint64_t load_params_gpa)
|
|
{
|
|
struct sw_kernel_info *sw_kernel = &(vm->sw.kernel_info);
|
|
struct sw_module_info *bootargs_info = &(vm->sw.bootargs_info);
|
|
struct sw_module_info *ramdisk_info = &(vm->sw.ramdisk_info);
|
|
struct sw_module_info *acpi_info = &(vm->sw.acpi_info);
|
|
|
|
pr_dbg("Loading guest to run-time location");
|
|
|
|
/* Copy the guest kernel image to its run-time location */
|
|
(void)copy_to_gpa(vm, sw_kernel->kernel_src_addr,
|
|
(uint64_t)sw_kernel->kernel_load_addr, sw_kernel->kernel_size);
|
|
|
|
if (vm->sw.kernel_type == KERNEL_BZIMAGE) {
|
|
|
|
load_sw_module(vm, ramdisk_info);
|
|
|
|
bootargs_info->load_addr = (void *)BZIMG_CMDLINE_GPA(load_params_gpa);
|
|
|
|
load_sw_module(vm, bootargs_info);
|
|
}
|
|
|
|
/* Copy Guest OS ACPI to its load location */
|
|
load_sw_module(vm, acpi_info);
|
|
|
|
}
|
|
|
|
static int32_t vm_bzimage_loader(struct acrn_vm *vm)
|
|
{
|
|
int32_t ret = 0;
|
|
/* get primary vcpu */
|
|
struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BSP_CPU_ID);
|
|
uint64_t load_params_gpa = find_space_from_ve820(vm, BZIMG_LOAD_PARAMS_SIZE, MEM_4K, MEM_1M);
|
|
|
|
if (load_params_gpa != INVALID_GPA) {
|
|
/* We boot bzImage from protected mode directly */
|
|
init_vcpu_protect_mode_regs(vcpu, BZIMG_INITGDT_GPA(load_params_gpa));
|
|
|
|
load_sw_modules(vm, load_params_gpa);
|
|
|
|
prepare_loading_bzimage(vm, vcpu, load_params_gpa);
|
|
} else {
|
|
ret = -ENOMEM;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t vm_rawimage_loader(struct acrn_vm *vm)
|
|
{
|
|
int32_t ret = 0;
|
|
uint64_t load_params_gpa = 0x800;
|
|
|
|
/*
|
|
* TODO:
|
|
* - We need to initialize the guest bsp registers according to
|
|
* guest boot mode (real mode vs protect mode)
|
|
* - The memory layout usage is unclear, only GDT might be needed as its boot param.
|
|
* currently we only support Zephyr which has no needs on cmdline/e820/efimmap/etc.
|
|
* hardcode the vGDT GPA to 0x800 where is not used by Zephyr so far;
|
|
*/
|
|
init_vcpu_protect_mode_regs(vcpu_from_vid(vm, BSP_CPU_ID), load_params_gpa);
|
|
|
|
load_sw_modules(vm, load_params_gpa);
|
|
|
|
prepare_loading_rawimage(vm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
int32_t vm_sw_loader(struct acrn_vm *vm)
|
|
{
|
|
int32_t ret = 0;
|
|
/* get primary vcpu */
|
|
struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BSP_CPU_ID);
|
|
|
|
if (vm->sw.kernel_type == KERNEL_BZIMAGE) {
|
|
|
|
ret = vm_bzimage_loader(vm);
|
|
|
|
} else if (vm->sw.kernel_type == KERNEL_ZEPHYR){
|
|
|
|
ret = vm_rawimage_loader(vm);
|
|
|
|
} else {
|
|
pr_err("%s, Loading VM SW failed", __func__);
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* Set VCPU entry point to kernel entry */
|
|
vcpu_set_rip(vcpu, (uint64_t)vm->sw.kernel_info.kernel_entry_addr);
|
|
pr_info("%s, VM %hu VCPU %hu Entry: 0x%016lx ", __func__, vm->vm_id, vcpu->vcpu_id,
|
|
vm->sw.kernel_info.kernel_entry_addr);
|
|
}
|
|
|
|
return ret;
|
|
}
|