323 lines
8.6 KiB
C
323 lines
8.6 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.
|
|
*
|
|
*/
|
|
|
|
/* This is a very simple elf binary loader. It only support static elf32 binary
|
|
* loading. So we don't need to handle elf relocation. Just need to load the
|
|
* PT_LOAD section to correct memory address and set correct entry.
|
|
*
|
|
* It also prepare simple multiboot info (only memory info) to guest. If some
|
|
* other things are necessary for guest, we could add it per requirement.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <elf.h>
|
|
|
|
#include "types.h"
|
|
#include "dm.h"
|
|
#include "vmmapi.h"
|
|
#include "sw_load.h"
|
|
#include "acpi.h"
|
|
#include "log.h"
|
|
|
|
#define ELF_BUF_LEN (1024UL*8UL)
|
|
#define MULTIBOOT_HEAD_MAGIC (0x1BADB002U)
|
|
#define MULTIBOOT_MACHINE_STATE_MAGIC (0x2BADB002U)
|
|
/* The max length for GDT is 8192 * 8 bytes */
|
|
#define GDT_LOAD_OFF(ctx) (ctx->lowmem - 64U * KB)
|
|
|
|
static char elf_path[STR_LEN];
|
|
/* Whether we need to setup multiboot info for guest */
|
|
static int multiboot_image = 0;
|
|
|
|
/* Multiboot info. Compatible with Multiboot spec 0.6.96 with vesa removed */
|
|
#define MULTIBOOT_INFO_MEMORY (0x00000001U)
|
|
#define MULTIBOOT_INFO_BOOTDEV (0x00000002U)
|
|
#define MULTIBOOT_INFO_CMDLINE (0x00000004U)
|
|
#define MULTIBOOT_INFO_MODS (0x00000008U)
|
|
#define MULTIBOOT_INFO_AOUT_SYMS (0x00000010U)
|
|
#define MULTIBOOT_INFO_ELF_SHDR (0x00000020U)
|
|
#define MULTIBOOT_INFO_MEM_MAP (0x00000040U)
|
|
#define MULTIBOOT_INFO_DRIVE_INFO (0x00000080U)
|
|
#define MULTIBOOT_INFO_CONFIG_TABLE (0x00000100U)
|
|
#define MULTIBOOT_INFO_BOOT_LOADER_NAME (0x00000200U)
|
|
#define MULTIBOOT_INFO_BOOT_APM_TABLE (0x00000400U)
|
|
|
|
struct multiboot_info {
|
|
uint32_t flags;
|
|
uint32_t mem_lower;
|
|
uint32_t mem_upper;
|
|
|
|
uint32_t boot_device;
|
|
uint32_t cmdline;
|
|
uint32_t mods_count;
|
|
uint32_t mods_addr;
|
|
|
|
uint32_t bin_sec[4];
|
|
|
|
uint32_t mmap_length;
|
|
uint32_t mmap_addr;
|
|
|
|
uint32_t drives_length;
|
|
uint32_t drives_addr;
|
|
|
|
uint32_t config_table;
|
|
|
|
uint32_t boot_loader_name;
|
|
};
|
|
|
|
int
|
|
acrn_parse_elf(char *arg)
|
|
{
|
|
int err = -1;
|
|
size_t len = strnlen(arg, STR_LEN);
|
|
size_t elfsz;
|
|
|
|
if (len < STR_LEN) {
|
|
strncpy(elf_path, arg, len + 1);
|
|
if (check_image(elf_path, 0, &elfsz) == 0) {
|
|
elf_file_name = elf_path;
|
|
pr_notice("SW_LOAD: get elf path %s\n", elf_path);
|
|
err = 0;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int load_elf32(struct vmctx *ctx, FILE *fp, void *buf)
|
|
{
|
|
int i;
|
|
size_t phd_size, read_len;
|
|
Elf32_Ehdr *elf32_header = (Elf32_Ehdr *)buf;
|
|
Elf32_Phdr *elf32_phdr, *elf32_phdr_bk;
|
|
|
|
phd_size = elf32_header->e_phentsize * elf32_header->e_phnum;
|
|
elf32_phdr_bk = elf32_phdr = (Elf32_Phdr *)calloc(1, phd_size);
|
|
if (elf32_phdr == NULL) {
|
|
pr_err("Can't allocate memory for elf program header\n");
|
|
return -1;
|
|
}
|
|
|
|
fseek(fp, elf32_header->e_phoff, SEEK_SET);
|
|
read_len = fread((void *)elf32_phdr, 1, phd_size, fp);
|
|
if (read_len != phd_size) {
|
|
pr_err("can't get %ld data from elf file\n", phd_size);
|
|
}
|
|
|
|
for (i = 0; i < elf32_header->e_phnum; i++) {
|
|
if (elf32_phdr->p_type == PT_LOAD) {
|
|
if ((elf32_phdr->p_vaddr + elf32_phdr->p_memsz) >
|
|
ctx->lowmem) {
|
|
pr_err("No enough memory to load elf file\n");
|
|
free(elf32_phdr_bk);
|
|
return -1;
|
|
}
|
|
|
|
void *seg_ptr = ctx->baseaddr + elf32_phdr->p_vaddr;
|
|
|
|
/* Clear the segment memory in memory.
|
|
* This is required for BSS section
|
|
*/
|
|
memset(seg_ptr, 0, elf32_phdr->p_memsz);
|
|
fseek(fp, elf32_phdr->p_offset, SEEK_SET);
|
|
read_len = fread(seg_ptr, 1, elf32_phdr->p_filesz, fp);
|
|
if (read_len != elf32_phdr->p_filesz) {
|
|
pr_err("Can't get %d data\n",
|
|
elf32_phdr->p_filesz);
|
|
}
|
|
}
|
|
|
|
elf32_phdr++;
|
|
}
|
|
|
|
free(elf32_phdr_bk);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
acrn_load_elf(struct vmctx *ctx, char *elf_file_name, unsigned long *entry,
|
|
uint32_t *multiboot_flags)
|
|
{
|
|
int i, ret = 0;
|
|
FILE *fp;
|
|
size_t read_len = 0;
|
|
unsigned int *ptr32;
|
|
char *elf_buf;
|
|
Elf32_Ehdr *elf_ehdr;
|
|
|
|
|
|
elf_buf = calloc(1, ELF_BUF_LEN);
|
|
if (elf_buf == NULL) {
|
|
pr_err("Can't allocate elf buf\r\n");
|
|
return -1;
|
|
}
|
|
|
|
fp = fopen(elf_file_name, "r");
|
|
if (fp == NULL) {
|
|
pr_err("Can't open elf file: %s\r\n", elf_file_name);
|
|
free(elf_buf);
|
|
return -1;
|
|
}
|
|
|
|
read_len = fread(elf_buf, 1, ELF_BUF_LEN, fp);
|
|
if (read_len != ELF_BUF_LEN) {
|
|
pr_err("Can't get %ld data from elf file\n",
|
|
ELF_BUF_LEN);
|
|
}
|
|
|
|
/* Scan the first 8k to detect whether the elf needs multboot
|
|
* info prepared.
|
|
*/
|
|
ptr32 = (unsigned int *) elf_buf;
|
|
for (i = 0; i <= ((ELF_BUF_LEN/4) - 3); i++) {
|
|
if (ptr32[i] == MULTIBOOT_HEAD_MAGIC) {
|
|
int j = 0;
|
|
unsigned int sum = 0;
|
|
|
|
/* According to multiboot spec 0.6.96 sec 3.1.2.
|
|
* There are three u32:
|
|
* offset field
|
|
* 0 multiboot_head_magic
|
|
* 4 flags
|
|
* 8 checksum
|
|
* The sum of these three u32 should be u32 zero.
|
|
*/
|
|
for (j = 0; j < 3; j++) {
|
|
sum += ptr32[j + i];
|
|
}
|
|
|
|
if (0 == sum) {
|
|
multiboot_image = 1;
|
|
*multiboot_flags = ptr32[i + 1];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
elf_ehdr = (Elf32_Ehdr *) elf_buf;
|
|
|
|
if ((elf_ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
|
|
(elf_ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
|
|
(elf_ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
|
|
(elf_ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
|
|
pr_err("This is not elf file\n");
|
|
fclose(fp);
|
|
free(elf_buf);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (elf_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
|
|
ret = load_elf32(ctx, fp, elf_buf);
|
|
} else {
|
|
pr_err("No available 64bit elf loader ready yet\n");
|
|
fclose(fp);
|
|
free(elf_buf);
|
|
return -1;
|
|
}
|
|
|
|
*entry = elf_ehdr->e_entry;
|
|
fclose(fp);
|
|
free(elf_buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Where we put multboot info to */
|
|
#define MULTIBOOT_OFFSET (0x20000)
|
|
static const uint64_t acrn_init_gdt[] = {
|
|
0x0UL,
|
|
0x00CF9B000000FFFFUL, /* Linear Code */
|
|
0x00CF93000000FFFFUL, /* Linear Data */
|
|
};
|
|
|
|
int
|
|
acrn_sw_load_elf(struct vmctx *ctx)
|
|
{
|
|
int ret;
|
|
uint32_t multiboot_flags = 0;
|
|
uint64_t entry = 0;
|
|
struct multiboot_info *mi;
|
|
|
|
ret = acrn_load_elf(ctx, elf_file_name, &entry, &multiboot_flags);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* 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;
|
|
|
|
memcpy(ctx->baseaddr + GDT_LOAD_OFF(ctx), &acrn_init_gdt,
|
|
sizeof(acrn_init_gdt));
|
|
ctx->bsp_regs.vcpu_regs.gdt.limit = sizeof(acrn_init_gdt) - 1;
|
|
ctx->bsp_regs.vcpu_regs.gdt.base = GDT_LOAD_OFF(ctx);
|
|
|
|
/* CR0_ET | CR0_NE | CR0_PE */
|
|
ctx->bsp_regs.vcpu_regs.cr0 = 0x31U;
|
|
|
|
ctx->bsp_regs.vcpu_regs.cs_ar = 0xCF9BU;
|
|
ctx->bsp_regs.vcpu_regs.cs_sel = 0x8U;
|
|
ctx->bsp_regs.vcpu_regs.cs_limit = 0xFFFFFFFFU;
|
|
|
|
ctx->bsp_regs.vcpu_regs.ds_sel = 0x10U;
|
|
ctx->bsp_regs.vcpu_regs.ss_sel = 0x10U;
|
|
ctx->bsp_regs.vcpu_regs.es_sel = 0x10U;
|
|
ctx->bsp_regs.vcpu_regs.gs_sel = 0x10U;
|
|
ctx->bsp_regs.vcpu_regs.fs_sel = 0x10U;
|
|
|
|
ctx->bsp_regs.vcpu_regs.rip = entry;
|
|
ctx->bsp_regs.vcpu_regs.gprs.rax = MULTIBOOT_MACHINE_STATE_MAGIC;
|
|
|
|
if (multiboot_image == 1) {
|
|
mi = (struct multiboot_info *)(ctx->baseaddr + MULTIBOOT_OFFSET);
|
|
memset(mi, 0, sizeof(*mi));
|
|
|
|
if (multiboot_flags == (1 << 1)) {
|
|
/* Now, we only support elf binary request multiboot
|
|
* info with memory info filled case.
|
|
*
|
|
* TODO:
|
|
* For other elf image with multiboot enabled, they
|
|
* may need more fileds initialized here. We will add
|
|
* them here per each requirement.
|
|
*/
|
|
mi->flags = MULTIBOOT_INFO_MEMORY;
|
|
mi->mem_lower = 0;
|
|
mi->mem_upper = GDT_LOAD_OFF(ctx) / 1024U;
|
|
ctx->bsp_regs.vcpu_regs.gprs.rbx = MULTIBOOT_OFFSET;
|
|
} else {
|
|
pr_err("Invalid multiboot header in elf binary\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|