[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v1 07/11] xsplice: Implement payload loading



Add support for loading xsplice payloads. This is somewhat similar to
the Linux kernel module loader, implementing the following steps:
- Verify the elf file.
- Parse the elf file.
- Allocate a region of memory mapped within a free area of
  [xen_virt_end, XEN_VIRT_END].
- Copy allocated sections into the new region.
- Resolve section symbols. All other symbols must be absolute addresses.
- Perform relocations.
- Process xsplice specific sections.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
---
 xen/arch/arm/Makefile             |   1 +
 xen/arch/arm/xsplice.c            |  23 ++++
 xen/arch/x86/Makefile             |   1 +
 xen/arch/x86/setup.c              |   7 +
 xen/arch/x86/xsplice.c            |  90 ++++++++++++
 xen/common/xsplice.c              | 282 ++++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/x86_64/page.h |   2 +
 xen/include/xen/xsplice.h         |  22 +++
 8 files changed, 428 insertions(+)
 create mode 100644 xen/arch/arm/xsplice.c
 create mode 100644 xen/arch/x86/xsplice.c

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 1ef39f7..f785c07 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -39,6 +39,7 @@ obj-y += device.o
 obj-y += decode.o
 obj-y += processor.o
 obj-y += smc.o
+obj-y += xsplice.o
 
 #obj-bin-y += ....o
 
diff --git a/xen/arch/arm/xsplice.c b/xen/arch/arm/xsplice.c
new file mode 100644
index 0000000..8d85fa9
--- /dev/null
+++ b/xen/arch/arm/xsplice.c
@@ -0,0 +1,23 @@
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/xsplice_elf.h>
+#include <xen/xsplice.h>
+
+int xsplice_verify_elf(uint8_t *data, ssize_t len)
+{
+    return -ENOSYS;
+}
+
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela)
+{
+    return -ENOSYS;
+}
+
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela)
+{
+    return -ENOSYS;
+}
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 39a8059..6e05532 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -61,6 +61,7 @@ obj-y += x86_emulate.o
 obj-y += tboot.o
 obj-y += hpet.o
 obj-y += vm_event.o
+obj-y += xsplice.o
 obj-y += xstate.o
 
 obj-$(crash_debug) += gdbstub.o
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 4ed0110..a79c5e3 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -99,6 +99,9 @@ unsigned long __read_mostly xen_phys_start;
 
 unsigned long __read_mostly xen_virt_end;
 
+unsigned long __read_mostly module_virt_start;
+unsigned long __read_mostly module_virt_end;
+
 DEFINE_PER_CPU(struct tss_struct, init_tss);
 
 char __section(".bss.stack_aligned") cpu0_stack[STACK_SIZE];
@@ -1145,6 +1148,10 @@ void __init noreturn __start_xen(unsigned long mbi_p)
                    ~((1UL << L2_PAGETABLE_SHIFT) - 1);
     destroy_xen_mappings(xen_virt_end, XEN_VIRT_START + BOOTSTRAP_MAP_BASE);
 
+    module_virt_start = xen_virt_end;
+    module_virt_end = XEN_VIRT_END - NR_CPUS * PAGE_SIZE;
+    BUG_ON(module_virt_end <= module_virt_start);
+
     memguard_init();
 
     nr_pages = 0;
diff --git a/xen/arch/x86/xsplice.c b/xen/arch/x86/xsplice.c
new file mode 100644
index 0000000..dbff0d5
--- /dev/null
+++ b/xen/arch/x86/xsplice.c
@@ -0,0 +1,90 @@
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/xsplice_elf.h>
+#include <xen/xsplice.h>
+
+int xsplice_verify_elf(uint8_t *data, ssize_t len)
+{
+
+    Elf64_Ehdr *hdr = (Elf64_Ehdr *)data;
+
+    if ( len < (sizeof *hdr) ||
+         !IS_ELF(*hdr) ||
+         hdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+         hdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+         hdr->e_machine != EM_X86_64 )
+    {
+        printk(XENLOG_ERR "Invalid ELF file\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela)
+{
+    printk(XENLOG_ERR "SHT_REL relocation unsupported\n");
+    return -ENOSYS;
+}
+
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela)
+{
+    Elf64_Rela *r;
+    int symndx, i;
+    uint64_t val;
+    uint8_t *dest;
+
+    for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ )
+    {
+        r = (Elf64_Rela *)(rela->data + i * rela->sec->sh_entsize);
+        symndx = ELF64_R_SYM(r->r_info);
+        dest = base->load_addr + r->r_offset;
+        val = r->r_addend + elf->sym[symndx].sym->st_value;
+
+        switch ( ELF64_R_TYPE(r->r_info) )
+        {
+            case R_X86_64_NONE:
+                break;
+            case R_X86_64_64:
+                *(uint64_t *)dest = val;
+                break;
+            case R_X86_64_32:
+                *(uint32_t *)dest = val;
+                if (val != *(uint32_t *)dest)
+                    goto overflow;
+                break;
+            case R_X86_64_32S:
+                *(int32_t *)dest = val;
+                if ((int64_t)val != *(int32_t *)dest)
+                    goto overflow;
+                break;
+            case R_X86_64_PLT32:
+                /*
+                 * Xen uses -fpic which normally uses PLT relocations
+                 * except that it sets visibility to hidden which means
+                 * that they are not used.  However, when gcc cannot
+                 * inline memcpy it emits memcpy with default visibility
+                 * which then creates a PLT relocation.  It can just be
+                 * treated the same as R_X86_64_PC32.
+                 */
+                /* Fall through */
+            case R_X86_64_PC32:
+                *(uint32_t *)dest = val - (uint64_t)dest;
+                break;
+            default:
+                printk(XENLOG_ERR "Unhandled relocation %lu\n",
+                       ELF64_R_TYPE(r->r_info));
+                return -EINVAL;
+        }
+    }
+
+    return 0;
+
+ overflow:
+    printk(XENLOG_ERR "Overflow in relocation %d in %s\n", i, rela->name);
+    return -EOVERFLOW;
+}
diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c
index d984c8a..5e88c55 100644
--- a/xen/common/xsplice.c
+++ b/xen/common/xsplice.c
@@ -12,6 +12,7 @@
 #include <xen/stdbool.h>
 #include <xen/sched.h>
 #include <xen/lib.h>
+#include <xen/xsplice_elf.h>
 #include <xen/xsplice.h>
 #include <public/sysctl.h>
 
@@ -29,9 +30,15 @@ struct payload {
 
     struct list_head   list;   /* Linked to 'payload_list'. */
 
+    void *module_address;
+    size_t module_pages;
+
     char  id[XEN_XSPLICE_NAME_SIZE + 1];          /* Name of it. */
 };
 
+static int load_module(struct payload *payload, uint8_t *raw, ssize_t len);
+static void free_module(struct payload *payload);
+
 static const char *state2str(int32_t state)
 {
 #define STATE(x) [XSPLICE_STATE_##x] = #x
@@ -140,6 +147,7 @@ static void __free_payload(struct payload *data)
     list_del(&data->list);
     payload_cnt --;
     payload_version ++;
+    free_module(data);
     xfree(data);
 }
 
@@ -178,6 +186,10 @@ static int xsplice_upload(xen_sysctl_xsplice_upload_t 
*upload)
     if ( copy_from_guest(raw_data, upload->payload, upload->size) )
         goto err_raw;
 
+    rc = load_module(data, raw_data, upload->size);
+    if ( rc )
+        goto err_raw;
+
     data->state = XSPLICE_STATE_LOADED;
     data->rc = 0;
     INIT_LIST_HEAD(&data->list);
@@ -390,6 +402,276 @@ int xsplice_control(xen_sysctl_xsplice_op_t *xsplice)
     return rc;
 }
 
+
+/*
+ * The following functions prepare an xSplice module to be executed by
+ * allocating space, loading the allocated sections, resolving symbols,
+ * performing relocations, etc.
+ */
+#ifdef CONFIG_X86
+static void *alloc_module(size_t size)
+{
+    mfn_t *mfn, *mfn_ptr;
+    size_t pages, i;
+    struct page_info *pg;
+    unsigned long hole_start, hole_end, cur;
+    struct payload *data, *data2;
+
+    ASSERT(size);
+
+    pages = PFN_UP(size);
+    mfn = xmalloc_array(mfn_t, pages);
+    if ( mfn == NULL )
+        return NULL;
+
+    for ( i = 0; i < pages; i++ )
+    {
+        pg = alloc_domheap_page(NULL, 0);
+        if ( pg == NULL )
+            goto error;
+        mfn[i] = _mfn(page_to_mfn(pg));
+    }
+
+    hole_start = (unsigned long)module_virt_start;
+    hole_end = hole_start + pages * PAGE_SIZE;
+    spin_lock(&payload_list_lock);
+    list_for_each_entry ( data, &payload_list, list )
+    {
+        list_for_each_entry ( data2, &payload_list, list )
+        {
+            unsigned long start, end;
+
+            start = (unsigned long)data2->module_address;
+            end = start + data2->module_pages * PAGE_SIZE;
+            if ( hole_end > start && hole_start < end )
+            {
+                hole_start = end;
+                hole_end = hole_start + pages * PAGE_SIZE;
+                break;
+            }
+        }
+        if ( &data2->list == &payload_list )
+            break;
+    }
+    spin_unlock(&payload_list_lock);
+
+    if ( hole_end >= module_virt_end )
+        goto error;
+
+    for ( cur = hole_start, mfn_ptr = mfn; pages--; ++mfn_ptr, cur += 
PAGE_SIZE )
+    {
+        if ( map_pages_to_xen(cur, mfn_x(*mfn_ptr), 1, PAGE_HYPERVISOR_RWX) )
+        {
+            if ( cur != hole_start )
+                destroy_xen_mappings(hole_start, cur);
+            goto error;
+        }
+    }
+    xfree(mfn);
+    return (void *)hole_start;
+
+ error:
+    while ( i-- )
+        free_domheap_page(mfn_to_page(mfn_x(mfn[i])));
+    xfree(mfn);
+    return NULL;
+}
+#else
+static void *alloc_module(size_t size)
+{
+    return NULL;
+}
+#endif
+
+static void free_module(struct payload *payload)
+{
+    int i;
+    struct page_info *pg;
+    PAGE_LIST_HEAD(pg_list);
+    void *va = payload->module_address;
+    unsigned long addr = (unsigned long)va;
+
+    if ( !payload->module_address )
+        return;
+
+    payload->module_address = NULL;
+
+    for ( i = 0; i < payload->module_pages; i++ )
+        page_list_add(vmap_to_page(va + i * PAGE_SIZE), &pg_list);
+
+    destroy_xen_mappings(addr, addr + payload->module_pages * PAGE_SIZE);
+
+    while ( (pg = page_list_remove_head(&pg_list)) != NULL )
+        free_domheap_page(pg);
+
+    payload->module_pages = 0;
+}
+
+static void alloc_section(struct xsplice_elf_sec *sec, size_t *core_size)
+{
+    size_t align_size = ROUNDUP(*core_size, sec->sec->sh_addralign);
+    sec->sec->sh_entsize = align_size;
+    *core_size = sec->sec->sh_size + align_size;
+}
+
+static int move_module(struct payload *payload, struct xsplice_elf *elf)
+{
+    uint8_t *buf;
+    int i;
+    size_t core_size = 0;
+
+    /* Allocate text regions */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & (SHF_ALLOC|SHF_EXECINSTR)) ==
+             (SHF_ALLOC|SHF_EXECINSTR) )
+            alloc_section(&elf->sec[i], &core_size);
+    }
+
+    /* Allocate rw data */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & SHF_ALLOC) &&
+             !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+             (elf->sec[i].sec->sh_flags & SHF_WRITE) )
+            alloc_section(&elf->sec[i], &core_size);
+    }
+
+    /* Allocate ro data */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & SHF_ALLOC) &&
+             !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+             !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
+            alloc_section(&elf->sec[i], &core_size);
+    }
+
+    buf = alloc_module(core_size);
+    if ( !buf ) {
+        printk(XENLOG_ERR "Could not allocate memory for module\n");
+        return -ENOMEM;
+    }
+    memset(buf, 0, core_size);
+
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( elf->sec[i].sec->sh_flags & SHF_ALLOC )
+        {
+            elf->sec[i].load_addr = buf + elf->sec[i].sec->sh_entsize;
+            memcpy(elf->sec[i].load_addr, elf->sec[i].data,
+                   elf->sec[i].sec->sh_size);
+            printk(XENLOG_DEBUG "Loaded %s at 0x%p\n",
+                   elf->sec[i].name, elf->sec[i].load_addr);
+        }
+    }
+
+    payload->module_address = buf;
+    payload->module_pages = PFN_UP(core_size);
+
+    return 0;
+}
+
+static int resolve_symbols(struct xsplice_elf *elf)
+{
+    int i;
+
+    for ( i = 1; i < elf->nsym; i++ )
+    {
+        switch ( elf->sym[i].sym->st_shndx )
+        {
+            case SHN_COMMON:
+                printk(XENLOG_ERR "Unexpected common symbol: %s\n",
+                       elf->sym[i].name);
+                return -EINVAL;
+                break;
+            case SHN_UNDEF:
+                printk(XENLOG_ERR "Unknown symbol: %s\n", elf->sym[i].name);
+                return -ENOENT;
+                break;
+            case SHN_ABS:
+                printk(XENLOG_DEBUG "Absolute symbol: %s => 0x%p\n",
+                       elf->sym[i].name, (void *)elf->sym[i].sym->st_value);
+                break;
+            default:
+                if ( elf->sec[elf->sym[i].sym->st_shndx].sec->sh_flags & 
SHF_ALLOC )
+                {
+                    elf->sym[i].sym->st_value +=
+                        (unsigned 
long)elf->sec[elf->sym[i].sym->st_shndx].load_addr;
+                    printk(XENLOG_DEBUG "Symbol resolved: %s => 0x%p\n",
+                           elf->sym[i].name, (void 
*)elf->sym[i].sym->st_value);
+                }
+        }
+    }
+
+    return 0;
+}
+
+static int perform_relocs(struct xsplice_elf *elf)
+{
+    struct xsplice_elf_sec *rela, *base;
+    int i, rc;
+
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        rela = &elf->sec[i];
+
+        /* Is it a valid relocation section? */
+        if ( rela->sec->sh_info >= elf->hdr->e_shnum )
+            continue;
+
+        base = &elf->sec[rela->sec->sh_info];
+
+        /* Don't relocate non-allocated sections */
+        if ( !(base->sec->sh_flags & SHF_ALLOC) )
+            continue;
+
+        if ( elf->sec[i].sec->sh_type == SHT_RELA )
+            rc = xsplice_perform_rela(elf, base, rela);
+        else if ( elf->sec[i].sec->sh_type == SHT_REL )
+            rc = xsplice_perform_rel(elf, base, rela);
+
+        if ( rc )
+            return rc;
+    }
+
+    return 0;
+}
+
+static int load_module(struct payload *payload, uint8_t *raw, ssize_t len)
+{
+    struct xsplice_elf elf;
+    int rc = 0;
+
+    rc = xsplice_verify_elf(raw, len);
+    if ( rc )
+        return rc;
+
+    rc = xsplice_elf_load(&elf, raw, len);
+    if ( rc )
+        return rc;
+
+    rc = move_module(payload, &elf);
+    if ( rc )
+        goto err_elf;
+
+    rc = resolve_symbols(&elf);
+    if ( rc )
+        goto err_module;
+
+    rc = perform_relocs(&elf);
+    if ( rc )
+        goto err_module;
+
+    return 0;
+
+ err_module:
+    free_module(payload);
+ err_elf:
+    xsplice_elf_free(&elf);
+
+    return rc;
+}
+
 static int __init xsplice_init(void)
 {
     register_keyhandler('x', xsplice_printall, "print xsplicing info", 1);
diff --git a/xen/include/asm-x86/x86_64/page.h 
b/xen/include/asm-x86/x86_64/page.h
index 19ab4d0..e6f08e9 100644
--- a/xen/include/asm-x86/x86_64/page.h
+++ b/xen/include/asm-x86/x86_64/page.h
@@ -38,6 +38,8 @@
 #include <xen/pdx.h>
 
 extern unsigned long xen_virt_end;
+extern unsigned long module_virt_start;
+extern unsigned long module_virt_end;
 
 #define spage_to_pdx(spg) (((spg) - spage_table)<<(SUPERPAGE_SHIFT-PAGE_SHIFT))
 #define pdx_to_spage(pdx) (spage_table + ((pdx)>>(SUPERPAGE_SHIFT-PAGE_SHIFT)))
diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h
index 41e28da..a3946a3 100644
--- a/xen/include/xen/xsplice.h
+++ b/xen/include/xen/xsplice.h
@@ -1,9 +1,31 @@
 #ifndef __XEN_XSPLICE_H__
 #define __XEN_XSPLICE_H__
 
+struct xsplice_elf;
+struct xsplice_elf_sec;
+struct xsplice_elf_sym;
+
+struct xsplice_patch_func {
+    unsigned long new_addr;
+    unsigned long new_size;
+    unsigned long old_addr;
+    unsigned long old_size;
+    char *name;
+    unsigned char undo[8];
+};
+
 struct xen_sysctl_xsplice_op;
 int xsplice_control(struct xen_sysctl_xsplice_op *);
 
 extern void xsplice_printall(unsigned char key);
 
+/* Arch hooks */
+int xsplice_verify_elf(uint8_t *data, ssize_t len);
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela);
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela);
+
 #endif /* __XEN_XSPLICE_H__ */
-- 
2.4.3


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.