# HG changeset patch
# User Ian Campbell <ian.campbell@xxxxxxxxxxxxx>
# Node ID d57b174adfd63787198c7ba4ea64e957caf592d0
# Parent 7ca72a1c41827abbfe65b38d95d05be2f57c16a4
[TOOLS] Allow tools to load kernels which use an ELF notes segment.
Compatability with kernels using the __xen_guest section is retained.
Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxxxxx>
---
tools/libxc/xc_linux_build.c | 29 +--
tools/libxc/xc_load_bin.c | 2
tools/libxc/xc_load_elf.c | 342 +++++++++++++++++++++++++++++++++++--------
tools/libxc/xg_private.h | 28 +++
4 files changed, 322 insertions(+), 79 deletions(-)
diff -r 7ca72a1c4182 -r d57b174adfd6 tools/libxc/xc_linux_build.c
--- a/tools/libxc/xc_linux_build.c Wed Aug 23 14:36:09 2006 +0100
+++ b/tools/libxc/xc_linux_build.c Wed Aug 23 14:37:39 2006 +0100
@@ -655,11 +655,13 @@ static int setup_guest(int xc_handle,
uint32_t required_features[XENFEAT_NR_SUBMAPS])
{
xen_pfn_t *page_array = NULL;
- unsigned long count, i, hypercall_pfn;
+ unsigned long count, i;
+ unsigned long long hypercall_page;
+ int hypercall_page_defined;
start_info_t *start_info;
shared_info_t *shared_info;
xc_mmu_t *mmu = NULL;
- char *p;
+ const char *p;
DECLARE_DOM0_OP;
int rc;
@@ -704,12 +706,9 @@ static int setup_guest(int xc_handle,
goto error_out;
/* Parse and validate kernel features. */
- p = strstr(dsi.xen_guest_string, "FEATURES=");
- if ( p != NULL )
- {
- if ( !parse_features(p + strlen("FEATURES="),
- supported_features,
- required_features) )
+ if ( (p = xen_elfnote_string(&dsi, XEN_ELFNOTE_FEATURES)) != NULL )
+ {
+ if ( !parse_features(p, supported_features, required_features) )
{
ERROR("Failed to parse guest kernel features.");
goto error_out;
@@ -1071,16 +1070,16 @@ static int setup_guest(int xc_handle,
if ( xc_finish_mmu_updates(xc_handle, mmu) )
goto error_out;
- p = strstr(dsi.xen_guest_string, "HYPERCALL_PAGE=");
- if ( p != NULL )
- {
- p += strlen("HYPERCALL_PAGE=");
- hypercall_pfn = strtoul(p, NULL, 16);
- if ( hypercall_pfn >= nr_pages )
+ hypercall_page = xen_elfnote_numeric(&dsi, XEN_ELFNOTE_HYPERCALL_PAGE,
+ &hypercall_page_defined);
+ if ( hypercall_page_defined )
+ {
+ unsigned long long pfn = (hypercall_page - dsi.v_start) >> PAGE_SHIFT;
+ if ( pfn >= nr_pages )
goto error_out;
op.u.hypercall_init.domain = (domid_t)dom;
op.u.hypercall_init.gmfn = shadow_mode_enabled ?
- hypercall_pfn : page_array[hypercall_pfn];
+ pfn : page_array[pfn];
op.cmd = DOM0_HYPERCALL_INIT;
if ( xc_dom0_op(xc_handle, &op) )
goto error_out;
diff -r 7ca72a1c4182 -r d57b174adfd6 tools/libxc/xc_load_bin.c
--- a/tools/libxc/xc_load_bin.c Wed Aug 23 14:36:09 2006 +0100
+++ b/tools/libxc/xc_load_bin.c Wed Aug 23 14:37:39 2006 +0100
@@ -227,7 +227,7 @@ static int parsebinimage(const char *ima
dsi->v_kernstart = dsi->v_start;
dsi->v_kernend = dsi->v_end;
dsi->v_kernentry = image_info->entry_addr;
- dsi->xen_guest_string = "";
+ dsi->__xen_guest_string = "";
return 0;
}
diff -r 7ca72a1c4182 -r d57b174adfd6 tools/libxc/xc_load_elf.c
--- a/tools/libxc/xc_load_elf.c Wed Aug 23 14:36:09 2006 +0100
+++ b/tools/libxc/xc_load_elf.c Wed Aug 23 14:37:39 2006 +0100
@@ -5,6 +5,7 @@
#include "xg_private.h"
#include "xc_elf.h"
#include <stdlib.h>
+#include <inttypes.h>
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
#define round_pgdown(_p) ((_p)&PAGE_MASK)
@@ -65,17 +66,190 @@ static inline int is_loadable_phdr(Elf_P
((phdr->p_flags & (PF_W|PF_X)) != 0));
}
+/*
+ * Fallback for kernels containing only the legacy __xen_guest string
+ * and no ELF notes.
+ */
+static int is_xen_guest_section(Elf_Shdr *shdr, const char *shstrtab)
+{
+ return strcmp(&shstrtab[shdr->sh_name], "__xen_guest") == 0;
+}
+
+static const char *xen_guest_lookup(struct domain_setup_info *dsi, int type)
+{
+ const char *xenguest_fallbacks[] = {
+ [XEN_ELFNOTE_ENTRY] = "VIRT_ENTRY=",
+ [XEN_ELFNOTE_HYPERCALL_PAGE] = "HYPERCALL_PAGE=",
+ [XEN_ELFNOTE_VIRT_BASE] = "VIRT_BASE=",
+ [XEN_ELFNOTE_PADDR_OFFSET] = "ELF_PADDR_OFFSET=",
+ [XEN_ELFNOTE_XEN_VERSION] = "XEN_VER=",
+ [XEN_ELFNOTE_GUEST_OS] = "GUEST_OS=",
+ [XEN_ELFNOTE_GUEST_VERSION] = "GUEST_VER=",
+ [XEN_ELFNOTE_LOADER] = "LOADER=",
+ [XEN_ELFNOTE_PAE_MODE] = "PAE=",
+ [XEN_ELFNOTE_FEATURES] = "FEATURES=",
+ [XEN_ELFNOTE_BSD_SYMTAB] = "BSD_SYMTAB=",
+ };
+ const char *fallback;
+ const char *p;
+
+ if ( type > sizeof(xenguest_fallbacks) )
+ return NULL;
+
+ if ( (fallback = xenguest_fallbacks[type]) == NULL )
+ return NULL;
+
+ if ( (p = strstr(dsi->__xen_guest_string,fallback)) == NULL )
+ return NULL;
+
+ return p + strlen(fallback);
+}
+
+static const char *xen_guest_string(struct domain_setup_info *dsi, int type)
+{
+ const char *p = xen_guest_lookup(dsi, type);
+
+ DPRINTF("found __xen_guest entry for type %#x = \"%s\"\n",
+ type, p);
+
+ /*
+ * We special case this since the __xen_guest_section treats the
+ * mere precense of the BSD_SYMTAB string as true or false.
+ */
+ if ( type == XEN_ELFNOTE_BSD_SYMTAB )
+ return p ? "yes" : "no";
+ return p;
+}
+
+static unsigned long long xen_guest_numeric(struct domain_setup_info *dsi,
+ int type, int *defined)
+{
+ const char *p = xen_guest_lookup(dsi, type);
+ unsigned long long value;
+
+ if ( p == NULL )
+ return 0;
+
+ errno = 0;
+ value = strtoull(p, NULL, 0);
+ if ( errno < 0 )
+ return 0;
+
+ /* We special case this since __xen_guest_section contains a PFN
+ * for this field not a virtual address.
+ */
+ if (type == XEN_ELFNOTE_HYPERCALL_PAGE)
+ value = dsi->v_start + (value<<PAGE_SHIFT);
+
+ DPRINTF("found __xen_guest entry for type %#x = %#llx\n",
+ type, value);
+
+ *defined = 1;
+ return value;
+}
+
+/*
+ * Interface to the Xen ELF notes.
+ */
+#define ELFNOTE_NAME(_n_) ((void*)(_n_) + sizeof(*(_n_)))
+#define ELFNOTE_DESC(_n_) (ELFNOTE_NAME(_n_) + (((_n_)->namesz+3)&~3))
+#define ELFNOTE_NEXT(_n_) (ELFNOTE_DESC(_n_) + (((_n_)->descsz+3)&~3))
+
+static int is_xen_elfnote_section(const char *image, Elf_Shdr *shdr)
+{
+ Elf_Note *note;
+
+ if ( shdr->sh_type != SHT_NOTE )
+ return 0;
+
+ for ( note = (Elf_Note *)(image + shdr->sh_offset);
+ note < (Elf_Note *)(image + shdr->sh_offset + shdr->sh_size);
+ note = ELFNOTE_NEXT(note) )
+ {
+ if ( !strncmp(ELFNOTE_NAME(note), "Xen", 4) )
+ return 1;
+ }
+
+ return 0;
+}
+
+static Elf_Note *xen_elfnote_lookup(struct domain_setup_info *dsi, int type)
+{
+ Elf_Note *note;
+
+ for ( note = (Elf_Note *)dsi->__elfnote_section;
+ note < (Elf_Note *)dsi->__elfnote_section_end;
+ note = ELFNOTE_NEXT(note) )
+ {
+ if ( strncmp(ELFNOTE_NAME(note), "Xen", 4) )
+ continue;
+
+ if ( note->type == type )
+ return note;
+ }
+
+ DPRINTF("unable to find Xen ELF note with type %#x\n", type);
+ return NULL;
+}
+
+const char *xen_elfnote_string(struct domain_setup_info *dsi, int type)
+{
+ Elf_Note *note;
+
+ if ( !dsi->__elfnote_section )
+ return xen_guest_string(dsi, type);
+
+ note = xen_elfnote_lookup(dsi, type);
+ if ( note == NULL )
+ return NULL;
+
+ DPRINTF("found Xen ELF note type %#x = \"%s\"\n",
+ type, (char *)ELFNOTE_DESC(note));
+
+ return (const char *)ELFNOTE_DESC(note);
+}
+
+unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
+ int type, int *defined)
+{
+ Elf_Note *note;
+
+ *defined = 0;
+
+ if ( !dsi->__elfnote_section )
+ return xen_guest_numeric(dsi, type, defined);
+
+ note = xen_elfnote_lookup(dsi, type);
+ if ( note == NULL )
+ {
+ return 0;
+ }
+
+ switch ( note->descsz )
+ {
+ case 4:
+ *defined = 1;
+ return *(uint32_t*)ELFNOTE_DESC(note);
+ case 8:
+ *defined = 1;
+ return *(uint64_t*)ELFNOTE_DESC(note);
+ default:
+ ERROR("elfnotes: unknown data size %#x for numeric type note %#x\n",
+ note->descsz, type);
+ return 0;
+ }
+}
+
static int parseelfimage(const char *image,
- unsigned long elfsize,
+ unsigned long image_len,
struct domain_setup_info *dsi)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
Elf_Phdr *phdr;
Elf_Shdr *shdr;
- Elf_Addr kernstart = ~0, kernend = 0, vaddr, virt_base, elf_pa_off;
- const char *shstrtab;
- char *guestinfo=NULL, *p;
- int h, virt_base_defined, elf_pa_off_defined;
+ Elf_Addr kernstart = ~0, kernend = 0, vaddr, virt_entry;
+ const char *shstrtab, *p;
+ int h, virt_base_defined, elf_pa_off_defined, virt_entry_defined;
if ( !IS_ELF(*ehdr) )
{
@@ -92,13 +266,13 @@ static int parseelfimage(const char *ima
return -EINVAL;
}
- if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize )
+ if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > image_len )
{
ERROR("ELF program headers extend beyond end of image.");
return -EINVAL;
}
- if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize )
+ if ( (ehdr->e_shoff + (ehdr->e_shnum*ehdr->e_shentsize)) > image_len )
{
ERROR("ELF section headers extend beyond end of image.");
return -EINVAL;
@@ -114,69 +288,119 @@ static int parseelfimage(const char *ima
(ehdr->e_shstrndx*ehdr->e_shentsize));
shstrtab = image + shdr->sh_offset;
- /* Find the special '__xen_guest' section and check its contents. */
+ dsi->__elfnote_section = NULL;
+ dsi->__xen_guest_string = NULL;
+
+ /* Look for .notes segment containing at least one Xen note */
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 )
+ if ( !is_xen_elfnote_section(image, shdr) )
continue;
-
- guestinfo = (char *)image + shdr->sh_offset;
-
- if ( (strstr(guestinfo, "LOADER=generic") == NULL) &&
- (strstr(guestinfo, "GUEST_OS=linux") == NULL) )
+ DPRINTF("found note section containing Xen entries\n");
+ dsi->__elfnote_section = (void *)image + shdr->sh_offset;
+ dsi->__elfnote_section_end =
+ (void *)image + shdr->sh_offset + shdr->sh_size;
+ break;
+ }
+
+ /* Fall back to looking for the special '__xen_guest' section. */
+ if ( dsi->__elfnote_section == NULL )
+ {
+ for ( h = 0; h < ehdr->e_shnum; h++ )
+ {
+ shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
+ if ( is_xen_guest_section(shdr, shstrtab) )
+ {
+ DPRINTF("found a legacy __xen_guest section\n");
+ dsi->__xen_guest_string = (char *)image + shdr->sh_offset;
+ break;
+ }
+ }
+ }
+
+ /* Check the contents of the Xen notes or guest string. */
+ if ( dsi->__elfnote_section || dsi->__xen_guest_string )
+ {
+ const char *loader = xen_elfnote_string(dsi, XEN_ELFNOTE_LOADER);
+ const char *guest_os = xen_elfnote_string(dsi, XEN_ELFNOTE_GUEST_OS);
+ const char *xen_version =
+ xen_elfnote_string(dsi, XEN_ELFNOTE_XEN_VERSION);
+
+ if ( ( loader == NULL || strncmp(loader, "generic", 7) ) &&
+ ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) )
{
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=xen-3.0") == NULL) )
+ if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) )
{
ERROR("Will only load images built for Xen v3.0");
- ERROR("Actually saw: '%s'", guestinfo);
return -EINVAL;
}
-
- dsi->pae_kernel = PAEKERN_no;
- p = strstr(guestinfo, "PAE=yes");
- if ( p != NULL )
+ }
+ else
+ {
+#ifdef __ia64__
+ dsi->__elfnote_section = NULL;
+ dsi->__xen_guest_string = "";
+#else
+ ERROR("Not a Xen-ELF image: "
+ "No ELF notes or '__xen_guest' section found.");
+ return -EINVAL;
+#endif
+ }
+
+ dsi->pae_kernel = PAEKERN_no;
+ if ( dsi->__elfnote_section )
+ {
+ p = xen_elfnote_string(dsi, XEN_ELFNOTE_PAE_MODE);
+ if ( p != NULL && strncmp(p, "yes", 3) == 0 )
+ dsi->pae_kernel = PAEKERN_extended_cr3;
+
+ }
+ else
+ {
+ p = xen_guest_lookup(dsi, XEN_ELFNOTE_PAE_MODE);
+ if ( p != NULL && strncmp(p, "yes", 3) == 0 )
{
dsi->pae_kernel = PAEKERN_yes;
- if ( !strncmp(p+7, "[extended-cr3]", 14) )
+ if ( !strncmp(p+4, "[extended-cr3]", 14) )
dsi->pae_kernel = PAEKERN_extended_cr3;
}
-
- break;
- }
-
- if ( guestinfo == NULL )
- {
-#ifdef __ia64__
- guestinfo = "";
-#else
- ERROR("Not a Xen-ELF image: '__xen_guest' section not found.");
- return -EINVAL;
-#endif
- }
-
- dsi->xen_guest_string = guestinfo;
-
- /* Initial guess for virt_base is 0 if it is not explicitly defined. */
- p = strstr(guestinfo, "VIRT_BASE=");
- virt_base_defined = (p != NULL);
- virt_base = virt_base_defined ? strtoull(p+10, &p, 0) : 0;
-
- /* Initial guess for elf_pa_off is virt_base if not explicitly defined. */
- p = strstr(guestinfo, "ELF_PADDR_OFFSET=");
- elf_pa_off_defined = (p != NULL);
- elf_pa_off = elf_pa_off_defined ? strtoull(p+17, &p, 0) : virt_base;
+ }
+
+ /* Initial guess for v_start is 0 if it is not explicitly defined. */
+ dsi->v_start =
+ xen_elfnote_numeric(dsi, XEN_ELFNOTE_VIRT_BASE, &virt_base_defined);
+ if ( !virt_base_defined )
+ dsi->v_start = 0;
+
+ /*
+ * If we are using the legacy __xen_guest section then elf_pa_off
+ * defaults to v_start in order to maintain compatibility with
+ * older hypervisors which set padd in the ELF header to
+ * virt_base.
+ *
+ * If we are using the modern ELF notes interface then the default
+ * is 0.
+ */
+ dsi->elf_paddr_offset =
+ xen_elfnote_numeric(dsi, XEN_ELFNOTE_PADDR_OFFSET,
&elf_pa_off_defined);
+ if ( !elf_pa_off_defined )
+ {
+ if ( dsi->__elfnote_section )
+ dsi->elf_paddr_offset = 0;
+ else
+ dsi->elf_paddr_offset = dsi->v_start;
+ }
if ( elf_pa_off_defined && !virt_base_defined )
{
- ERROR("Neither ELF_PADDR_OFFSET nor VIRT_BASE found in __xen_guest"
- " section.");
+ ERROR("Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF "
+ " notes or __xen_guest section.");
return -EINVAL;
}
@@ -185,7 +409,7 @@ static int parseelfimage(const char *ima
phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
if ( !is_loadable_phdr(phdr) )
continue;
- vaddr = phdr->p_paddr - elf_pa_off + virt_base;
+ vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start;
if ( (vaddr + phdr->p_memsz) < vaddr )
{
ERROR("ELF program header %d is too large.", h);
@@ -198,17 +422,12 @@ static int parseelfimage(const char *ima
kernend = vaddr + phdr->p_memsz;
}
- /*
- * Legacy compatibility and images with no __xen_guest section: assume
- * header addresses are virtual addresses, and that guest memory should be
- * mapped starting at kernel load address.
- */
- dsi->v_start = virt_base_defined ? virt_base : kernstart;
- dsi->elf_paddr_offset = elf_pa_off_defined ? elf_pa_off : dsi->v_start;
-
dsi->v_kernentry = ehdr->e_entry;
- if ( (p = strstr(guestinfo, "VIRT_ENTRY=")) != NULL )
- dsi->v_kernentry = strtoull(p+11, &p, 0);
+
+ virt_entry =
+ xen_elfnote_numeric(dsi, XEN_ELFNOTE_ENTRY, &virt_entry_defined);
+ if ( virt_entry_defined )
+ dsi->v_kernentry = virt_entry;
if ( (kernstart > kernend) ||
(dsi->v_kernentry < kernstart) ||
@@ -219,7 +438,8 @@ static int parseelfimage(const char *ima
return -EINVAL;
}
- if ( (p = strstr(guestinfo, "BSD_SYMTAB")) != NULL )
+ p = xen_elfnote_string(dsi, XEN_ELFNOTE_BSD_SYMTAB);
+ if ( p != NULL && strncmp(p, "yes", 3) == 0 )
dsi->load_symtab = 1;
dsi->v_kernstart = kernstart;
diff -r 7ca72a1c4182 -r d57b174adfd6 tools/libxc/xg_private.h
--- a/tools/libxc/xg_private.h Wed Aug 23 14:36:09 2006 +0100
+++ b/tools/libxc/xg_private.h Wed Aug 23 14:37:39 2006 +0100
@@ -5,6 +5,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
@@ -16,6 +17,7 @@
#include <xen/sys/privcmd.h>
#include <xen/memory.h>
+#include <xen/elfnote.h>
/* valgrind cannot see when a hypercall has filled in some values. For this
reason, we must zero the dom0_op_t instance before a call, if using
@@ -149,8 +151,15 @@ struct domain_setup_info
unsigned long symtab_addr;
unsigned long symtab_len;
- /* __xen_guest info string for convenient loader parsing. */
- char *xen_guest_string;
+ /*
+ * Only one of __elfnote_* or __xen_guest_string will be
+ * non-NULL.
+ *
+ * You should use the xen_elfnote_* accessors below in order to
+ * pickup the correct one and retain backwards compatibility.
+ */
+ void *__elfnote_section, *__elfnote_section_end;
+ char *__xen_guest_string;
};
typedef int (*parseimagefunc)(const char *image, unsigned long image_size,
@@ -159,6 +168,21 @@ typedef int (*loadimagefunc)(const char
int xch,
uint32_t dom, xen_pfn_t *parray,
struct domain_setup_info *dsi);
+
+/*
+ * If an ELF note of the given type is found then the value contained
+ * in the note is returned and *defined is set to non-zero. If no such
+ * note is found then *defined is set to 0 and 0 is returned.
+ */
+extern unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
+ int type, int *defined);
+
+/*
+ * If an ELF note of the given type is found then the string contained
+ * in the value is returned, otherwise NULL is returned.
+ */
+extern const char * xen_elfnote_string(struct domain_setup_info *dsi,
+ int type);
struct load_funcs
{
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|