mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-11 09:55:24 +00:00
In grub 2.02, the flag of MULTIBOOT_INFO_HAS_CMDLINE is set even there is no cmdline was configured. So we need to validate the content of cmdline in multiboot info. If there is no cmdline exist, we should not do merge cmdline for SOS VM. Tracked-On: #3214 Signed-off-by: Victor Sun <victor.sun@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
337 lines
9.8 KiB
C
337 lines
9.8 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>
|
|
#include <vboot_info.h>
|
|
|
|
#define ACRN_DBG_BOOT 6U
|
|
|
|
#define MAX_BOOT_PARAMS_LEN 64U
|
|
#define INVALID_MOD_IDX 0xFFFFU
|
|
|
|
/**
|
|
* @pre vm != NULL && mbi != NULL
|
|
*/
|
|
static void init_vm_ramdisk_info(struct acrn_vm *vm, const struct multiboot_module *mod)
|
|
{
|
|
void *mod_addr = hpa2hva((uint64_t)mod->mm_mod_start);
|
|
|
|
if ((mod_addr != NULL) && (mod->mm_mod_end > mod->mm_mod_start)) {
|
|
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->mm_mod_end - mod->mm_mod_start;
|
|
}
|
|
}
|
|
|
|
/* 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];
|
|
|
|
/**
|
|
* @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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
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;
|
|
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
|
|
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;
|
|
case KERNEL_ZEPHYR:
|
|
load_addr = (void *)vm_config->os_config.kernel_load_addr;
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL && mod != NULL
|
|
*/
|
|
static int32_t init_vm_kernel_info(struct acrn_vm *vm, const struct multiboot_module *mod)
|
|
{
|
|
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
|
|
dev_dbg(ACRN_DBG_BOOT, "kernel mod start=0x%x, end=0x%x",
|
|
mod->mm_mod_start, mod->mm_mod_end);
|
|
|
|
vm->sw.kernel_type = vm_config->os_config.kernel_type;
|
|
vm->sw.kernel_info.kernel_src_addr = hpa2hva((uint64_t)mod->mm_mod_start);
|
|
if ((vm->sw.kernel_info.kernel_src_addr != NULL) && (mod->mm_mod_end > mod->mm_mod_start)){
|
|
vm->sw.kernel_info.kernel_size = mod->mm_mod_end - mod->mm_mod_start;
|
|
vm->sw.kernel_info.kernel_load_addr = get_kernel_load_addr(vm);
|
|
}
|
|
|
|
return (vm->sw.kernel_info.kernel_load_addr == NULL) ? (-EINVAL) : 0;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL && mbi != NULL
|
|
*/
|
|
static void init_vm_bootargs_info(struct acrn_vm *vm, const struct multiboot_info *mbi)
|
|
{
|
|
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
char *bootargs = vm_config->os_config.bootargs;
|
|
|
|
if (vm_config->load_order == PRE_LAUNCHED_VM) {
|
|
vm->sw.bootargs_info.src_addr = bootargs;
|
|
vm->sw.bootargs_info.size = strnlen_s(bootargs, MAX_BOOTARGS_SIZE);
|
|
} else {
|
|
/* vm_config->load_order == SOS_VM */
|
|
if (((mbi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE) != 0U)
|
|
&& (*(char *)hpa2hva(mbi->mi_cmdline) != 0)) {
|
|
/*
|
|
* 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), 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 = bootargs;
|
|
vm->sw.bootargs_info.size = strnlen_s(bootargs, MAX_BOOTARGS_SIZE);
|
|
}
|
|
}
|
|
|
|
/* Kernel bootarg and zero page are right before the kernel image */
|
|
if (vm->sw.bootargs_info.size > 0U) {
|
|
vm->sw.bootargs_info.load_addr = vm->sw.kernel_info.kernel_load_addr - (MEM_1K * 8U);
|
|
} else {
|
|
vm->sw.bootargs_info.load_addr = NULL;
|
|
}
|
|
}
|
|
|
|
/* @pre mods != NULL
|
|
*/
|
|
static uint32_t get_mod_idx_by_tag(const struct multiboot_module *mods, uint32_t mods_count, const char *tag)
|
|
{
|
|
uint32_t i, ret = INVALID_MOD_IDX;
|
|
uint32_t tag_len = strnlen_s(tag, MAX_MOD_TAG_LEN);
|
|
|
|
for (i = 0U; i < mods_count; i++) {
|
|
const char *mm_string = (char *)hpa2hva((uint64_t)mods[i].mm_string);
|
|
uint32_t mm_str_len = strnlen_s(mm_string, MAX_MOD_TAG_LEN);
|
|
|
|
/* when do file stitch by tool, the tag in mm_string might be followed with 0x0d or 0x0a */
|
|
if ((mm_str_len >= tag_len) && (strncmp(mm_string, tag, tag_len) == 0)
|
|
&& ((*(mm_string + tag_len) == 0x0d)
|
|
|| (*(mm_string + tag_len) == 0x0a)
|
|
|| (*(mm_string + tag_len) == 0))){
|
|
ret = i;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* @pre vm != NULL && mbi != NULL
|
|
*/
|
|
static int32_t init_vm_sw_load(struct acrn_vm *vm, const struct multiboot_info *mbi)
|
|
{
|
|
struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
|
|
struct multiboot_module *mods = (struct multiboot_module *)hpa2hva((uint64_t)mbi->mi_mods_addr);
|
|
uint32_t mod_idx;
|
|
int32_t ret = -EINVAL;
|
|
|
|
dev_dbg(ACRN_DBG_BOOT, "mod counts=%d\n", mbi->mi_mods_count);
|
|
|
|
if (mods != NULL) {
|
|
mod_idx = get_mod_idx_by_tag(mods, mbi->mi_mods_count, vm_config->os_config.kernel_mod_tag);
|
|
if (mod_idx != INVALID_MOD_IDX) {
|
|
ret = init_vm_kernel_info(vm, &mods[mod_idx]);
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
init_vm_bootargs_info(vm, mbi);
|
|
/* check whether there is a ramdisk module */
|
|
mod_idx = get_mod_idx_by_tag(mods, mbi->mi_mods_count, vm_config->os_config.ramdisk_mod_tag);
|
|
if (mod_idx != INVALID_MOD_IDX) {
|
|
init_vm_ramdisk_info(vm, &mods[mod_idx]);
|
|
/* TODO: prepare other modules like firmware, seedlist */
|
|
}
|
|
} else {
|
|
pr_err("failed to load VM %d kernel module", vm->vm_id);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pre vm != NULL
|
|
*/
|
|
static int32_t init_general_vm_boot_info(struct acrn_vm *vm)
|
|
{
|
|
struct multiboot_info *mbi = NULL;
|
|
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 multiboot module info found");
|
|
} else {
|
|
ret = init_vm_sw_load(vm, mbi);
|
|
}
|
|
clac();
|
|
}
|
|
}
|
|
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.
|
|
*/
|
|
(void)memcpy_s((void *)&(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;
|
|
}
|