[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [TOOLS] Allow tools to load kernels which use an ELF notes segment.
# 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |