qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 35/40] xenner: Domain Builder


From: Alexander Graf
Subject: [Qemu-devel] [PATCH 35/40] xenner: Domain Builder
Date: Mon, 1 Nov 2010 16:01:48 +0100

The traditional Xen way of loading a kernel and initial data structures into the
guest's memory is by calling libxc functions. We want to be able to run without
libxc dependencies though, so we need an alternative.

This patch implements a full domain builder for xenner. It loads the guest
kernel, sets up basic memory layouts and saves everything off for the pv
communication device between xenner and qemu.

Signed-off-by: Alexander Graf <address@hidden>
---
 hw/xenner_dom_builder.c |  406 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 406 insertions(+), 0 deletions(-)
 create mode 100644 hw/xenner_dom_builder.c

diff --git a/hw/xenner_dom_builder.c b/hw/xenner_dom_builder.c
new file mode 100644
index 0000000..b4d55dd
--- /dev/null
+++ b/hw/xenner_dom_builder.c
@@ -0,0 +1,406 @@
+/*
+ *  Copyright (C) Red Hat 2007
+ *  Copyright (C) Novell Inc. 2010
+ *
+ *  Author(s): Gerd Hoffmann <address@hidden>
+ *             Alexander Graf <address@hidden>
+ *
+ *  Xenner Domain Builder
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/mman.h>
+
+#include <xen/io/protocols.h>
+
+#include "hw.h"
+#include "pc.h"
+#include "kvm.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "xen_backend.h"
+#include "xen_interfaces.h"
+#include "xen_domainbuild.h"
+#include "xenner.h"
+#include "xenner_emudev.h"
+#include "loader.h"
+#include "elf.h"
+#include "apic.h"
+
+#define XEN_ELFNOTE_INFO            0
+#define XEN_ELFNOTE_ENTRY           1
+#define XEN_ELFNOTE_HYPERCALL_PAGE  2
+#define XEN_ELFNOTE_VIRT_BASE       3
+#define XEN_ELFNOTE_PADDR_OFFSET    4
+#define XEN_ELFNOTE_XEN_VERSION     5
+#define XEN_ELFNOTE_GUEST_OS        6
+#define XEN_ELFNOTE_GUEST_VERSION   7
+#define XEN_ELFNOTE_LOADER          8
+#define XEN_ELFNOTE_PAE_MODE        9
+#define XEN_ELFNOTE_FEATURES       10
+#define XEN_ELFNOTE_BSD_SYMTAB     11
+#define XEN_ELFNOTE_HV_START_LOW   12
+#define XEN_ELFNOTE_L1_MFN_VALID   13
+#define XEN_ELFNOTE_SUSPEND_CANCEL 14
+#define XEN_ELFNOTE_INIT_P2M       15
+
+
+typedef struct DomParams {
+    uint64_t virt_hypercall;
+    uint64_t virt_base;
+    uint64_t virt_entry;
+    int pae;
+    int bits;
+} DomParams;
+
+#define MEGABYTE        (uint64_t)0x100000ULL
+#define MB_TO_PG(x)     (MEGABYTE * x / TARGET_PAGE_SIZE)
+#define PG_TO_MB(x)     (x * TARGET_PAGE_SIZE / MEGABYTE)
+
+static uint64_t note_numeric(uint8_t *desc, uint32_t desc_len)
+{
+    uint64_t r = 0;
+
+    switch (desc_len) {
+        case sizeof(uint8_t):
+            r = *desc;
+            break;
+        case sizeof(uint16_t):
+            r = lduw_p(desc);
+            break;
+        case sizeof(uint32_t):
+            r = ldl_p(desc);
+            break;
+        case sizeof(uint64_t):
+            r = ldq_p(desc);
+            break;
+    }
+
+    return r;
+}
+
+static void dombuild_note(void *opaque, uint8_t *name, uint32_t name_len,
+                          uint8_t *desc, uint32_t desc_len, uint32_t type)
+{
+    DomParams *params = (DomParams *)opaque;
+
+    if (name_len != 4) {
+        return;
+    }
+
+    if (!((name[0] == 'X') && (name[1] == 'e') && (name[2] == 'n'))) {
+        return;
+    }
+
+    switch (type) {
+        case XEN_ELFNOTE_HYPERCALL_PAGE:
+            params->virt_hypercall = note_numeric(desc, desc_len);
+            break;
+        case XEN_ELFNOTE_VIRT_BASE:
+            params->virt_base = note_numeric(desc, desc_len);
+            break;
+        case XEN_ELFNOTE_ENTRY:
+            params->virt_entry = note_numeric(desc, desc_len);
+            break;
+        case XEN_ELFNOTE_PAE_MODE:
+            params->pae = !strncmp((char*)desc, "yes", desc_len);
+            break;
+    }
+}
+
+static void dombuild_xen_guest_key(DomParams *params, char *key, char *value)
+{
+    if (!strncmp(key, "HYPERCALL_PAGE", strlen("HYPERCALL_PAGE"))) {
+        params->virt_hypercall = (strtoull(value, NULL, 0) << PAGE_SHIFT) +
+                                 params->virt_base;
+    }
+
+    if (!strncmp(key, "VIRT_BASE", strlen("VIRT_BASE"))) {
+        params->virt_base = strtoull(value, NULL, 0);
+    }
+
+    if (!strncmp(key, "VIRT_ENTRY", strlen("VIRT_ENTRY"))) {
+        params->virt_entry = strtoull(value, NULL, 0);
+    }
+
+    if (!strncmp(key, "PAE", strlen("PAE"))) {
+        params->pae = !strncmp(value, "yes", 3);
+    }
+}
+
+static void dombuild_section(void *opaque, uint8_t *_name, uint32_t len,
+                             uint8_t *_content)
+{
+    DomParams *params = (DomParams *)opaque;
+    char *entry, *next_entry;
+    char *name = (void*)_name;
+    char *content = (void*)_content;
+
+    if (strlen(name) != strlen("__xen_guest")) {
+        return;
+    }
+
+    if (strncmp(name, "__xen_guest", strlen("__xen_guest"))) {
+        return;
+    }
+
+    /* Example contents of section __xen_guest (without line break):
+     *
+     * GUEST_OS=Mini-OS,XEN_VER=xen-3.0,VIRT_BASE=0x0,ELF_PADDR_OFFSET=0x0,
+     * HYPERCALL_PAGE=0x2,LOADER=generic
+     *
+     */
+
+    entry = (char *)content;
+    while (entry) {
+        char *comma, *equalsign, *key, *value;
+
+        comma = strchr(entry, ',');
+        if (comma) {
+            *comma = '\0';
+            next_entry = comma + 1;
+        } else {
+            next_entry = NULL;
+        }
+
+        /* entry now contains KEY=VALUE */
+        equalsign = strchr(entry, '=');
+        if (equalsign) {
+            /* well-formed entry */
+
+            *equalsign = '\0';
+            key = entry;
+            value = equalsign + 1;
+
+            dombuild_xen_guest_key(params, key, value);
+        }
+
+        /* process next entry */
+        entry = next_entry;
+    }
+}
+
+static uint64_t mfn_to_pfn(uint64_t mfn, uint64_t mfn_guest)
+{
+    return mfn - mfn_guest;
+}
+
+static uint64_t dombuild_translate(void *opaque, uint64_t addr)
+{
+    xen_pfn_t *offset = opaque;
+
+    /* offset the kernel into physical memory */
+    addr += *offset * PAGE_SIZE;
+
+    return addr;
+}
+
+static void dombuild_header_notify(void *opaque, void *header, int bits)
+{
+    DomParams *params = opaque;
+
+    params->bits = bits;
+}
+
+
+static void xenner_cpu_reset(void *opaque)
+{
+    uint8_t boot_buf[] = { 0xea, 0x00, 0x00, 0x00, 0x00 };
+
+    /* install the stage0 boot loader */
+    cpu_physical_memory_rw(0xfffffff0, boot_buf, sizeof(boot_buf), 1);
+}
+
+int xenner_domain_build_pv(const char *kernel,
+                           const char *initrd,
+                           const char *cmdline)
+{
+    const char *emu_file;
+    xen_pfn_t pg_total, pg_emu, pg_m2p, pg_guest;
+    xen_pfn_t mfn_emu, mfn_m2p, mfn_guest;
+    uint64_t emu_entry, emu_low, emu_high;
+    int emu_size;
+    DomParams dom_params;
+    uint64_t elf_entry;
+    uint64_t elf_low, elf_high;
+    int kernel_size;
+    ElfHandlers handlers = elf_default_handlers;
+    target_phys_addr_t cur_addr;
+    int long_mode;
+    int console_evtchn, xenstore_evtchn;
+    uint64_t console_mfn, xenstore_mfn, start_info_pfn;
+    uint64_t init_pt_pfn, cmdline_pfn = 0, initrd_len = 0;
+    uint64_t mfn_list_pfn;
+
+    pg_total = ram_size / PAGE_SIZE;
+    pg_emu   = MB_TO_PG(4);
+    pg_m2p   = MB_TO_PG(4);
+    /* every page entry occupies one long */
+    while (pg_m2p < pg_total / (sizeof(target_phys_addr_t) * 8)) {
+        pg_m2p += MB_TO_PG(2);
+    }
+    mfn_guest  = pg_emu + pg_m2p;
+
+    memset(&dom_params, 0, sizeof(DomParams));
+    handlers.note_fn = dombuild_note;
+    handlers.note_opaque = &dom_params;
+    handlers.section_fn = dombuild_section;
+    handlers.section_opaque = &dom_params;
+    handlers.translate_fn = dombuild_translate;
+    handlers.translate_opaque = &mfn_guest;
+    handlers.header_notify_fn = dombuild_header_notify;
+    handlers.header_notify_opaque = &dom_params;
+    kernel_size = load_elf(kernel, &handlers, &elf_entry,
+                           &elf_low, &elf_high, 0, ELF_MACHINE, 0);
+    if (kernel_size < 0) {
+        fprintf(stderr, "Error while loading elf kernel\n");
+        exit(1);
+    }
+
+#if 0
+    printf("virt base: %#lx\n", dom_params.virt_base);
+    printf("virt hypercall: %#lx\n", dom_params.virt_hypercall);
+    printf("virt entry: %#lx\n", dom_params.virt_entry);
+    printf("guest bitness: %d%s\n", dom_params.bits,
+           dom_params.pae ? " pae" : "");
+#endif
+
+    cur_addr = (elf_high + 0x100000) & ~0xfffffULL;
+
+    if (initrd) {
+        int r;
+
+        xenner_set_config(EMUDEV_CONF_PFN_INITRD,
+                          mfn_to_pfn(cur_addr >> PAGE_SHIFT, mfn_guest));
+
+        r = load_image_targphys(initrd, cur_addr, 0);
+        cur_addr += r;
+        initrd_len = r;
+
+        if (r < 0) {
+            fprintf(stderr, "failed to load initrd\n");
+            exit(1);
+        }
+    }
+
+    /* memory setup */
+    if (dom_params.bits == 64) {
+        /* 64bit */
+        emu_file = "xenner64.elf";
+        long_mode = 1;
+    } else if (dom_params.pae) {
+        /* 32bit, PAE */
+        if (pg_total > MB_TO_PG(16384)) {
+            pg_total = MB_TO_PG(16384);
+        }
+        emu_file = "xenner32-pae.elf";
+        long_mode = 0;
+    } else {
+        /* 32bit, non-PAE */
+        if (pg_total > MB_TO_PG(4096)) {
+            pg_total = MB_TO_PG(4096);
+        }
+        emu_file = "xenner32.elf";
+        long_mode = 0;
+    }
+
+    mfn_emu    = 0;
+    mfn_m2p    = pg_emu;
+    pg_guest   = pg_total - mfn_guest;
+
+    emu_file = qemu_find_file(QEMU_FILE_TYPE_BIOS, emu_file);
+    emu_size = load_elf(emu_file, NULL, &emu_entry, &emu_low, &emu_high,
+                        0, ELF_MACHINE, 0);
+
+    if (emu_size < 0) {
+        fprintf(stderr, "Couldn't load xenner image\n");
+        exit(1);
+    }
+
+    cur_addr = (cur_addr + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+    mfn_list_pfn = mfn_to_pfn(cur_addr >> PAGE_SHIFT, mfn_guest);
+    cur_addr += (pg_guest * (long_mode ? 8 : 4) + ~PAGE_MASK) & PAGE_MASK;
+
+    start_info_pfn = mfn_to_pfn(cur_addr >> PAGE_SHIFT, mfn_guest);
+    cur_addr += PAGE_SIZE;
+
+    console_mfn = (cur_addr >> PAGE_SHIFT);
+    cur_addr += PAGE_SIZE;
+
+    xenstore_mfn = (cur_addr >> PAGE_SHIFT);
+    cur_addr += PAGE_SIZE;
+
+    if (cmdline) {
+        cpu_physical_memory_rw(cur_addr, (void*)cmdline, strlen(cmdline) + 1, 
1);
+        cmdline_pfn = mfn_to_pfn(cur_addr >> PAGE_SHIFT, mfn_guest);
+        cur_addr += PAGE_SIZE;
+    }
+
+    cur_addr = (cur_addr + ((4 * 1024 * 1024) - 1)) & ~((4 * 1024 * 1024) - 1);
+
+    init_pt_pfn = mfn_to_pfn(cur_addr >> PAGE_SHIFT, mfn_guest);
+    cur_addr += PAGE_SIZE * 100;
+
+    xenner_set_config(EMUDEV_CONF_MFN_CONSOLE,     console_mfn);
+    xenner_set_config(EMUDEV_CONF_MFN_XENSTORE,    xenstore_mfn);
+    xenner_set_config(EMUDEV_CONF_PFN_START_INFO,  start_info_pfn);
+    xenner_set_config(EMUDEV_CONF_PFN_INIT_PT,     init_pt_pfn);
+    xenner_set_config(EMUDEV_CONF_PFN_MFN_LIST,    mfn_list_pfn);
+    xenner_set_config(EMUDEV_CONF_PV_VIRT_ENTRY,   dom_params.virt_entry);
+    xenner_set_config(EMUDEV_CONF_PV_VIRT_BASE,    dom_params.virt_base);
+    xenner_set_config(EMUDEV_CONF_HYPERCALL_PAGE,  dom_params.virt_hypercall);
+
+    xenner_set_config(EMUDEV_CONF_DEBUG_LEVEL,      0);
+    xenner_set_config(EMUDEV_CONF_EMU_START_PFN,    mfn_emu);
+    xenner_set_config(EMUDEV_CONF_EMU_PAGE_COUNT,   pg_emu);
+    xenner_set_config(EMUDEV_CONF_M2P_START_PFN,    mfn_m2p);
+    xenner_set_config(EMUDEV_CONF_M2P_PAGE_COUNT,   pg_m2p);
+    xenner_set_config(EMUDEV_CONF_GUEST_START_PFN,  mfn_guest);
+    xenner_set_config(EMUDEV_CONF_TOTAL_PAGE_COUNT, pg_total);
+    xenner_set_config(EMUDEV_CONF_GUEST_PAGE_COUNT, pg_guest);
+    xenner_set_config(EMUDEV_CONF_NR_VCPUS,         smp_cpus);
+
+    xenner_set_config(EMUDEV_CONF_INITRD_LEN,       initrd_len);
+    xenner_set_config(EMUDEV_CONF_PFN_CMDLINE,      cmdline_pfn);
+
+    /* guest setup */
+
+    console_evtchn  = xenner_guest_evtchn_alloc();
+    xenstore_evtchn = xenner_guest_evtchn_alloc();
+
+    xenner_set_config(EMUDEV_CONF_EVTCH_CONSOLE,    console_evtchn);
+    xenner_set_config(EMUDEV_CONF_EVTCH_XENSTORE,   xenstore_evtchn);
+
+    /* default frontent protocol */
+    if (long_mode) {
+        xen_protocol = XEN_IO_PROTO_ABI_X86_32;
+    } else {
+        xen_protocol = XEN_IO_PROTO_ABI_X86_32;
+    }
+
+    xenstore_domain_init1(kernel, initrd, cmdline);
+    xenstore_domain_init2(xenstore_evtchn,
+                          xenstore_mfn,
+                          console_evtchn,
+                          console_mfn);
+
+    xenner_guest_store_setup(xenstore_mfn,
+                             xenstore_evtchn);
+
+    qemu_register_reset(xenner_cpu_reset, NULL);
+    xenner_cpu_reset(NULL);
+
+    return 0;
+}
-- 
1.6.0.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]