dm: add elf loader to dm

This patch adds a simple 32bit static elf binary loader to acrn DM.
And if the elf binary follow multiboot protocol, only memory info
will be included in multiboot info.

Tracked-On: #1465
Signed-off-by: Yin Fengwei <fengwei.yin@intel.com>
Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Yin Fengwei 2018-10-10 23:10:23 +08:00 committed by wenlingz
parent 0e897c0a6a
commit 9f7642648b
7 changed files with 345 additions and 2 deletions

View File

@ -108,6 +108,7 @@ SRCS += core/monitor.c
SRCS += core/sw_load_common.c
SRCS += core/sw_load_bzimage.c
SRCS += core/sw_load_vsbl.c
SRCS += core/sw_load_elf.c
SRCS += core/smbiostbl.c
SRCS += core/mevent.c
SRCS += core/gc.c

View File

@ -77,6 +77,8 @@ char *vmname;
int guest_ncpus;
char *guest_uuid_str;
char *vsbl_file_name;
char *kernel_file_name;
char *elf_file_name;
uint8_t trusty_enabled;
bool stdio_in_use;
@ -135,6 +137,7 @@ usage(int code)
" -b: enable bvmcons\n"
" -c: # cpus (default 1)\n"
" -C: include guest memory in core file\n"
" -E: elf image path\n"
" -g: gdb port\n"
" -h: help\n"
" -l: LPC device configuration\n"
@ -690,6 +693,7 @@ static struct option long_options[] = {
{"pincpu", required_argument, 0, 'p' },
{"ncpus", required_argument, 0, 'c' },
{"memflags_incore", no_argument, 0, 'C' },
{"elf_file", required_argument, 0, 'E' },
{"gdb_port", required_argument, 0, 'g' },
{"ioc node", required_argument, 0, 'i' },
{"lpc", required_argument, 0, 'l' },
@ -742,7 +746,7 @@ main(int argc, char *argv[])
if (signal(SIGINT, sig_handler_term) == SIG_ERR)
fprintf(stderr, "cannot register handler for SIGINT\n");
optstr = "abhuwxACSWYvk:r:B:p:g:c:s:m:l:U:G:i:";
optstr = "abhuwxACSWYvE:k:r:B:p:g:c:s:m:l:U:G:i:";
while ((c = getopt_long(argc, argv, optstr, long_options,
&option_idx)) != -1) {
switch (c) {
@ -768,6 +772,12 @@ main(int argc, char *argv[])
case 'C':
memflags |= VM_MEM_F_INCORE;
break;
case 'E':
if (acrn_parse_elf(optarg) != 0)
exit(1);
else
break;
break;
case 'g':
gdb_port = atoi(optarg);
break;

View File

@ -30,6 +30,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include "dm.h"
#include "vmmapi.h"
#include "sw_load.h"
@ -137,6 +138,8 @@ acrn_parse_kernel(char *arg)
kernel_path);
exit(10); /* Non-zero */
}
kernel_file_name = kernel_path;
with_kernel = 1;
printf("SW_LOAD: get kernel path %s\n", kernel_path);
return 0;

View File

@ -222,6 +222,10 @@ acrn_sw_load(struct vmctx *ctx)
{
if (vsbl_file_name)
return acrn_sw_load_vsbl(ctx);
else
else if (kernel_file_name)
return acrn_sw_load_bzimage(ctx);
else if (elf_file_name)
return acrn_sw_load_elf(ctx);
else
return -1;
}

View File

@ -0,0 +1,321 @@
/*-
* Copyright (c) 2018 Intel Corporation
* All rights reserved.
*
* 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 <assert.h>
#include <stdbool.h>
#include <elf.h>
#include "types.h"
#include "dm.h"
#include "vmmapi.h"
#include "sw_load.h"
#include "acpi.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)
{
size_t len = strnlen(arg, STR_LEN);
if (len < STR_LEN) {
strncpy(elf_path, arg, len + 1);
assert(check_image(elf_path) == 0);
elf_file_name = elf_path;
printf("SW_LOAD: get elf path %s\n", elf_path);
return 0;
} else
return -1;
}
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) {
fprintf(stderr, "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) {
fprintf(stderr, "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) {
fprintf(stderr,
"No enough memory to load elf file\n");
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) {
fprintf(stderr, "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) {
fprintf(stderr, "Can't allocate elf buf\r\n");
return -1;
}
fp = fopen(elf_file_name, "r");
if (fp == NULL) {
fprintf(stderr, "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) {
fprintf(stderr, "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; 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)) {
fprintf(stderr, "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 {
fprintf(stderr, "No available 64bit elf loader ready yet\n");
fclose(fp);
free(elf_buf);
return -1;
}
*entry = elf_ehdr->e_entry;
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_set_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.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 {
fprintf(stderr,
"Invalid multiboot header in elf binary\n");
return -1;
}
}
return 0;
}

View File

@ -38,6 +38,8 @@ extern int guest_ncpus;
extern char *guest_uuid_str;
extern uint8_t trusty_enabled;
extern char *vsbl_file_name;
extern char *kernel_file_name;
extern char *elf_file_name;
extern char *vmname;
extern bool stdio_in_use;

View File

@ -60,6 +60,7 @@ int acrn_parse_ramdisk(char *arg);
int acrn_parse_bootargs(char *arg);
int acrn_parse_gvtargs(char *arg);
int acrn_parse_vsbl(char *arg);
int acrn_parse_elf(char *arg);
int acrn_parse_guest_part_info(char *arg);
char *get_bootargs(void);
void vsbl_set_bdf(int bnum, int snum, int fnum);
@ -70,6 +71,7 @@ int add_e820_entry(struct e820_entry *e820, int len, uint64_t start,
uint64_t size, uint32_t type);
int acrn_sw_load_bzimage(struct vmctx *ctx);
int acrn_sw_load_elf(struct vmctx *ctx);
int acrn_sw_load_vsbl(struct vmctx *ctx);
int acrn_sw_load(struct vmctx *ctx);
#endif