313 lines
9.0 KiB
C
313 lines
9.0 KiB
C
/*-
|
|
* Copyright (c) 2018-2022 Intel Corporation.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``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 NETAPP, INC 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 <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "dm.h"
|
|
#include "vmmapi.h"
|
|
#include "sw_load.h"
|
|
#include "acpi.h"
|
|
#include "log.h"
|
|
|
|
|
|
/* If the vsbl is loaded by DM, the User VM memory layout will be like:
|
|
*
|
|
* | ... |
|
|
* +--------------------------------------------------+
|
|
* | offset: 0xf2400 (ACPI table) |
|
|
* +--------------------------------------------------+
|
|
* | ... |
|
|
* +--------------------------------------------------+
|
|
* | offset: 16MB (vsbl image) |
|
|
* +--------------------------------------------------+
|
|
* | ... |
|
|
* +--------------------------------------------------+
|
|
* | offset: lowmem - 16K (partition blob) |
|
|
* +--------------------------------------------------+
|
|
* | offset: lowmem - 12K (e820 table) |
|
|
* +--------------------------------------------------+
|
|
* | offset: lowmem - 8K (boot_args_address) |
|
|
* +--------------------------------------------------+
|
|
* | offset: lowmem - 6K (vsbl entry address) |
|
|
* +--------------------------------------------------+
|
|
* | offset: lowmem - 4K (config_page with e820 table)|
|
|
* +--------------------------------------------------+
|
|
*/
|
|
|
|
/* vsbl binary layout:
|
|
*
|
|
* +--------------------------------------------------+ <--vSBL Top
|
|
* | |offset: Top - 0x10 (reset vector) |
|
|
* + STAGEINIT |------------------------------------+
|
|
* | (0x10000) |other |
|
|
* +--------------------------------------------------+
|
|
* | |
|
|
* + PAYLOAD +
|
|
* |(0x100000) |
|
|
* +--------------------------------------------------+
|
|
* | |
|
|
* + vFastboot +
|
|
* |(0x200000) |
|
|
* +--------------------------------------------------+
|
|
*/
|
|
|
|
/* Check default e820 table in sw_load_common.c for info about ctx->lowmem */
|
|
#define CONFIGPAGE_OFF(ctx) ((ctx)->lowmem - 4*KB)
|
|
#define VSBL_ENTRY_OFF(ctx) ((ctx)->lowmem - 6*KB)
|
|
#define BOOTARGS_OFF(ctx) ((ctx)->lowmem - 8*KB)
|
|
#define E820_TABLE_OFF(ctx) ((ctx)->lowmem - 12*KB)
|
|
#define GUEST_PART_INFO_OFF(ctx) ((ctx)->lowmem - 16*KB)
|
|
/* vsbl real entry is reset vector, which is (VSBL_TOP - 16) */
|
|
#define VSBL_TOP(ctx) (64*MB)
|
|
|
|
struct vsbl_para {
|
|
uint64_t e820_table_address;
|
|
uint64_t e820_entries;
|
|
uint64_t acpi_table_address;
|
|
uint64_t acpi_table_size;
|
|
uint64_t guest_part_info_address;
|
|
uint64_t guest_part_info_size;
|
|
uint64_t vsbl_address;
|
|
uint64_t vsbl_size;
|
|
uint64_t bootargs_address;
|
|
uint32_t trusty_enabled;
|
|
uint32_t key_info_lock;
|
|
uint32_t reserved;
|
|
uint32_t boot_device_address;
|
|
};
|
|
|
|
static char guest_part_info_path[STR_LEN];
|
|
static size_t guest_part_info_size;
|
|
static bool with_guest_part_info;
|
|
|
|
static char vsbl_path[STR_LEN];
|
|
static size_t vsbl_size;
|
|
|
|
static int boot_blk_bdf;
|
|
|
|
extern int init_cmos_vrpmb(struct vmctx *ctx);
|
|
|
|
#define LOW_8BIT(x) ((x) & 0xFF)
|
|
void
|
|
vsbl_set_bdf(int bnum, int snum, int fnum)
|
|
{
|
|
boot_blk_bdf = (LOW_8BIT(bnum) << 16) | (LOW_8BIT(snum) << 8) |
|
|
LOW_8BIT(fnum);
|
|
}
|
|
|
|
int
|
|
acrn_parse_guest_part_info(char *arg)
|
|
{
|
|
int error = -1;
|
|
size_t len = strnlen(arg, STR_LEN);
|
|
|
|
if (len < STR_LEN) {
|
|
strncpy(guest_part_info_path, arg, len + 1);
|
|
if (check_image(guest_part_info_path, 0, &guest_part_info_size) == 0) {
|
|
with_guest_part_info = true;
|
|
pr_info("SW_LOAD: get partition blob path %s\n",
|
|
guest_part_info_path);
|
|
error = 0;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
acrn_prepare_guest_part_info(struct vmctx *ctx)
|
|
{
|
|
FILE *fp;
|
|
long len;
|
|
size_t read;
|
|
|
|
fp = fopen(guest_part_info_path, "r");
|
|
if (fp == NULL) {
|
|
pr_err("SW_LOAD ERR: could not open partition blob %s\n",
|
|
guest_part_info_path);
|
|
return -1;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
len = ftell(fp);
|
|
|
|
if (len != guest_part_info_size) {
|
|
pr_err("SW_LOAD ERR: partition blob changed\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
if ((len + GUEST_PART_INFO_OFF(ctx)) > BOOTARGS_OFF(ctx)) {
|
|
pr_err("SW_LOAD ERR: too large partition blob\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
read = fread(ctx->baseaddr + GUEST_PART_INFO_OFF(ctx),
|
|
sizeof(char), len, fp);
|
|
if (read < len) {
|
|
pr_err("SW_LOAD ERR: could not read whole partition blob\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
fclose(fp);
|
|
pr_info("SW_LOAD: partition blob %s size %lu copy to guest 0x%lx\n",
|
|
guest_part_info_path, guest_part_info_size,
|
|
GUEST_PART_INFO_OFF(ctx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
acrn_parse_vsbl(char *arg)
|
|
{
|
|
int error = -1;
|
|
size_t len = strnlen(arg, STR_LEN);
|
|
|
|
if (len < STR_LEN) {
|
|
strncpy(vsbl_path, arg, len + 1);
|
|
if (check_image(vsbl_path, 8 * MB, &vsbl_size) == 0) {
|
|
vsbl_file_name = vsbl_path;
|
|
pr_notice("SW_LOAD: get vsbl path %s\n", vsbl_path);
|
|
error = 0;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
acrn_prepare_vsbl(struct vmctx *ctx)
|
|
{
|
|
FILE *fp;
|
|
size_t read;
|
|
|
|
fp = fopen(vsbl_path, "r");
|
|
if (fp == NULL) {
|
|
pr_err("SW_LOAD ERR: could not open vsbl file: %s\n",
|
|
vsbl_path);
|
|
return -1;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
|
|
if (ftell(fp) != vsbl_size) {
|
|
pr_err("SW_LOAD ERR: vsbl file changed\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_SET);
|
|
read = fread(ctx->baseaddr + VSBL_TOP(ctx) - vsbl_size,
|
|
sizeof(char), vsbl_size, fp);
|
|
if (read < vsbl_size) {
|
|
pr_err("SW_LOAD ERR: could not read whole partition blob\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
fclose(fp);
|
|
pr_info("SW_LOAD: partition blob %s size %lu copy to guest 0x%lx\n",
|
|
vsbl_path, vsbl_size, VSBL_TOP(ctx) - vsbl_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
acrn_sw_load_vsbl(struct vmctx *ctx)
|
|
{
|
|
int ret;
|
|
struct e820_entry *e820;
|
|
struct vsbl_para *vsbl_para;
|
|
|
|
init_cmos_vrpmb(ctx);
|
|
|
|
vsbl_para = (struct vsbl_para *)
|
|
(ctx->baseaddr + CONFIGPAGE_OFF(ctx));
|
|
|
|
memset(vsbl_para, 0x0, sizeof(struct vsbl_para));
|
|
|
|
e820 = (struct e820_entry *)
|
|
(ctx->baseaddr + E820_TABLE_OFF(ctx));
|
|
|
|
vsbl_para->e820_entries = acrn_create_e820_table(ctx, e820);
|
|
vsbl_para->e820_table_address = E820_TABLE_OFF(ctx);
|
|
|
|
vsbl_para->acpi_table_address = get_acpi_base();
|
|
vsbl_para->acpi_table_size = get_acpi_table_length();
|
|
|
|
if (with_bootargs) {
|
|
strncpy(ctx->baseaddr + BOOTARGS_OFF(ctx), get_bootargs(), STR_LEN);
|
|
vsbl_para->bootargs_address = BOOTARGS_OFF(ctx);
|
|
} else {
|
|
vsbl_para->bootargs_address = 0;
|
|
}
|
|
|
|
if (with_guest_part_info) {
|
|
ret = acrn_prepare_guest_part_info(ctx);
|
|
if (ret)
|
|
return ret;
|
|
vsbl_para->guest_part_info_address = GUEST_PART_INFO_OFF(ctx);
|
|
vsbl_para->guest_part_info_size = guest_part_info_size;
|
|
} else {
|
|
vsbl_para->guest_part_info_address = 0;
|
|
vsbl_para->guest_part_info_size = 0;
|
|
}
|
|
|
|
ret = acrn_prepare_vsbl(ctx);
|
|
if (ret)
|
|
return ret;
|
|
|
|
vsbl_para->vsbl_address = VSBL_TOP(ctx) - vsbl_size;
|
|
vsbl_para->vsbl_size = vsbl_size;
|
|
|
|
vsbl_para->e820_entries = add_e820_entry(e820, vsbl_para->e820_entries,
|
|
vsbl_para->vsbl_address, vsbl_size, E820_TYPE_RESERVED);
|
|
|
|
pr_info("SW_LOAD: vsbl_entry 0x%lx\n", VSBL_TOP(ctx) - 16);
|
|
|
|
vsbl_para->boot_device_address = boot_blk_bdf;
|
|
vsbl_para->trusty_enabled = trusty_enabled;
|
|
|
|
/* set guest bsp state. Will call hypercall set bsp state
|
|
* after bsp is created.
|
|
*/
|
|
memset(&ctx->bsp_regs, 0, sizeof( struct acrn_vcpu_regs));
|
|
ctx->bsp_regs.vcpu_id = 0;
|
|
|
|
/* CR0_ET | CR0_NE */
|
|
ctx->bsp_regs.vcpu_regs.cr0 = 0x30U;
|
|
ctx->bsp_regs.vcpu_regs.cs_ar = 0x009FU;
|
|
ctx->bsp_regs.vcpu_regs.cs_sel = 0xF000U;
|
|
ctx->bsp_regs.vcpu_regs.cs_limit = 0xFFFFU;
|
|
ctx->bsp_regs.vcpu_regs.cs_base = (VSBL_TOP(ctx) - 16) &0xFFFF0000UL;
|
|
ctx->bsp_regs.vcpu_regs.rip = (VSBL_TOP(ctx) - 16) & 0xFFFFUL;
|
|
ctx->bsp_regs.vcpu_regs.gprs.rsi = CONFIGPAGE_OFF(ctx);
|
|
|
|
return 0;
|
|
}
|