mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-31 03:15:42 +00:00
Previously the bootargs of SOS_VM is stored in a text file and stitched into multiboot mods[0].string whereas the bootargs of PRE_LAUNCHED_VM is stored in vm_configurations.c. Given the mods[].string will be used to store Kernel image signature under hybrid mode, move the bootargs of SOS_VM to vm configurations also to make it consistent with PRE_LAUNCHED_VM; Tracked-On: #3214 Signed-off-by: Victor Sun <victor.sun@intel.com> Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com>
310 lines
9.3 KiB
C
310 lines
9.3 KiB
C
/*
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <types.h>
|
|
#include <rtl.h>
|
|
#include <errno.h>
|
|
#include <per_cpu.h>
|
|
#include <irq.h>
|
|
#include <boot_context.h>
|
|
#include <sprintf.h>
|
|
#include <multiboot.h>
|
|
#include <pgtable.h>
|
|
#include <zeropage.h>
|
|
#include <seed.h>
|
|
#include <mmu.h>
|
|
#include <vm.h>
|
|
#include <logmsg.h>
|
|
#include <deprivilege_boot.h>
|
|
|
|
#define ACRN_DBG_BOOT 6U
|
|
|
|
#define MAX_BOOT_PARAMS_LEN 64U
|
|
|
|
/* There are two sources for sos_vm kernel cmdline:
|
|
* - cmdline from direct boot mbi->cmdline
|
|
* - cmdline from acrn stitching tool. mod[0].mm_string
|
|
* We need to merge them together
|
|
*/
|
|
static char kernel_cmdline[MAX_BOOTARGS_SIZE + 1U];
|
|
|
|
/* now modules support: FIRMWARE & RAMDISK & SeedList */
|
|
static void parse_other_modules(struct acrn_vm *vm, const struct multiboot_module *mods, uint32_t mods_count)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < mods_count; i++) {
|
|
uint32_t type_len;
|
|
const char *start = (char *)hpa2hva((uint64_t)mods[i].mm_string);
|
|
const char *end;
|
|
void *mod_addr = hpa2hva((uint64_t)mods[i].mm_mod_start);
|
|
uint32_t mod_size = mods[i].mm_mod_end - mods[i].mm_mod_start;
|
|
|
|
dev_dbg(ACRN_DBG_BOOT, "other mod-%d start=0x%x, end=0x%x",
|
|
i, mods[i].mm_mod_start, mods[i].mm_mod_end);
|
|
dev_dbg(ACRN_DBG_BOOT, "cmd addr=0x%x, str=%s",
|
|
mods[i].mm_string, start);
|
|
|
|
while (*start == ' ') {
|
|
start++;
|
|
}
|
|
|
|
end = start;
|
|
while (((*end) != ' ') && ((*end) != '\0')) {
|
|
end++;
|
|
}
|
|
|
|
type_len = end - start;
|
|
if (strncmp("FIRMWARE", start, type_len) == 0) {
|
|
char dyn_bootargs[100] = {'\0'};
|
|
void *load_addr = gpa2hva(vm, (uint64_t)vm->sw.bootargs_info.load_addr);
|
|
uint32_t args_size = vm->sw.bootargs_info.size;
|
|
static int32_t copy_once = 1;
|
|
|
|
start = end + 1; /*it is fw name for boot args */
|
|
snprintf(dyn_bootargs, 100U, " %s=0x%x@0x%x ", start, mod_size, mod_addr);
|
|
dev_dbg(ACRN_DBG_BOOT, "fw-%d: %s", i, dyn_bootargs);
|
|
|
|
/*copy boot args to load addr, set src=load addr*/
|
|
if (copy_once != 0) {
|
|
copy_once = 0;
|
|
(void)strncpy_s(load_addr, MAX_BOOTARGS_SIZE + 1U,
|
|
(const char *)vm->sw.bootargs_info.src_addr,
|
|
vm->sw.bootargs_info.size);
|
|
vm->sw.bootargs_info.src_addr = load_addr;
|
|
}
|
|
|
|
(void)strncpy_s(load_addr + args_size, 100U, dyn_bootargs, 100U);
|
|
vm->sw.bootargs_info.size = strnlen_s(load_addr, MAX_BOOTARGS_SIZE);
|
|
|
|
} else if (strncmp("RAMDISK", start, type_len) == 0) {
|
|
vm->sw.ramdisk_info.src_addr = mod_addr;
|
|
vm->sw.ramdisk_info.load_addr = vm->sw.kernel_info.kernel_load_addr +
|
|
vm->sw.kernel_info.kernel_size;
|
|
vm->sw.ramdisk_info.load_addr =
|
|
(void *)round_page_up((uint64_t)vm->sw.ramdisk_info.load_addr);
|
|
vm->sw.ramdisk_info.size = mod_size;
|
|
} else {
|
|
pr_warn("not support mod, cmd: %s", start);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL && cmdline != NULL && cmdstr != NULL
|
|
*/
|
|
static void merge_cmdline(const struct acrn_vm *vm, const char *cmdline, const char *cmdstr)
|
|
{
|
|
char *cmd_dst = kernel_cmdline;
|
|
uint32_t cmdline_len, cmdstr_len;
|
|
uint32_t dst_avail; /* available room for cmd_dst[] */
|
|
uint32_t dst_len; /* the actual number of characters that are copied */
|
|
|
|
/*
|
|
* Append seed argument for SOS
|
|
* seed_arg string ends with a white space and '\0', so no aditional delimiter is needed
|
|
*/
|
|
append_seed_arg(cmd_dst, is_sos_vm(vm));
|
|
dst_len = strnlen_s(cmd_dst, MAX_BOOTARGS_SIZE);
|
|
dst_avail = MAX_BOOTARGS_SIZE + 1U - dst_len;
|
|
cmd_dst += dst_len;
|
|
|
|
cmdline_len = strnlen_s(cmdline, MAX_BOOTARGS_SIZE);
|
|
cmdstr_len = strnlen_s(cmdstr, MAX_BOOTARGS_SIZE);
|
|
|
|
/* reserve one character for the delimiter between 2 strings (one white space) */
|
|
if ((cmdline_len + cmdstr_len + 1U) >= dst_avail) {
|
|
panic("Multiboot bootarg string too long");
|
|
} else {
|
|
/* copy mbi->mi_cmdline */
|
|
(void)strncpy_s(cmd_dst, dst_avail, cmdline, cmdline_len);
|
|
dst_len = strnlen_s(cmd_dst, dst_avail);
|
|
dst_avail -= dst_len;
|
|
cmd_dst += dst_len;
|
|
|
|
/* overwrite '\0' with a white space */
|
|
(void)strncpy_s(cmd_dst, dst_avail, " ", 1U);
|
|
dst_avail -= 1U;
|
|
cmd_dst += 1U;
|
|
|
|
/* copy vm_config->os_config.bootargs */
|
|
(void)strncpy_s(cmd_dst, dst_avail, cmdstr, cmdstr_len);
|
|
}
|
|
}
|
|
|
|
static void *get_kernel_load_addr(struct acrn_vm *vm)
|
|
{
|
|
void *load_addr = NULL;
|
|
struct vm_sw_info *sw_info = &vm->sw;
|
|
struct zero_page *zeropage;
|
|
|
|
switch (sw_info->kernel_type) {
|
|
case KERNEL_BZIMAGE:
|
|
/* According to the explaination for pref_address
|
|
* in Documentation/x86/boot.txt, a relocating
|
|
* bootloader should attempt to load kernel at pref_address
|
|
* if possible. A non-relocatable kernel will unconditionally
|
|
* move itself and to run at this address, so no need to copy
|
|
* kernel to perf_address by bootloader, if kernel is
|
|
* non-relocatable.
|
|
*/
|
|
zeropage = (struct zero_page *)sw_info->kernel_info.kernel_src_addr;
|
|
if (zeropage->hdr.relocatable_kernel != 0U) {
|
|
zeropage = (struct zero_page *)zeropage->hdr.pref_addr;
|
|
}
|
|
load_addr = (void *)zeropage;
|
|
break;
|
|
default:
|
|
pr_err("Unsupported Kernel type.");
|
|
break;
|
|
}
|
|
if (load_addr == NULL) {
|
|
pr_err("Could not get kernel load addr of VM %d .", vm->vm_id);
|
|
}
|
|
return load_addr;
|
|
}
|
|
|
|
static int32_t init_general_vm_boot_info(struct acrn_vm *vm)
|
|
{
|
|
struct multiboot_module *mods = NULL;
|
|
struct multiboot_info *mbi = NULL;
|
|
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
int32_t ret = -EINVAL;
|
|
|
|
if (boot_regs[0] != MULTIBOOT_INFO_MAGIC) {
|
|
panic("no multiboot info found");
|
|
} else {
|
|
mbi = (struct multiboot_info *)hpa2hva((uint64_t)boot_regs[1]);
|
|
|
|
if (mbi != NULL) {
|
|
stac();
|
|
dev_dbg(ACRN_DBG_BOOT, "Multiboot detected, flag=0x%x", mbi->mi_flags);
|
|
if ((mbi->mi_flags & MULTIBOOT_INFO_HAS_MODS) == 0U) {
|
|
panic("no sos kernel info found");
|
|
clac();
|
|
} else {
|
|
|
|
dev_dbg(ACRN_DBG_BOOT, "mod counts=%d\n", mbi->mi_mods_count);
|
|
|
|
/* mod[0] is for kernel, other mod for ramdisk/firmware info*/
|
|
mods = (struct multiboot_module *)hpa2hva((uint64_t)mbi->mi_mods_addr);
|
|
|
|
dev_dbg(ACRN_DBG_BOOT, "mod0 start=0x%x, end=0x%x",
|
|
mods[0].mm_mod_start, mods[0].mm_mod_end);
|
|
|
|
vm->sw.kernel_type = vm_config->os_config.kernel_type;
|
|
vm->sw.kernel_info.kernel_src_addr = hpa2hva((uint64_t)mods[0].mm_mod_start);
|
|
vm->sw.kernel_info.kernel_size = mods[0].mm_mod_end - mods[0].mm_mod_start;
|
|
vm->sw.kernel_info.kernel_load_addr = get_kernel_load_addr(vm);
|
|
|
|
if (vm_config->load_order == PRE_LAUNCHED_VM) {
|
|
vm->sw.bootargs_info.src_addr = (void *)vm_config->os_config.bootargs;
|
|
vm->sw.bootargs_info.size =
|
|
strnlen_s(vm_config->os_config.bootargs, MAX_BOOTARGS_SIZE);
|
|
} else {
|
|
if ((mbi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE) != 0U) {
|
|
/*
|
|
* If there is cmdline from mbi->mi_cmdline, merge it with
|
|
* vm_config->os_config.bootargs
|
|
*/
|
|
merge_cmdline(vm, hpa2hva((uint64_t)mbi->mi_cmdline),
|
|
vm_config->os_config.bootargs);
|
|
|
|
vm->sw.bootargs_info.src_addr = kernel_cmdline;
|
|
vm->sw.bootargs_info.size =
|
|
strnlen_s(kernel_cmdline, MAX_BOOTARGS_SIZE);
|
|
} else {
|
|
vm->sw.bootargs_info.src_addr = vm_config->os_config.bootargs;
|
|
vm->sw.bootargs_info.size =
|
|
strnlen_s(vm_config->os_config.bootargs, MAX_BOOTARGS_SIZE);
|
|
}
|
|
}
|
|
|
|
/* Kernel bootarg and zero page are right before the kernel image */
|
|
vm->sw.bootargs_info.load_addr =
|
|
vm->sw.kernel_info.kernel_load_addr - (MEM_1K * 8U);
|
|
|
|
if (mbi->mi_mods_count > 1U) {
|
|
/*parse other modules, like firmware /ramdisk */
|
|
parse_other_modules(vm, mods + 1, mbi->mi_mods_count - 1);
|
|
}
|
|
clac();
|
|
if (vm->sw.kernel_info.kernel_load_addr != NULL) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void depri_boot_spurious_handler(uint32_t vector)
|
|
{
|
|
if (get_pcpu_id() == BOOT_CPU_ID) {
|
|
struct acrn_vcpu *vcpu = per_cpu(vcpu, BOOT_CPU_ID);
|
|
|
|
if (vcpu != NULL) {
|
|
vlapic_set_intr(vcpu, vector, LAPIC_TRIG_EDGE);
|
|
} else {
|
|
pr_err("%s vcpu or vlapic is not ready, interrupt lost\n", __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int32_t depri_boot_sw_loader(struct acrn_vm *vm)
|
|
{
|
|
int32_t ret = 0;
|
|
/* get primary vcpu */
|
|
struct acrn_vcpu *vcpu = vcpu_from_vid(vm, BOOT_CPU_ID);
|
|
struct acrn_vcpu_regs *vcpu_regs = &boot_context;
|
|
const struct depri_boot_context *depri_boot_ctx = get_depri_boot_ctx();
|
|
const struct lapic_regs *depri_boot_lapic_regs = get_depri_boot_lapic_regs();
|
|
|
|
pr_dbg("Loading guest to run-time location");
|
|
|
|
vlapic_restore(vcpu_vlapic(vcpu), depri_boot_lapic_regs);
|
|
|
|
/* For UEFI platform, the bsp init regs come from two places:
|
|
* 1. saved in depri_boot: gpregs, rip
|
|
* 2. saved when HV started: other registers
|
|
* We copy the info saved in depri_boot to boot_context and
|
|
* init bsp with boot_context.
|
|
*/
|
|
memcpy_s(&(vcpu_regs->gprs), sizeof(struct acrn_gp_regs),
|
|
&(depri_boot_ctx->vcpu_regs.gprs), sizeof(struct acrn_gp_regs));
|
|
|
|
vcpu_regs->rip = depri_boot_ctx->vcpu_regs.rip;
|
|
set_vcpu_regs(vcpu, vcpu_regs);
|
|
|
|
/* defer irq enabling till vlapic is ready */
|
|
spurious_handler = depri_boot_spurious_handler;
|
|
CPU_IRQ_ENABLE();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @param[inout] vm pointer to a vm descriptor
|
|
*
|
|
* @retval 0 on success
|
|
* @retval -EINVAL on invalid parameters
|
|
*
|
|
* @pre vm != NULL
|
|
*/
|
|
int32_t init_vm_boot_info(struct acrn_vm *vm)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (is_sos_vm(vm) && (get_sos_boot_mode() == DEPRI_BOOT_MODE)) {
|
|
vm_sw_loader = depri_boot_sw_loader;
|
|
} else {
|
|
vm_sw_loader = direct_boot_sw_loader;
|
|
ret = init_general_vm_boot_info(vm);
|
|
}
|
|
|
|
return ret;
|
|
}
|