mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
use stdint-format bitsize types (uint32_t and friends). Signed-off-by: Keir Fraser <keir@xensource.com>
326 lines
9.0 KiB
C
326 lines
9.0 KiB
C
/******************************************************************************
|
|
* xc_elf_load.c
|
|
*/
|
|
|
|
#include "xg_private.h"
|
|
|
|
#if defined(__i386__)
|
|
#define ELFSIZE 32
|
|
#endif
|
|
#if defined(__x86_64__) || defined(__ia64__)
|
|
#define ELFSIZE 64
|
|
#endif
|
|
|
|
#include "xc_elf.h"
|
|
#include <stdlib.h>
|
|
|
|
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
|
|
#define round_pgdown(_p) ((_p)&PAGE_MASK)
|
|
|
|
static int
|
|
parseelfimage(
|
|
char *image, unsigned long image_size, struct domain_setup_info *dsi);
|
|
static int
|
|
loadelfimage(
|
|
char *image, unsigned long image_size, int xch, uint32_t dom,
|
|
unsigned long *parray, struct domain_setup_info *dsi);
|
|
static int
|
|
loadelfsymtab(
|
|
char *image, int xch, uint32_t dom, unsigned long *parray,
|
|
struct domain_setup_info *dsi);
|
|
|
|
int probe_elf(char *image,
|
|
unsigned long image_size,
|
|
struct load_funcs *load_funcs)
|
|
{
|
|
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
|
|
|
|
if ( !IS_ELF(*ehdr) )
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
load_funcs->parseimage = parseelfimage;
|
|
load_funcs->loadimage = loadelfimage;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int is_loadable_phdr(Elf_Phdr *phdr)
|
|
{
|
|
return ((phdr->p_type == PT_LOAD) &&
|
|
((phdr->p_flags & (PF_W|PF_X)) != 0));
|
|
}
|
|
|
|
static int parseelfimage(char *image,
|
|
unsigned long elfsize,
|
|
struct domain_setup_info *dsi)
|
|
{
|
|
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
|
|
Elf_Phdr *phdr;
|
|
Elf_Shdr *shdr;
|
|
unsigned long kernstart = ~0UL, kernend=0UL;
|
|
char *shstrtab, *guestinfo=NULL, *p;
|
|
int h;
|
|
|
|
if ( !IS_ELF(*ehdr) )
|
|
{
|
|
ERROR("Kernel image does not have an ELF header.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize )
|
|
{
|
|
ERROR("ELF program headers extend beyond end of image.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize )
|
|
{
|
|
ERROR("ELF section headers extend beyond end of image.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Find the section-header strings table. */
|
|
if ( ehdr->e_shstrndx == SHN_UNDEF )
|
|
{
|
|
ERROR("ELF image has no section-header strings table (shstrtab).");
|
|
return -EINVAL;
|
|
}
|
|
shdr = (Elf_Shdr *)(image + ehdr->e_shoff +
|
|
(ehdr->e_shstrndx*ehdr->e_shentsize));
|
|
shstrtab = image + shdr->sh_offset;
|
|
|
|
/* Find the special '__xen_guest' section and check its contents. */
|
|
for ( h = 0; h < ehdr->e_shnum; h++ )
|
|
{
|
|
shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
|
|
if ( strcmp(&shstrtab[shdr->sh_name], "__xen_guest") != 0 )
|
|
continue;
|
|
|
|
guestinfo = image + shdr->sh_offset;
|
|
|
|
if ( (strstr(guestinfo, "LOADER=generic") == NULL) &&
|
|
(strstr(guestinfo, "GUEST_OS=linux") == NULL) )
|
|
{
|
|
ERROR("Will only load images built for the generic loader "
|
|
"or Linux images");
|
|
ERROR("Actually saw: '%s'", guestinfo);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ( (strstr(guestinfo, "XEN_VER=3.0") == NULL) )
|
|
{
|
|
ERROR("Will only load images built for Xen v3.0");
|
|
ERROR("Actually saw: '%s'", guestinfo);
|
|
return -EINVAL;
|
|
}
|
|
if ( (strstr(guestinfo, "PAE=yes") != NULL) )
|
|
dsi->pae_kernel = 1;
|
|
|
|
break;
|
|
}
|
|
if ( guestinfo == NULL )
|
|
{
|
|
#ifdef __ia64__
|
|
guestinfo = "";
|
|
#else
|
|
ERROR("Not a Xen-ELF image: '__xen_guest' section not found.");
|
|
return -EINVAL;
|
|
#endif
|
|
}
|
|
|
|
for ( h = 0; h < ehdr->e_phnum; h++ )
|
|
{
|
|
phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
|
|
if ( !is_loadable_phdr(phdr) )
|
|
continue;
|
|
if ( phdr->p_paddr < kernstart )
|
|
kernstart = phdr->p_paddr;
|
|
if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
|
|
kernend = phdr->p_paddr + phdr->p_memsz;
|
|
}
|
|
|
|
if ( (kernstart > kernend) ||
|
|
(ehdr->e_entry < kernstart) ||
|
|
(ehdr->e_entry > kernend) )
|
|
{
|
|
ERROR("Malformed ELF image.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dsi->v_start = kernstart;
|
|
if ( (p = strstr(guestinfo, "VIRT_BASE=")) != NULL )
|
|
dsi->v_start = strtoul(p+10, &p, 0);
|
|
|
|
if ( (p = strstr(guestinfo, "BSD_SYMTAB")) != NULL )
|
|
dsi->load_symtab = 1;
|
|
|
|
dsi->v_kernstart = kernstart;
|
|
dsi->v_kernend = kernend;
|
|
dsi->v_kernentry = ehdr->e_entry;
|
|
dsi->v_end = dsi->v_kernend;
|
|
|
|
loadelfsymtab(image, 0, 0, NULL, dsi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
loadelfimage(
|
|
char *image, unsigned long elfsize, int xch, uint32_t dom,
|
|
unsigned long *parray, struct domain_setup_info *dsi)
|
|
{
|
|
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
|
|
Elf_Phdr *phdr;
|
|
int h;
|
|
|
|
char *va;
|
|
unsigned long pa, done, chunksz;
|
|
|
|
for ( h = 0; h < ehdr->e_phnum; h++ )
|
|
{
|
|
phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
|
|
if ( !is_loadable_phdr(phdr) )
|
|
continue;
|
|
|
|
for ( done = 0; done < phdr->p_filesz; done += chunksz )
|
|
{
|
|
pa = (phdr->p_paddr + done) - dsi->v_start;
|
|
va = xc_map_foreign_range(
|
|
xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
|
|
chunksz = phdr->p_filesz - done;
|
|
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
|
|
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
|
|
memcpy(va + (pa & (PAGE_SIZE-1)),
|
|
image + phdr->p_offset + done, chunksz);
|
|
munmap(va, PAGE_SIZE);
|
|
}
|
|
|
|
for ( ; done < phdr->p_memsz; done += chunksz )
|
|
{
|
|
pa = (phdr->p_paddr + done) - dsi->v_start;
|
|
va = xc_map_foreign_range(
|
|
xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
|
|
chunksz = phdr->p_memsz - done;
|
|
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
|
|
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
|
|
memset(va + (pa & (PAGE_SIZE-1)), 0, chunksz);
|
|
munmap(va, PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
loadelfsymtab(image, xch, dom, parray, dsi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define ELFROUND (ELFSIZE / 8)
|
|
|
|
static int
|
|
loadelfsymtab(
|
|
char *image, int xch, uint32_t dom, unsigned long *parray,
|
|
struct domain_setup_info *dsi)
|
|
{
|
|
Elf_Ehdr *ehdr = (Elf_Ehdr *)image, *sym_ehdr;
|
|
Elf_Shdr *shdr;
|
|
unsigned long maxva, symva;
|
|
char *p;
|
|
int h, i;
|
|
|
|
if ( !dsi->load_symtab )
|
|
return 0;
|
|
|
|
p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
|
|
ehdr->e_shnum * sizeof(Elf_Shdr));
|
|
if (p == NULL)
|
|
return 0;
|
|
|
|
maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
|
|
symva = maxva;
|
|
maxva += sizeof(int);
|
|
dsi->symtab_addr = maxva;
|
|
dsi->symtab_len = 0;
|
|
maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
|
|
maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
|
|
|
|
shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
|
|
memcpy(shdr, image + ehdr->e_shoff, ehdr->e_shnum * sizeof(Elf_Shdr));
|
|
|
|
for ( h = 0; h < ehdr->e_shnum; h++ )
|
|
{
|
|
if ( shdr[h].sh_type == SHT_STRTAB )
|
|
{
|
|
/* Look for a strtab @i linked to symtab @h. */
|
|
for ( i = 0; i < ehdr->e_shnum; i++ )
|
|
if ( (shdr[i].sh_type == SHT_SYMTAB) &&
|
|
(shdr[i].sh_link == h) )
|
|
break;
|
|
/* Skip symtab @h if we found no corresponding strtab @i. */
|
|
if ( i == ehdr->e_shnum )
|
|
{
|
|
shdr[h].sh_offset = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( (shdr[h].sh_type == SHT_STRTAB) ||
|
|
(shdr[h].sh_type == SHT_SYMTAB) )
|
|
{
|
|
if ( parray != NULL )
|
|
xc_map_memcpy(maxva, image + shdr[h].sh_offset, shdr[h].sh_size,
|
|
xch, dom, parray, dsi->v_start);
|
|
|
|
/* Mangled to be based on ELF header location. */
|
|
shdr[h].sh_offset = maxva - dsi->symtab_addr;
|
|
|
|
dsi->symtab_len += shdr[h].sh_size;
|
|
maxva += shdr[h].sh_size;
|
|
maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
|
|
}
|
|
|
|
shdr[h].sh_name = 0; /* Name is NULL. */
|
|
}
|
|
|
|
if ( dsi->symtab_len == 0 )
|
|
{
|
|
dsi->symtab_addr = 0;
|
|
goto out;
|
|
}
|
|
|
|
if ( parray != NULL )
|
|
{
|
|
*(int *)p = maxva - dsi->symtab_addr;
|
|
sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
|
|
memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
|
|
sym_ehdr->e_phoff = 0;
|
|
sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
|
|
sym_ehdr->e_phentsize = 0;
|
|
sym_ehdr->e_phnum = 0;
|
|
sym_ehdr->e_shstrndx = SHN_UNDEF;
|
|
|
|
/* Copy total length, crafted ELF header and section header table */
|
|
xc_map_memcpy(symva, p, sizeof(int) + sizeof(Elf_Ehdr) +
|
|
ehdr->e_shnum * sizeof(Elf_Shdr), xch, dom, parray,
|
|
dsi->v_start);
|
|
}
|
|
|
|
dsi->symtab_len = maxva - dsi->symtab_addr;
|
|
dsi->v_end = round_pgup(maxva);
|
|
|
|
out:
|
|
free(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-set-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|